From 4b95ff655aa051578d26d755ba1532819b1a9da5 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 16 Jan 2024 12:29:37 -0800 Subject: [PATCH 01/11] Use untyped table API to get table info (#2747) --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/index.rs | 183 ++++++++++++++------------------------------------- 3 files changed, 54 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ce8745b51..a4cad15f13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2514,9 +2514,9 @@ dependencies = [ [[package]] name = "redb" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08837f9a129bde83c51953b8c96cbb3422b940166b730caa954836106eb1dfd2" +checksum = "72623e6275cd430215b741f41ebda34db93a13ebde253f908b70871c46afc5ba" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index ff97614705..81d2cf5c27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ mime_guess = "2.0.4" miniscript = "10.0.0" mp4 = "0.14.0" ord-bitcoincore-rpc = "0.17.1" -redb = "1.4.0" +redb = "1.5.0" regex = "1.6.0" rss = "2.0.1" rust-embed = "8.0.0" diff --git a/src/index.rs b/src/index.rs index f032ea4c44..037da06edf 100644 --- a/src/index.rs +++ b/src/index.rs @@ -20,8 +20,8 @@ use { log::log_enabled, redb::{ Database, DatabaseError, MultimapTable, MultimapTableDefinition, MultimapTableHandle, - ReadOnlyTable, ReadableMultimapTable, ReadableTable, RedbKey, RedbValue, RepairSession, - StorageError, Table, TableDefinition, TableHandle, WriteTransaction, + ReadOnlyTable, ReadableMultimapTable, ReadableTable, RepairSession, StorageError, Table, + TableDefinition, TableHandle, TableStats, WriteTransaction, }, std::{ collections::{BTreeSet, HashMap}, @@ -140,6 +140,21 @@ pub(crate) struct TableInfo { tree_height: u32, } +impl From for TableInfo { + fn from(stats: TableStats) -> Self { + Self { + branch_pages: stats.branch_pages(), + fragmented_bytes: stats.fragmented_bytes(), + leaf_pages: stats.leaf_pages(), + metadata_bytes: stats.metadata_bytes(), + proportion: 0.0, + stored_bytes: stats.stored_bytes(), + total_bytes: stats.stored_bytes() + stats.metadata_bytes() + stats.fragmented_bytes(), + tree_height: stats.tree_height(), + } + } +} + #[derive(Serialize)] pub(crate) struct TransactionInfo { pub(crate) starting_block_count: u32, @@ -483,139 +498,43 @@ impl Index { } pub(crate) fn info(&self) -> Result { - fn insert_table_info( - tables: &mut BTreeMap, - wtx: &WriteTransaction, - database_total_bytes: u64, - definition: TableDefinition, - ) { - let stats = wtx.open_table(definition).unwrap().stats().unwrap(); - - let fragmented_bytes = stats.fragmented_bytes(); - let metadata_bytes = stats.metadata_bytes(); - let stored_bytes = stats.stored_bytes(); - let total_bytes = stored_bytes + metadata_bytes + fragmented_bytes; - - tables.insert( - definition.name().into(), - TableInfo { - branch_pages: stats.branch_pages(), - fragmented_bytes, - leaf_pages: stats.leaf_pages(), - metadata_bytes, - proportion: total_bytes as f64 / database_total_bytes as f64, - stored_bytes, - total_bytes, - tree_height: stats.tree_height(), - }, - ); - } - - fn insert_multimap_table_info( - tables: &mut BTreeMap, - wtx: &WriteTransaction, - database_total_bytes: u64, - definition: MultimapTableDefinition, - ) { - let stats = wtx - .open_multimap_table(definition) - .unwrap() - .stats() - .unwrap(); - - let fragmented_bytes = stats.fragmented_bytes(); - let metadata_bytes = stats.metadata_bytes(); - let stored_bytes = stats.stored_bytes(); - let total_bytes = stored_bytes + metadata_bytes + fragmented_bytes; - - tables.insert( - definition.name().into(), - TableInfo { - branch_pages: stats.branch_pages(), - fragmented_bytes, - leaf_pages: stats.leaf_pages(), - metadata_bytes, - proportion: total_bytes as f64 / database_total_bytes as f64, - stored_bytes, - total_bytes, - tree_height: stats.tree_height(), - }, - ); - } + let stats = self.database.begin_write()?.stats()?; - let wtx = self.begin_write()?; - - let stats = wtx.stats()?; - - let fragmented_bytes = stats.fragmented_bytes(); - let metadata_bytes = stats.metadata_bytes(); - let stored_bytes = stats.stored_bytes(); - let total_bytes = fragmented_bytes + metadata_bytes + stored_bytes; + let rtx = self.database.begin_read()?; let mut tables: BTreeMap = BTreeMap::new(); - insert_multimap_table_info(&mut tables, &wtx, total_bytes, SATPOINT_TO_SEQUENCE_NUMBER); - insert_multimap_table_info(&mut tables, &wtx, total_bytes, SAT_TO_SEQUENCE_NUMBER); - insert_multimap_table_info(&mut tables, &wtx, total_bytes, SEQUENCE_NUMBER_TO_CHILDREN); - insert_table_info(&mut tables, &wtx, total_bytes, HEIGHT_TO_BLOCK_HEADER); - insert_table_info( - &mut tables, - &wtx, - total_bytes, - HEIGHT_TO_LAST_SEQUENCE_NUMBER, - ); - insert_table_info(&mut tables, &wtx, total_bytes, HOME_INSCRIPTIONS); - insert_table_info( - &mut tables, - &wtx, - total_bytes, - INSCRIPTION_ID_TO_SEQUENCE_NUMBER, - ); - insert_table_info( - &mut tables, - &wtx, - total_bytes, - INSCRIPTION_NUMBER_TO_SEQUENCE_NUMBER, - ); - insert_table_info(&mut tables, &wtx, total_bytes, OUTPOINT_TO_RUNE_BALANCES); - insert_table_info(&mut tables, &wtx, total_bytes, OUTPOINT_TO_SAT_RANGES); - insert_table_info(&mut tables, &wtx, total_bytes, OUTPOINT_TO_VALUE); - insert_table_info(&mut tables, &wtx, total_bytes, RUNE_ID_TO_RUNE_ENTRY); - insert_table_info(&mut tables, &wtx, total_bytes, RUNE_TO_RUNE_ID); - insert_table_info(&mut tables, &wtx, total_bytes, SAT_TO_SATPOINT); - insert_table_info( - &mut tables, - &wtx, - total_bytes, - SEQUENCE_NUMBER_TO_INSCRIPTION_ENTRY, - ); - insert_table_info(&mut tables, &wtx, total_bytes, SEQUENCE_NUMBER_TO_RUNE_ID); - insert_table_info(&mut tables, &wtx, total_bytes, SEQUENCE_NUMBER_TO_SATPOINT); - insert_table_info(&mut tables, &wtx, total_bytes, STATISTIC_TO_COUNT); - insert_table_info(&mut tables, &wtx, total_bytes, TRANSACTION_ID_TO_RUNE); - insert_table_info( - &mut tables, - &wtx, - total_bytes, - TRANSACTION_ID_TO_TRANSACTION, - ); - insert_table_info( - &mut tables, - &wtx, - total_bytes, - WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP, - ); + for handle in rtx.list_tables()? { + let name = handle.name().into(); + let stats = rtx.open_untyped_table(handle)?.stats()?; + tables.insert(name, stats.into()); + } + + for handle in rtx.list_multimap_tables()? { + let name = handle.name().into(); + let stats = rtx.open_untyped_multimap_table(handle)?.stats()?; + tables.insert(name, stats.into()); + } - for table in wtx.list_tables()? { + for table in rtx.list_tables()? { assert!(tables.contains_key(table.name())); } - for table in wtx.list_multimap_tables()? { + for table in rtx.list_multimap_tables()? { assert!(tables.contains_key(table.name())); } + let total_bytes = tables + .values() + .map(|table_info| table_info.total_bytes) + .sum(); + + tables.values_mut().for_each(|table_info| { + table_info.proportion = table_info.total_bytes as f64 / total_bytes as f64 + }); + let info = { - let statistic_to_count = wtx.open_table(STATISTIC_TO_COUNT)?; + let statistic_to_count = rtx.open_table(STATISTIC_TO_COUNT)?; let sat_ranges = statistic_to_count .get(&Statistic::SatRanges.key())? .map(|x| x.value()) @@ -625,7 +544,8 @@ impl Index { .map(|x| x.value()) .unwrap_or(0); Info { - blocks_indexed: wtx + index_path: self.path.clone(), + blocks_indexed: rtx .open_table(HEIGHT_TO_BLOCK_HEADER)? .range(0..)? .next_back() @@ -633,18 +553,17 @@ impl Index { .map(|(height, _header)| height.value() + 1) .unwrap_or(0), branch_pages: stats.branch_pages(), - fragmented_bytes, + fragmented_bytes: stats.fragmented_bytes(), index_file_size: fs::metadata(&self.path)?.len(), - index_path: self.path.clone(), leaf_pages: stats.leaf_pages(), - metadata_bytes, + metadata_bytes: stats.metadata_bytes(), + sat_ranges, outputs_traversed, page_size: stats.page_size(), - sat_ranges, - stored_bytes, - tables, + stored_bytes: stats.stored_bytes(), total_bytes, - transactions: wtx + tables, + transactions: rtx .open_table(WRITE_TRANSACTION_STARTING_BLOCK_COUNT_TO_TIMESTAMP)? .range(0..)? .flat_map(|result| { @@ -657,7 +576,7 @@ impl Index { }) .collect(), tree_height: stats.tree_height(), - utxos_indexed: wtx.open_table(OUTPOINT_TO_SAT_RANGES)?.len()?, + utxos_indexed: rtx.open_table(OUTPOINT_TO_SAT_RANGES)?.len()?, } }; From 08b63805143139bb203192670f2d209e40fd572d Mon Sep 17 00:00:00 2001 From: raph Date: Tue, 23 Jan 2024 01:22:16 +0100 Subject: [PATCH 02/11] Make wallet communicate with index via RPC (#2929) --- Cargo.toml | 1 + docs/src/guides/explorer.md | 4 - justfile | 3 - src/index.rs | 113 +- src/index/testing.rs | 4 +- src/lib.rs | 34 +- src/options.rs | 4 +- src/runes/pile.rs | 9 +- src/subcommand.rs | 8 +- src/subcommand/preview.rs | 207 --- src/subcommand/server.rs | 63 +- src/subcommand/wallet.rs | 273 +--- src/subcommand/wallet/balance.rs | 23 +- src/subcommand/wallet/cardinals.rs | 14 +- src/subcommand/wallet/create.rs | 9 +- src/subcommand/wallet/etch.rs | 35 +- src/subcommand/wallet/inscribe.rs | 119 +- src/subcommand/wallet/inscriptions.rs | 13 +- src/subcommand/wallet/outputs.rs | 10 +- src/subcommand/wallet/receive.rs | 5 +- src/subcommand/wallet/restore.rs | 8 +- src/subcommand/wallet/sats.rs | 17 +- src/subcommand/wallet/send.rs | 87 +- src/subcommand/wallet/transactions.rs | 4 +- src/templates.rs | 2 +- src/templates/output.rs | 4 +- src/templates/status.rs | 2 + src/wallet.rs | 478 +++++++ src/wallet/inscribe.rs | 45 + src/{subcommand => }/wallet/inscribe/batch.rs | 205 +-- src/wallet/inscribe/batch_entry.rs | 24 + src/wallet/inscribe/batch_file.rs | 101 ++ src/wallet/inscribe/mode.rs | 12 + .../wallet/transaction_builder.rs | 0 test-bitcoincore-rpc/src/lib.rs | 2 +- tests/balances.rs | 20 +- tests/command_builder.rs | 44 +- tests/core.rs | 90 -- tests/decode.rs | 13 +- tests/etch.rs | 281 +++-- tests/find.rs | 10 +- tests/index.rs | 26 +- tests/info.rs | 10 +- tests/json_api.rs | 231 ++-- tests/lib.rs | 100 +- tests/list.rs | 6 +- tests/runes.rs | 42 +- tests/server.rs | 212 ++-- tests/test_server.rs | 79 +- tests/wallet/balance.rs | 96 +- tests/wallet/cardinals.rs | 16 +- tests/wallet/create.rs | 14 +- tests/wallet/inscribe.rs | 1124 ++++++++++------- tests/wallet/inscriptions.rs | 73 +- tests/wallet/outputs.rs | 43 +- tests/wallet/receive.rs | 13 +- tests/wallet/restore.rs | 8 +- tests/wallet/sats.rs | 74 +- tests/wallet/send.rs | 590 ++++++--- tests/wallet/transactions.rs | 38 +- 60 files changed, 2918 insertions(+), 2277 deletions(-) delete mode 100644 src/subcommand/preview.rs create mode 100644 src/wallet.rs create mode 100644 src/wallet/inscribe.rs rename src/{subcommand => }/wallet/inscribe/batch.rs (73%) create mode 100644 src/wallet/inscribe/batch_entry.rs create mode 100644 src/wallet/inscribe/batch_file.rs create mode 100644 src/wallet/inscribe/mode.rs rename src/{subcommand => }/wallet/transaction_builder.rs (100%) delete mode 100644 tests/core.rs diff --git a/Cargo.toml b/Cargo.toml index 81d2cf5c27..523bd41fcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ mp4 = "0.14.0" ord-bitcoincore-rpc = "0.17.1" redb = "1.5.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/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.rs b/src/index.rs index 037da06edf..2991ddaf20 100644 --- a/src/index.rs +++ b/src/index.rs @@ -24,7 +24,7 @@ use { TableDefinition, TableHandle, TableStats, WriteTransaction, }, std::{ - collections::{BTreeSet, HashMap}, + collections::HashMap, io::{BufWriter, Write}, sync::{Mutex, Once}, }, @@ -174,7 +174,7 @@ pub(crate) struct InscriptionInfo { pub(crate) charms: u16, } -trait BitcoinCoreRpcResultExt { +pub(crate) trait BitcoinCoreRpcResultExt { fn into_option(self) -> Result>; } @@ -417,20 +417,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 contains_output(&self, output: &OutPoint) -> Result { Ok( self @@ -858,32 +844,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, @@ -925,22 +885,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()?; @@ -1575,28 +1519,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: u32, @@ -2036,11 +1958,7 @@ impl Index { #[cfg(test)] mod tests { - use { - super::*, - crate::index::testing::Context, - bitcoin::secp256k1::rand::{self, RngCore}, - }; + use {super::*, crate::index::testing::Context}; #[test] fn height_limit() { @@ -3339,31 +3257,6 @@ mod tests { } } - #[test] - fn unsynced_index_fails() { - for context in Context::configurations() { - let mut entropy = [0; 16]; - rand::thread_rng().fill_bytes(&mut entropy); - let mnemonic = Mnemonic::from_entropy(&entropy).unwrap(); - crate::subcommand::wallet::initialize("ord".into(), &context.options, mnemonic.to_seed("")) - .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 - ) - .unwrap_err() - .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/index/testing.rs b/src/index/testing.rs index 199dfe7974..bb705861a5 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, @@ -36,7 +36,6 @@ impl ContextBuilder { index.update().unwrap(); Ok(Context { - 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, diff --git a/src/lib.rs b/src/lib.rs index 4bdb117364..2ee4bc0706 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ #![allow( + clippy::large_enum_variant, + clippy::result_large_err, clippy::too_many_arguments, - clippy::type_complexity, - clippy::result_large_err + clippy::type_complexity )] #![deny( clippy::cast_lossless, @@ -59,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}, @@ -77,7 +77,7 @@ use { time::{Duration, Instant, SystemTime}, }, sysinfo::System, - tempfile::TempDir, + templates::{InscriptionJson, OutputJson, RuneJson, StatusJson}, tokio::{runtime::Runtime, task}, }; @@ -92,7 +92,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)] @@ -112,7 +112,7 @@ macro_rules! tprintln { }; } -mod arguments; +pub mod arguments; mod blocktime; pub mod chain; mod config; @@ -123,7 +123,7 @@ mod deserialize_from_str; mod epoch; mod fee_rate; mod height; -mod index; +pub mod index; mod inscriptions; mod object; mod options; @@ -137,6 +137,7 @@ mod server_config; pub mod subcommand; mod tally; pub mod templates; +pub mod wallet; type Result = std::result::Result; @@ -200,6 +201,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 @@ -226,6 +237,8 @@ pub fn main() { .unwrap() .iter() .for_each(|handle| handle.graceful_shutdown(Some(Duration::from_millis(100)))); + + gracefully_shutdown_indexer(); }) .expect("Error setting handler"); @@ -251,8 +264,7 @@ pub fn main() { if let Some(output) = output { output.print_json(); } + gracefully_shutdown_indexer(); } } - - gracefully_shutdown_indexer(); } diff --git a/src/options.rs b/src/options.rs index 4d91b3181d..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() { @@ -569,7 +569,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/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.rs b/src/subcommand.rs index bc158a1b4e..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")] @@ -47,7 +44,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 { @@ -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)?); @@ -77,7 +73,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 deleted file mode 100644 index fe1bc73812..0000000000 --- a/src/subcommand/preview.rs +++ /dev/null @@ -1,207 +0,0 @@ -use {super::*, fee_rate::FeeRate, std::sync::atomic}; - -#[derive(Debug, Parser)] -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 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={rpc_port}")) - .spawn() - .context("failed to spawn `bitcoind`")?, - ); - - 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() - }; - - 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)); - } - - super::wallet::create::Create { - passphrase: "".into(), - } - .run("ord".into(), options.clone())?; - - let rpc_client = options.bitcoin_rpc_client(None)?; - - let address = 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)?; - - if let Some(files) = self.files { - for file in files { - Arguments { - options: options.clone(), - subcommand: Subcommand::Wallet(super::wallet::Wallet { - name: "ord".into(), - 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()?; - - rpc_client.generate_to_address(1, &address)?; - } - } - - if let Some(batches) = self.batches { - for batch in batches { - Arguments { - options: options.clone(), - subcommand: Subcommand::Wallet(super::wallet::Wallet { - name: "ord".into(), - 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()?; - - rpc_client.generate_to_address(1, &address)?; - } - } - - 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/server.rs b/src/subcommand/server.rs index fc07835aa3..4655416e05 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -124,44 +124,44 @@ impl Display for StaticHtml { } } -#[derive(Debug, Parser)] -pub(crate) struct Server { +#[derive(Debug, Parser, Clone)] +pub struct Server { #[arg( 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( @@ -170,11 +170,11 @@ 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 { - 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(); @@ -182,13 +182,20 @@ 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}"); } } - thread::sleep(Duration::from_millis(5000)); + + if integration_test() { + thread::sleep(Duration::from_millis(100)); + } else { + thread::sleep(Duration::from_millis(5000)); + } }); + INDEXER.lock().unwrap().replace(index_thread); let config = Arc::new(options.load_config()?); @@ -589,10 +596,7 @@ impl Server { outpoint, output, indexed, - runes - .into_iter() - .map(|(spaced_rune, pile)| (spaced_rune.rune, pile.amount)) - .collect(), + runes, sat_ranges, spent, )) @@ -1616,6 +1620,7 @@ mod tests { reqwest::Url, serde::de::DeserializeOwned, std::net::TcpListener, + tempfile::TempDir, }; const RUNE: u128 = 99246114928149462; @@ -1742,7 +1747,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() @@ -1755,12 +1760,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 { @@ -2618,10 +2623,18 @@ mod tests { sat_ranges: None, indexed: true, inscriptions: Vec::new(), + runes: vec![( + SpacedRune { + rune: Rune(RUNE), + spacers: 0 + }, + Pile { + amount: 340282366920938463463374607431768211455, + divisibility: 1, + symbol: None, + } + )], spent: false, - runes: vec![(Rune(RUNE), 340282366920938463463374607431768211455)] - .into_iter() - .collect(), } ); } diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index a71271575e..9aac669fc3 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -1,17 +1,10 @@ use { super::*, - bitcoin::secp256k1::{ - rand::{self, RngCore}, - All, Secp256k1, + crate::wallet::{ + inscribe::{Batch, Batchfile, Mode}, + Wallet, }, - bitcoin::{ - bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint}, - Network, - }, - bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, - fee_rate::FeeRate, - miniscript::descriptor::{Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard}, - transaction_builder::TransactionBuilder, + reqwest::Url, }; pub mod balance; @@ -22,16 +15,23 @@ 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; 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, + #[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, } @@ -65,233 +65,28 @@ pub(crate) enum Subcommand { Cardinals, } -impl Wallet { +impl WalletCommand { pub(crate) fn run(self, options: Options) -> SubcommandResult { + let wallet = Wallet { + name: self.name.clone(), + no_sync: self.no_sync, + options, + ord_url: self.server_url, + }; + match self.subcommand { - Subcommand::Balance => balance::run(self.name, options), - Subcommand::Create(create) => create.run(self.name, options), - Subcommand::Etch(etch) => etch.run(self.name, options), - Subcommand::Inscribe(inscribe) => inscribe.run(self.name, options), - Subcommand::Inscriptions => inscriptions::run(self.name, options), - Subcommand::Receive => receive::run(self.name, options), - Subcommand::Restore(restore) => restore.run(self.name, 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::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), } } } - -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, - ), - ); - } - - index.check_sync(&utxos)?; - - Ok(utxos) -} - -pub(crate) fn get_unspent_output_ranges( - client: &Client, - index: &Index, -) -> Result)>> { - get_unspent_outputs(client, index)? - .iter() - .map(|(outpoint, value)| match index.list(*outpoint)? { - Some(sat_ranges) => { - assert_eq!( - sat_ranges - .iter() - .map(|(start, end)| end - start) - .sum::(), - value.to_sat() - ); - Ok((*outpoint, sat_ranges)) - } - None => bail!("index does not have sat index"), - }) - .collect() -} - -pub(crate) fn get_locked_outputs(client: &Client) -> Result> { - #[derive(Deserialize)] - pub(crate) struct JsonOutPoint { - txid: bitcoin::Txid, - vout: u32, - } - - Ok( - client - .call::>("listlockunspent", &[])? - .into_iter() - .map(|outpoint| OutPoint::new(outpoint.txid, outpoint.vout)) - .collect(), - ) -} - -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())?, - ) -} - -pub(crate) fn initialize(wallet: String, options: &Options, seed: [u8; 64]) -> Result { - check_version(options.bitcoin_rpc_client(None)?)?.create_wallet( - &wallet, - None, - Some(true), - None, - None, - )?; - - let client = options.bitcoin_rpc_client(Some(wallet))?; - - let network = options.chain().network(); - - let secp = Secp256k1::new(); - - let master_private_key = ExtendedPrivKey::new_master(network, &seed)?; - - let fingerprint = master_private_key.fingerprint(&secp); - - 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] { - derive_and_import_descriptor( - &client, - &secp, - (fingerprint, derivation_path.clone()), - derived_private_key, - change, - )?; - } - - Ok(()) -} - -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(()) -} - -pub(crate) fn bitcoin_rpc_client_for_wallet_command( - 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; - - let bitcoin_version = client.version()?; - if bitcoin_version < MIN_VERSION { - bail!( - "Bitcoin Core {} or newer required, current version is {}", - format_bitcoin_core_version(MIN_VERSION), - format_bitcoin_core_version(bitcoin_version), - ); - } else { - Ok(client) - } -} - -fn format_bitcoin_core_version(version: usize) -> String { - format!( - "{}.{}.{}", - version / 10000, - version % 10000 / 100, - version % 100 - ) -} diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index ae88dc2aba..9488644793 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -11,16 +11,11 @@ pub struct Output { pub total: u64, } -pub(crate) fn run(wallet: String, 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 client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; - - let unspent_outputs = get_unspent_outputs(&client, &index)?; - - let inscription_outputs = index - .get_inscriptions(&unspent_outputs)? + let inscription_outputs = wallet + .get_inscriptions()? .keys() .map(|satpoint| satpoint.outpoint) .collect::>(); @@ -30,10 +25,10 @@ pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { let mut runes = BTreeMap::new(); let mut runic = 0; - for (outpoint, amount) in unspent_outputs { - let rune_balances = index.get_rune_balances_for_outpoint(outpoint)?; + for (output, amount) in unspent_outputs { + let rune_balances = wallet.get_runes_balances_for_output(&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 { @@ -48,8 +43,8 @@ pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { Ok(Some(Box::new(Output { cardinal, ordinal, - runes: index.has_rune_index().then_some(runes), - runic: index.has_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/cardinals.rs b/src/subcommand/wallet/cardinals.rs index f809571e34..a7389a4b54 100644 --- a/src/subcommand/wallet/cardinals.rs +++ b/src/subcommand/wallet/cardinals.rs @@ -6,17 +6,11 @@ pub struct CardinalUtxo { pub amount: u64, } -pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + let unspent_outputs = wallet.get_unspent_outputs()?; - index.update()?; - - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; - - let unspent_outputs = get_unspent_outputs(&client, &index)?; - - 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/create.rs b/src/subcommand/wallet/create.rs index 69121d2746..4a99c4d991 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -1,4 +1,7 @@ -use super::*; +use { + super::*, + bitcoin::secp256k1::rand::{self, RngCore}, +}; #[derive(Serialize, Deserialize)] pub struct Output { @@ -17,13 +20,13 @@ pub(crate) struct Create { } impl Create { - pub(crate) fn run(self, wallet: String, 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(wallet, &options, mnemonic.to_seed(self.passphrase.clone()))?; + wallet.initialize(mnemonic.to_seed(&self.passphrase))?; Ok(Some(Box::new(Output { mnemonic, diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index 2f863f7182..8e5d38d5e1 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -21,30 +21,26 @@ pub struct Output { } impl Etch { - pub(crate) fn run(self, wallet: String, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; - + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { ensure!( - index.has_rune_index(), + wallet.has_rune_index()?, "`ord wallet etch` requires index created with `--index-runes` flag", ); - index.update()?; - - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; - let SpacedRune { rune, spacers } = self.rune; - let count = client.get_block_count()?; + let bitcoin_client = wallet.bitcoin_client()?; + + let count = bitcoin_client.get_block_count()?; ensure!( - index.rune(rune)?.is_none(), + wallet.get_rune(rune)?.is_none(), "rune `{}` has already been etched", rune, ); 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, @@ -59,7 +55,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()?; let runestone = Runestone { etching: Some(Etching { @@ -104,25 +100,24 @@ impl Etch { ], }; - let unspent_outputs = get_unspent_outputs(&client, &index)?; - - let inscriptions = index - .get_inscriptions(&unspent_outputs)? + let inscriptions = wallet + .get_inscriptions()? .keys() .map(|satpoint| satpoint.outpoint) .collect::>(); - if !client.lock_unspent(&inscriptions)? { + if !bitcoin_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(&bitcoin_client, self.fee_rate, &unfunded_transaction)?; - let signed_transaction = client + let signed_transaction = bitcoin_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let transaction = client.send_raw_transaction(&signed_transaction)?; + let transaction = bitcoin_client.send_raw_transaction(&signed_transaction)?; Ok(Some(Box::new(Output { rune: self.rune, diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 7c036b5bf7..e22a0a682c 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -1,45 +1,4 @@ -use { - self::batch::{Batch, Batchfile, Mode}, - super::*, - crate::subcommand::wallet::transaction_builder::Target, - bitcoin::{ - blockdata::{opcodes, script}, - key::PrivateKey, - key::{TapTweak, TweakedKeyPair, TweakedPublicKey, UntweakedKeyPair}, - policy::MAX_STANDARD_TX_WEIGHT, - secp256k1::{self, constants::SCHNORR_SIGNATURE_SIZE, rand, Secp256k1, XOnlyPublicKey}, - sighash::{Prevouts, SighashCache, TapSighashType}, - taproot::Signature, - taproot::{ControlBlock, LeafVersion, TapLeafHash, TaprootBuilder}, - }, - bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, SignRawTransactionInput, Timestamp}, - bitcoincore_rpc::Client, -}; - -mod batch; - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct InscriptionInfo { - pub id: InscriptionId, - pub location: SatPoint, -} - -#[derive(Serialize, Deserialize)] -pub struct Output { - pub commit: Txid, - pub inscriptions: Vec, - pub parent: Option, - pub reveal: Txid, - pub total_fees: u64, -} - -#[derive(Clone, Debug)] -pub(crate) struct ParentInfo { - destination: Address, - id: InscriptionId, - location: SatPoint, - tx_out: TxOut, -} +use super::*; #[derive(Debug, Parser)] #[clap( @@ -112,21 +71,16 @@ pub(crate) struct Inscribe { } impl Inscribe { - pub(crate) fn run(self, wallet: String, 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 client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let locked_utxos = wallet.get_locked_outputs()?; - let utxos = get_unspent_outputs(&client, &index)?; + let runic_utxos = wallet.get_runic_outputs()?; - let locked_utxos = get_locked_outputs(&client)?; - - let runic_utxos = index.get_runic_outputs(&utxos.keys().cloned().collect::>())?; - - let chain = options.chain(); + let chain = wallet.chain(); let postage; let destinations; @@ -137,13 +91,13 @@ impl Inscribe { match (self.file, self.batch) { (Some(file), None) => { - parent_info = Inscribe::get_parent_info(self.parent, &index, &utxos, &client, chain)?; + parent_info = wallet.get_parent_info(self.parent, &utxos)?; postage = self.postage.unwrap_or(TARGET_POSTAGE); if let Some(delegate) = self.delegate { ensure! { - index.inscription_exists(delegate)?, + wallet.inscription_exists(delegate)?, "delegate {delegate} does not exist" } } @@ -165,13 +119,13 @@ 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()?, }]; } (None, Some(batch)) => { let batchfile = Batchfile::load(&batch)?; - parent_info = Inscribe::get_parent_info(batchfile.parent, &index, &utxos, &client, chain)?; + parent_info = wallet.get_parent_info(batchfile.parent, &utxos)?; postage = batchfile .postage @@ -179,9 +133,7 @@ impl Inscribe { .unwrap_or(TARGET_POSTAGE); (inscriptions, destinations) = batchfile.inscriptions( - &index, - &client, - chain, + &wallet, parent_info.as_ref().map(|info| info.tx_out.value), metadata, postage, @@ -200,15 +152,7 @@ impl Inscribe { } let satpoint = if let Some(sat) = sat { - if !index.has_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 }; @@ -227,7 +171,7 @@ impl Inscribe { reveal_fee_rate: self.fee_rate, satpoint, } - .inscribe(chain, &index, &client, &locked_utxos, runic_utxos, &utxos) + .inscribe(&locked_utxos, runic_utxos, &utxos, &wallet) } fn parse_metadata(cbor: Option, json: Option) -> Result>> { @@ -248,47 +192,16 @@ impl Inscribe { Ok(None) } } - - fn get_parent_info( - parent: Option, - index: &Index, - utxos: &BTreeMap, - client: &Client, - chain: Chain, - ) -> 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"))); - } - - Ok(Some(ParentInfo { - destination: get_change_address(client, chain)?, - 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"))) - } - } else { - Ok(None) - } - } } #[cfg(test)] mod tests { use { - self::batch::BatchEntry, super::*, + crate::wallet::inscribe::{BatchEntry, ParentInfo}, + bitcoin::policy::MAX_STANDARD_TX_WEIGHT, serde_yaml::{Mapping, Value}, + tempfile::TempDir, }; #[test] diff --git a/src/subcommand/wallet/inscriptions.rs b/src/subcommand/wallet/inscriptions.rs index 0d58297fff..4113d27067 100644 --- a/src/subcommand/wallet/inscriptions.rs +++ b/src/subcommand/wallet/inscriptions.rs @@ -8,17 +8,12 @@ pub struct Output { pub postage: u64, } -pub(crate) fn run(wallet: String, 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 client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let inscriptions = wallet.get_inscriptions()?; - let unspent_outputs = get_unspent_outputs(&client, &index)?; - - let inscriptions = index.get_inscriptions(&unspent_outputs)?; - - 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/outputs.rs b/src/subcommand/wallet/outputs.rs index b37faadbea..121a6e7de6 100644 --- a/src/subcommand/wallet/outputs.rs +++ b/src/subcommand/wallet/outputs.rs @@ -6,15 +6,9 @@ pub struct Output { pub amount: u64, } -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)?; - +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { 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 fe7d40ec2a..669540f6db 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_command(wallet, &options)? +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 index 54c2d64d4e..9513594f9b 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) -> 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 index 65a0c2491d..e279a45b9e 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -24,18 +24,13 @@ pub struct OutputRare { } impl Sats { - pub(crate) fn run(&self, wallet: String, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; - - if !index.has_sat_index() { - bail!("sats requires index created with `--index-sats` flag"); - } - - index.update()?; - - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + pub(crate) fn run(&self, wallet: Wallet) -> SubcommandResult { + ensure!( + wallet.has_sat_index()?, + "sats requires index created with `--index-sats` flag" + ); - let utxos = get_unspent_output_ranges(&client, &index)?; + let utxos = wallet.get_output_sat_ranges()?; 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 39eed8712f..ed25292b93 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -1,4 +1,4 @@ -use {super::*, crate::subcommand::wallet::transaction_builder::Target}; +use {super::*, crate::wallet::transaction_builder::Target}; #[derive(Debug, Parser)] pub(crate) struct Send { @@ -19,50 +19,45 @@ pub struct Output { } impl Send { - pub(crate) fn run(self, wallet: String, 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 index = Index::open(&options)?; + let unspent_outputs = wallet.get_unspent_outputs()?; - index.update()?; + let locked_outputs = wallet.get_locked_outputs()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let inscriptions = wallet.get_inscriptions()?; - let chain = options.chain(); + let runic_outputs = wallet.get_runic_outputs()?; - let unspent_outputs = get_unspent_outputs(&client, &index)?; - - let locked_outputs = get_locked_outputs(&client)?; - - let inscriptions = index.get_inscriptions(&unspent_outputs)?; - - let runic_outputs = - index.get_runic_outputs(&unspent_outputs.keys().cloned().collect::>())?; + let bitcoin_client = wallet.bitcoin_client()?; 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( + &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 }))); } - 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, - chain, - &client, + &bitcoin_client, decimal, self.fee_rate, - &index, inscriptions, rune, runic_outputs, unspent_outputs, + &wallet, )?; return Ok(Some(Box::new(Output { transaction }))); } @@ -82,10 +77,7 @@ impl Send { } }; - let change = [ - get_change_address(&client, chain)?, - get_change_address(&client, chain)?, - ]; + let change = [wallet.get_change_address()?, wallet.get_change_address()?]; let postage = if let Some(postage) = self.postage { Target::ExactPostage(postage) @@ -106,17 +98,17 @@ impl Send { ) .build_transaction()?; - let signed_tx = client + let signed_tx = bitcoin_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let txid = 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( - client: &Client, + bitcoin_client: &Client, inscriptions: &BTreeMap, runic_outputs: &BTreeSet, unspent_outputs: BTreeMap, @@ -133,7 +125,7 @@ impl Send { .cloned() .collect::>(); - if !client.lock_unspent(&locked_outputs)? { + if !bitcoin_client.lock_unspent(&locked_outputs)? { bail!("failed to lock UTXOs"); } @@ -141,12 +133,12 @@ impl Send { } fn send_amount( - client: &Client, + wallet: &Wallet, amount: Amount, address: Address, fee_rate: FeeRate, ) -> Result { - Ok(client.call( + Ok(wallet.bitcoin_client()?.call( "sendtoaddress", &[ address.to_string().into(), // 1. address @@ -165,25 +157,29 @@ impl Send { fn send_runes( address: Address, - chain: Chain, - client: &Client, + bitcoin_client: &Client, decimal: Decimal, fee_rate: FeeRate, - index: &Index, inscriptions: BTreeMap, spaced_rune: SpacedRune, runic_outputs: BTreeSet, unspent_outputs: BTreeMap, + wallet: &Wallet, ) -> Result { ensure!( - index.has_rune_index(), + wallet.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( + bitcoin_client, + &inscriptions, + &runic_outputs, + unspent_outputs, + )?; - let (id, entry, _parent) = index - .rune(spaced_rune.rune)? + 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)?; @@ -201,7 +197,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; @@ -251,7 +247,7 @@ impl Send { value: 0, }, TxOut { - script_pubkey: get_change_address(client, chain)?.script_pubkey(), + script_pubkey: wallet.get_change_address()?.script_pubkey(), value: TARGET_POSTAGE.to_sat(), }, TxOut { @@ -261,12 +257,13 @@ impl Send { ], }; - let unsigned_transaction = fund_raw_transaction(client, fee_rate, &unfunded_transaction)?; + let unsigned_transaction = + fund_raw_transaction(bitcoin_client, fee_rate, &unfunded_transaction)?; - let signed_transaction = client + let signed_transaction = bitcoin_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - Ok(client.send_raw_transaction(&signed_transaction)?) + Ok(bitcoin_client.send_raw_transaction(&signed_transaction)?) } } diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index d212f2200e..f0ad7d9057 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_command(wallet, &options)?; + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + let client = wallet.bitcoin_client()?; let mut output = Vec::new(); for tx in client.list_transactions( diff --git a/src/templates.rs b/src/templates.rs index f8dbf1e7ba..a2008d26f8 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -24,7 +24,7 @@ pub(crate) use { runes::{RunesHtml, RunesJson}, sat::{SatHtml, SatInscriptionJson, SatInscriptionsJson, SatJson}, server_config::ServerConfig, - status::StatusHtml, + status::{StatusHtml, StatusJson}, transaction::{TransactionHtml, TransactionJson}, }; diff --git a/src/templates/output.rs b/src/templates/output.rs index ed18801d3f..58fa66cfac 100644 --- a/src/templates/output.rs +++ b/src/templates/output.rs @@ -16,7 +16,7 @@ pub struct OutputJson { pub address: Option, pub indexed: bool, pub inscriptions: Vec, - pub runes: BTreeMap, + pub runes: Vec<(SpacedRune, Pile)>, pub sat_ranges: Option>, pub script_pubkey: String, pub spent: bool, @@ -31,7 +31,7 @@ impl OutputJson { outpoint: OutPoint, output: TxOut, indexed: bool, - runes: BTreeMap, + runes: Vec<(SpacedRune, Pile)>, sat_ranges: Option>, spent: bool, ) -> Self { 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/src/wallet.rs b/src/wallet.rs new file mode 100644 index 0000000000..df0308c46f --- /dev/null +++ b/src/wallet.rs @@ -0,0 +1,478 @@ +use { + super::*, + bitcoin::secp256k1::{All, Secp256k1}, + bitcoin::{ + bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint}, + Network, + }, + 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 inscribe; +pub mod transaction_builder; + +pub(crate) struct Wallet { + pub(crate) name: String, + pub(crate) no_sync: bool, + pub(crate) options: Options, + pub(crate) ord_url: Url, +} + +impl Wallet { + 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) { + client.load_wallet(&self.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.name); + } + + Ok(client) + } + + pub(crate) fn ord_client(&self) -> Result { + let mut headers = header::HeaderMap::new(); + headers.insert( + header::ACCEPT, + header::HeaderValue::from_static("application/json"), + ); + + let client = reqwest::blocking::ClientBuilder::new() + .default_headers(headers) + .build() + .map_err(|err| anyhow!(err))?; + + let chain_block_count = self.bitcoin_client()?.get_block_count().unwrap() + 1; + + 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); + + if response.text()?.parse::().unwrap() >= chain_block_count { + break; + } else if i == 20 { + bail!("wallet failed to synchronize to index"); + } + + thread::sleep(Duration::from_millis(50)); + } + } + + Ok(client) + } + + 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()?)?; + + if !output_json.indexed { + 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()? + .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 = self.get_locked_outputs()?; + + for outpoint in locked_utxos { + utxos.insert( + outpoint, + Amount::from_sat( + self + .bitcoin_client()? + .get_raw_transaction(&outpoint.txid, None)? + .output[TryInto::::try_into(outpoint.vout).unwrap()] + .value, + ), + ); + } + + for output in utxos.keys() { + self.get_output(output)?; + } + + Ok(utxos) + } + + pub(crate) fn get_output_sat_ranges(&self) -> Result)>> { + ensure!( + self.has_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() { + 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(output_sat_ranges) + } + + pub(crate) fn find_sat_in_outputs( + &self, + sat: Sat, + utxos: &BTreeMap, + ) -> Result { + ensure!( + self.has_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 { + 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 inscription_exists(&self, inscription_id: InscriptionId) -> Result { + Ok( + !self + .ord_client()? + .get( + self + .ord_url + .join(&format!("/inscription/{inscription_id}")) + .unwrap(), + ) + .send()? + .status() + .is_client_error(), + ) + } + + fn get_inscription(&self, inscription_id: InscriptionId) -> Result { + let response = self + .ord_client()? + .get( + self + .ord_url + .join(&format!("/inscription/{inscription_id}")) + .unwrap(), + ) + .send()?; + + 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) -> Result> { + let mut inscriptions = BTreeMap::new(); + for output in self.get_unspent_outputs()?.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_rune( + &self, + rune: Rune, + ) -> Result)>> { + let response = self + .ord_client()? + .get( + self + .ord_url + .join(&format!("/rune/{}", SpacedRune { rune, spacers: 0 })) + .unwrap(), + ) + .send()?; + + if response.status().is_client_error() { + return Ok(None); + } + + 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> { + let mut runic_outputs = BTreeSet::new(); + for output in self.get_unspent_outputs()?.keys() { + if !self.get_output(output)?.runes.is_empty() { + runic_outputs.insert(*output); + } + } + + Ok(runic_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_locked_outputs(&self) -> Result> { + #[derive(Deserialize)] + pub(crate) struct JsonOutPoint { + txid: bitcoin::Txid, + vout: u32, + } + + Ok( + self + .bitcoin_client()? + .call::>("listlockunspent", &[])? + .into_iter() + .map(|outpoint| OutPoint::new(outpoint.txid, outpoint.vout)) + .collect(), + ) + } + + 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 + .bitcoin_client()? + .call::>("getrawchangeaddress", &["bech32m".into()]) + .context("could not get change addresses from wallet")? + .require_network(self.chain().network())?, + ) + } + + pub(crate) fn get_server_status(&self) -> Result { + Ok(serde_json::from_str( + &self + .ord_client()? + .get(self.ord_url.join("/status").unwrap()) + .send()? + .text()?, + )?) + } + + pub(crate) fn has_rune_index(&self) -> Result { + Ok(self.get_server_status()?.rune_index) + } + + pub(crate) fn has_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, + None, + Some(true), + None, + None, + )?; + + let network = self.chain().network(); + + let secp = Secp256k1::new(); + + let master_private_key = ExtendedPrivKey::new_master(network, &seed)?; + + let fingerprint = master_private_key.fingerprint(&secp); + + 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, + )?; + } + + 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); + + let desc = Descriptor::new_tr(public_key, None)?; + + self + .options + .bitcoin_rpc_client(Some(self.name.clone()))? + .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 check_version(client: Client) -> Result { + const MIN_VERSION: usize = 240000; + + let bitcoin_version = client.version()?; + if bitcoin_version < MIN_VERSION { + bail!( + "Bitcoin Core {} or newer required, current version is {}", + format_bitcoin_core_version(MIN_VERSION), + format_bitcoin_core_version(bitcoin_version), + ); + } else { + Ok(client) + } +} + +fn format_bitcoin_core_version(version: usize) -> String { + format!( + "{}.{}.{}", + version / 10000, + version % 10000 / 100, + version % 100 + ) +} diff --git a/src/wallet/inscribe.rs b/src/wallet/inscribe.rs new file mode 100644 index 0000000000..87783ae256 --- /dev/null +++ b/src/wallet/inscribe.rs @@ -0,0 +1,45 @@ +use { + super::*, + bitcoin::{ + blockdata::{opcodes, script}, + key::PrivateKey, + key::{TapTweak, TweakedKeyPair, TweakedPublicKey, UntweakedKeyPair}, + policy::MAX_STANDARD_TX_WEIGHT, + secp256k1::{self, constants::SCHNORR_SIGNATURE_SIZE, rand, Secp256k1, XOnlyPublicKey}, + sighash::{Prevouts, SighashCache, TapSighashType}, + taproot::Signature, + taproot::{ControlBlock, LeafVersion, TapLeafHash, TaprootBuilder}, + }, + bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, SignRawTransactionInput, Timestamp}, + wallet::transaction_builder::Target, +}; + +pub use {batch::Batch, batch_entry::BatchEntry, batch_file::Batchfile, mode::Mode}; + +pub mod batch; +pub mod batch_entry; +pub mod batch_file; +pub mod mode; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct InscriptionInfo { + pub id: InscriptionId, + pub location: SatPoint, +} + +#[derive(Serialize, Deserialize)] +pub struct Output { + pub commit: Txid, + pub inscriptions: Vec, + pub parent: Option, + pub reveal: Txid, + pub total_fees: u64, +} + +#[derive(Clone, Debug)] +pub struct ParentInfo { + pub destination: Address, + pub id: InscriptionId, + pub location: SatPoint, + pub tx_out: TxOut, +} diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/wallet/inscribe/batch.rs similarity index 73% rename from src/subcommand/wallet/inscribe/batch.rs rename to src/wallet/inscribe/batch.rs index f22092d5ba..6a1238b4fe 100644 --- a/src/subcommand/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 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 { @@ -37,24 +37,19 @@ impl Default for Batch { impl Batch { pub(crate) fn inscribe( &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 wallet_inscriptions = wallet.get_inscriptions()?; - let commit_tx_change = [ - get_change_address(client, chain)?, - get_change_address(client, 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( wallet_inscriptions, - chain, + wallet.chain(), locked_utxos.clone(), runic_utxos, utxos.clone(), @@ -70,12 +65,14 @@ impl Batch { )))); } - let signed_commit_tx = 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() { - client + bitcoin_client .sign_raw_transaction_with_wallet( &reveal_tx, Some( @@ -100,12 +97,12 @@ impl Batch { }; if !self.no_backup { - Self::backup_recovery_key(client, recovery_key_pair, chain.network())?; + Self::backup_recovery_key(wallet, recovery_key_pair)?; } - let commit = client.send_raw_transaction(&signed_commit_tx)?; + let commit = bitcoin_client.send_raw_transaction(&signed_commit_tx)?; - let reveal = match 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!( @@ -444,16 +441,18 @@ impl Batch { Ok((unsigned_commit_tx, reveal_tx, recovery_key_pair, total_fees)) } - fn backup_recovery_key( - client: &Client, - 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 = client.get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; + let bitcoin_client = wallet.bitcoin_client()?; - let response = client.import_descriptors(ImportDescriptors { + 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), @@ -528,139 +527,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) delegate: Option, - 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, - index: &Index, - client: &Client, - chain: Chain, - 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() { - if let Some(delegate) = entry.delegate { - ensure! { - index.inscription_exists(delegate)?, - "delegate {delegate} does not exist" - } - } - - inscriptions.push(Inscription::from_file( - chain, - compress, - entry.delegate, - match &metadata { - Some(metadata) => Some(metadata.clone()), - None => entry.metadata()?, - }, - entry.metaprotocol.clone(), - self.parent, - &entry.file, - if i == 0 { None } else { Some(pointer) }, - )?); - - pointer += postage.to_sat(); - } - - let destinations = match self.mode { - Mode::SharedOutput | Mode::SameSat => vec![get_change_address(client, chain)?], - Mode::SeparateOutputs => self - .inscriptions - .iter() - .map(|entry| { - entry.destination.as_ref().map_or_else( - || get_change_address(client, chain), - |address| { - address - .clone() - .require_network(chain.network()) - .map_err(|e| e.into()) - }, - ) - }) - .collect::, _>>()?, - }; - - Ok((inscriptions, destinations)) - } -} diff --git a/src/wallet/inscribe/batch_entry.rs b/src/wallet/inscribe/batch_entry.rs new file mode 100644 index 0000000000..2cdff54292 --- /dev/null +++ b/src/wallet/inscribe/batch_entry.rs @@ -0,0 +1,24 @@ +use super::*; + +#[derive(Deserialize, Default, PartialEq, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct BatchEntry { + pub(crate) delegate: Option, + 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/batch_file.rs b/src/wallet/inscribe/batch_file.rs new file mode 100644 index 0000000000..1f08dafc5c --- /dev/null +++ b/src/wallet/inscribe/batch_file.rs @@ -0,0 +1,101 @@ +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() { + if let Some(delegate) = entry.delegate { + ensure! { + wallet.inscription_exists(delegate)?, + "delegate {delegate} does not exist" + } + } + + inscriptions.push(Inscription::from_file( + wallet.chain(), + compress, + entry.delegate, + match &metadata { + Some(metadata) => Some(metadata.clone()), + None => entry.metadata()?, + }, + entry.metaprotocol.clone(), + self.parent, + &entry.file, + if i == 0 { None } else { Some(pointer) }, + )?); + + 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, +} 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/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/balances.rs b/tests/balances.rs index 1fba7f19da..4deb478dfc 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(&bitcoin_rpc_server, &ord_rpc_server); - let a = etch(&rpc_server, Rune(RUNE)); - let b = etch(&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") - .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 01fc621fe2..2ef347d448 100644 --- a/tests/command_builder.rs +++ b/tests/command_builder.rs @@ -33,8 +33,9 @@ pub(crate) struct CommandBuilder { expected_exit_code: i32, expected_stderr: Expected, expected_stdout: Expected, - 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, } @@ -46,8 +47,9 @@ impl CommandBuilder { expected_exit_code: 0, expected_stderr: Expected::String(String::new()), expected_stdout: Expected::String(String::new()), - 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()), } @@ -58,10 +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_rpc_server(self, ord_rpc_server: &TestServer) -> Self { + Self { + ord_rpc_server_url: Some(ord_rpc_server.url()), ..self } } @@ -105,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() @@ -119,6 +131,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_rpc_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 +151,7 @@ impl CommandBuilder { .current_dir(&*self.tempdir) .arg("--data-dir") .arg(self.tempdir.path()) - .args(&self.args); + .args(&args); command } diff --git a/tests/core.rs b/tests/core.rs deleted file mode 100644 index 986602edf8..0000000000 --- a/tests/core.rs +++ /dev/null @@ -1,90 +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 builder = CommandBuilder::new(format!( - "preview --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()); - - for attempt in 0.. { - if let Ok(response) = reqwest::blocking::get(format!("http://127.0.0.1:{port}/status")) { - if response.status() == 200 { - break; - } - } - - if attempt == 100 { - panic!("Server did not respond to status check",); - } - - thread::sleep(Duration::from_millis(500)); - } - - assert_regex_match!( - reqwest::blocking::get(format!("http://127.0.0.1:{port}/inscriptions")) - .unwrap() - .text() - .unwrap(), - format!(".*(() - .unwrap(); - - for attempt in 0.. { - if attempt == 20 { - panic!("Bitcoin Core did not mine blocks",); - } - - if reqwest::blocking::get(format!("http://127.0.0.1:{port}/blockheight")) - .unwrap() - .text() - .unwrap() - .parse::() - .unwrap() - > blockheight - { - break; - } - - thread::sleep(Duration::from_millis(250)); - } -} diff --git a/tests/decode.rs b/tests/decode.rs index 48b400d694..04693aedb1 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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (_inscription, reveal) = inscribe(&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 a434dd8f23..73226e3982 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -1,19 +1,28 @@ use { super::*, - ord::{subcommand::wallet::etch::Output, Rune}, + ord::{ + subcommand::wallet::{balance, etch::Output}, + Rune, + }, }; #[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"]); + + create_wallet(&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(); @@ -21,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(&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(); @@ -42,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"], + ); + + create_wallet(&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 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(); @@ -63,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(&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(); @@ -84,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(&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(); @@ -103,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(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, Rune(RUNE)); + etch(&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(); @@ -126,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(&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 { @@ -173,33 +224,41 @@ fn runes_can_be_etched() { ); let output = CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); assert_eq!(output.runes.unwrap()[&Rune(RUNE)], 10000); } #[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"], + ); - rpc_server.mine_blocks(1); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + 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); @@ -210,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(&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); @@ -239,30 +305,39 @@ 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(&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) - .run_and_deserialize_output::(); + .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) - .run_and_deserialize_output::(); + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); @@ -271,37 +346,44 @@ 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(&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) - .run_and_deserialize_output::(); + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); assert_eq!(output.ordinal, 0); @@ -309,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(); @@ -317,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(&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(); @@ -344,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(&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(); @@ -371,42 +470,52 @@ 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(&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) - .run_and_deserialize_output::(); + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); assert_eq!(output.ordinal, 10000); 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..9a9a3f3ab7 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(&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(&bitcoin_rpc_server, &ord_rpc_server); + inscribe(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + let (inscription, _) = inscribe(&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 353f949e2e..15b283dc85 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(&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(&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(&bitcoin_rpc_server, &ord_rpc_server); - inscribe(&rpc_server); + inscribe(&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(&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(&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(&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(&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(&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); @@ -337,7 +360,7 @@ fn get_output() { InscriptionId { txid, index: 2 }, ], indexed: true, - runes: BTreeMap::new(), + runes: Vec::new(), sat_ranges: Some(vec![ (5000000000, 10000000000,), (10000000000, 15000000000,), @@ -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,25 +477,26 @@ 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(&bitcoin_rpc_server, &ord_rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + inscribe(&bitcoin_rpc_server, &ord_rpc_server); + + let response = ord_rpc_server.json_request("/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::>() @@ -481,7 +509,7 @@ fn get_status() { pretty_assert_eq!( status_json, - StatusHtml { + StatusJson { blessed_inscriptions: 1, cursed_inscriptions: 0, chain: Chain::Regtest, @@ -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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(3); + + 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); + + 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 273d83b4bd..3eb9ded79a 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -16,7 +16,7 @@ use { templates::{ block::BlockJson, blocks::BlocksJson, inscription::InscriptionJson, inscriptions::InscriptionsJson, output::OutputJson, rune::RuneJson, runes::RunesJson, - sat::SatJson, status::StatusHtml, transaction::TransactionJson, + sat::SatJson, status::StatusJson, transaction::TransactionJson, }, Edict, InscriptionId, Rune, RuneEntry, RuneId, Runestone, SatPoint, }, @@ -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, @@ -54,35 +54,47 @@ macro_rules! assert_regex_match { const RUNE: u128 = 99246114928149462; -type Inscribe = ord::subcommand::wallet::inscribe::Output; +type Inscribe = ord::wallet::inscribe::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::(); +fn create_wallet(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 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); +fn inscribe( + bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, + ord_rpc_server: &TestServer, +) -> (InscriptionId, Txid) { + bitcoin_rpc_server.mine_blocks(1); - for data in payload { - let mut buf = bitcoin::script::PushBytesBuf::new(); - buf.extend_from_slice(data).unwrap(); - builder = builder.push_slice(buf); - } + 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::(); - let script = builder - .push_opcode(bitcoin::opcodes::all::OP_ENDIF) - .into_script(); + bitcoin_rpc_server.mine_blocks(1); - bitcoin::Witness::from_slice(&[script.into_bytes(), Vec::new()]) + assert_eq!(output.inscriptions.len(), 1); + + (output.inscriptions[0].id, output.reveal) } -fn etch(rpc_server: &test_bitcoincore_rpc::Handle, rune: Rune) -> Etch { - rpc_server.mine_blocks(1); +fn etch( + 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!( @@ -90,37 +102,38 @@ 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 inscribe(rpc_server: &test_bitcoincore_rpc::Handle) -> (InscriptionId, Txid) { - 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", - rpc_server.network() - )) - .write("foo.txt", "FOO") - .rpc_server(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); + } - 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 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; @@ -128,7 +141,6 @@ mod expected; mod test_server; mod balances; -mod core; mod decode; mod epochs; mod etch; 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..2b3c529334 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(&bitcoin_rpc_server, &ord_rpc_server); - let etch = etch(&rpc_server, Rune(RUNE)); + let etch = etch(&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(&bitcoin_rpc_server, &ord_rpc_server); - let a = etch(&rpc_server, Rune(RUNE)); - let b = etch(&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") - .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..2449fde82b 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(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe(&rpc_server); + let (inscription, reveal) = inscribe(&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(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + let (_, reveal) = inscribe(&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(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe(&rpc_server); + let (inscription, _) = inscribe(&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(&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(&bitcoin_rpc_server, &ord_rpc_server); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + let (inscription, _) = inscribe(&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(&bitcoin_rpc_server, &ord_rpc_server); let mut regex = String::new(); for _ in 0..8 { - let (inscription, _) = inscribe(&rpc_server); + let (inscription, _) = inscribe(&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(&bitcoin_rpc_server, &ord_rpc_server); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + 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}"), format!( ".*

Inscription 1

.* @@ -367,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); @@ -385,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(); @@ -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(&bitcoin_rpc_server, &ord_rpc_server); - let server = TestServer::spawn_with_args(&rpc_server, &["--index-transactions"]); + let (_inscription, reveal) = inscribe(&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 b31de49acb..6637d0653c 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -1,42 +1,45 @@ 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, + #[allow(unused)] + index: Arc, + bitcoin_rpc_url: String, + ord_server_handle: Handle, port: u16, #[allow(unused)] tempdir: TempDir, - 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(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 { + std::env::set_var("ORD_INTEGRATION_TEST", "1"); + let tempdir = TempDir::new().unwrap(); - let cookie_file = match 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() @@ -44,36 +47,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", - rpc_server.url(), + 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(" "), - server_args.join(" "), - ).to_args()) - .env("ORD_INTEGRATION_TEST", "1") - .current_dir(&tempdir) - .spawn().unwrap(); + ord_server_args.join(" "), + )); + + 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, + index, + bitcoin_rpc_url: bitcoin_rpc_server.url(), + ord_server_handle, port, - rpc_url: rpc_server.url(), + tempdir, } } @@ -120,24 +131,28 @@ impl TestServer { } pub(crate) fn sync_server(&self) { - let client = Client::new(&self.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.. { 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"); } - 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(); } } diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index ea50fe36b8..5fa48f4e77 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(&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(); - create_wallet(&rpc_server); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet(&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(&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(&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(&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, @@ -100,3 +119,50 @@ fn runic_utxos_are_deducted_from_cardinal() { } ); } +#[test] +fn unsynced_wallet_fails_with_unindexed_output() { + 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(&bitcoin_rpc_server, &ord_rpc_server); + + assert_eq!( + CommandBuilder::new("wallet balance") + .ord_rpc_server(&ord_rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .run_and_deserialize_output::(), + Output { + cardinal: 50 * COIN_VALUE, + ordinal: 0, + runic: None, + runes: None, + total: 50 * COIN_VALUE, + } + ); + + let no_sync_ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &[], + &["--no-sync", "--enable-json-api"], + ); + + inscribe(&bitcoin_rpc_server, &ord_rpc_server); + + CommandBuilder::new("wallet balance") + .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_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+.*", + ) + .run_and_extract_stdout(); +} diff --git a/tests/wallet/cardinals.rs b/tests/wallet/cardinals.rs index db966c39a8..b2815b36ef 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(&bitcoin_rpc_server, &ord_rpc_server); + + inscribe(&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 2158fe3ffd..bbb5e2c873 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -1,20 +1,25 @@ -use {super::*, std::ops::Deref}; +use { + super::*, + ord::subcommand::wallet::{create, inscriptions, receive}, + std::ops::Deref, +}; #[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(&bitcoin_rpc_server, &ord_rpc_server); - assert_eq!(rpc_server.descriptors().len(), 3); + let (inscription, _) = inscribe(&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!( @@ -26,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(&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(&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
.*", ); @@ -63,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(&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(&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(); @@ -105,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(&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", @@ -123,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(&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(&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(&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") @@ -170,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(&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(&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(); @@ -191,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(&bitcoin_rpc_server, &ord_rpc_server); + + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let output = OutPoint { txid: reveal, @@ -205,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", @@ -215,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(&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(&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(); @@ -266,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; @@ -289,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(&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(); @@ -316,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; @@ -332,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) - .run_and_deserialize_output::(); + .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(&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(&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; @@ -392,13 +476,18 @@ 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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let destination = CommandBuilder::new("wallet receive") - .rpc_server(&rpc_server) - .run_and_deserialize_output::() + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::() .address; let txid = CommandBuilder::new(format!( @@ -406,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, @@ -420,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(&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(); @@ -436,42 +530,53 @@ 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(&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(&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) - .run_and_deserialize_output::>(); + .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); } #[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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let parent_id = "0000000000000000000000000000000000000000000000000000000000000000i0"; @@ -479,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(); @@ -487,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(&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) @@ -510,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) @@ -533,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
.*
.*", @@ -545,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
.*
.*", @@ -556,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(&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); @@ -581,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
.*
.*.*", @@ -601,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(&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(); @@ -627,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(&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.*" @@ -658,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(&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); + bitcoin_rpc_server.mine_blocks(1); - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - - assert!(!ord_server + assert!(!ord_rpc_server .request(format!("/inscription/{inscription}"),) .text() .unwrap() @@ -683,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(&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); + 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
.*", ); @@ -709,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(&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", @@ -723,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); - - 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
.*", ); @@ -740,11 +874,14 @@ fn cbor_metadata_appears_on_inscription_page() { #[test] fn error_message_when_parsing_json_metadata_is_reasonable() { + 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", "{") + .bitcoin_rpc_server(&bitcoin_rpc_server) .stderr_regex(".*failed to parse JSON metadata.*") .expected_exit_code(1) .run_and_extract_stdout(); @@ -752,11 +889,14 @@ fn error_message_when_parsing_json_metadata_is_reasonable() { #[test] fn error_message_when_parsing_cbor_metadata_is_reasonable() { + 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]) + .bitcoin_rpc_server(&bitcoin_rpc_server) .stderr_regex(".*failed to parse CBOR metadata.*") .expected_exit_code(1) .run_and_extract_stdout(); @@ -764,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(); - 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(&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("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(); @@ -782,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(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet(&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") .write("inscription.txt", "Hello World") @@ -795,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); + 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, &[]); - - 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!( @@ -813,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
.*", ); @@ -821,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(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet(&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") @@ -836,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(), @@ -852,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(); - 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(&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::(); - 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; @@ -891,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); - - 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/{}", 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(&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") @@ -929,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(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&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") @@ -957,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; @@ -971,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
.*", @@ -985,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
.*", @@ -993,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
.*", @@ -1001,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), ); @@ -1009,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(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet(&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") @@ -1022,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; @@ -1036,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
.*", @@ -1050,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
.*", @@ -1058,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
.*", @@ -1066,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), ); @@ -1074,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(); - 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(&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::(); - 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; @@ -1100,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 { @@ -1115,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
.*", @@ -1131,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
.*", @@ -1139,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
.*", @@ -1150,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(); - 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(&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::(); - 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; @@ -1176,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 { @@ -1192,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
.*", @@ -1208,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
.*", @@ -1216,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
.*", @@ -1227,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 coinbase_tx = &rpc_server.mine_blocks(1)[0].txdata[0]; + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + 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); - 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") @@ -1245,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(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&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) @@ -1271,7 +1436,7 @@ fn inscribe_can_compress() { let response = client .get( - test_server + ord_rpc_server .url() .join(format!("/content/{inscription}",).as_ref()) .unwrap(), @@ -1292,7 +1457,7 @@ fn inscribe_can_compress() { let response = client .get( - test_server + ord_rpc_server .url() .join(format!("/content/{inscription}",).as_ref()) .unwrap(), @@ -1306,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(&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) @@ -1332,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(), @@ -1346,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(&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(); @@ -1367,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(&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(); @@ -1386,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(&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") @@ -1401,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
@@ -1437,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(&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") @@ -1450,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!( @@ -1462,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
.*", @@ -1476,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
.*", @@ -1484,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
.*", @@ -1492,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), ); @@ -1500,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(&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; @@ -1522,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!( @@ -1534,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
.*", @@ -1548,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
.*", @@ -1556,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
.*", @@ -1564,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
.*", @@ -1572,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), ); @@ -1580,68 +1761,92 @@ 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(&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(&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`\n") + .expected_stderr("error: could not find sat `5000000000` in wallet outputs\n") .run_and_extract_stdout(); } #[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(&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; @@ -1653,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!( ".*.*.*.*", @@ -1669,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(&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") @@ -1681,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(); @@ -1689,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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(2); let set_fee_rate = 1.0; @@ -1703,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(); @@ -1720,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; @@ -1742,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(&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) @@ -1768,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(), @@ -1778,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(); @@ -1803,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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + 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" )) .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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let delegate = "0000000000000000000000000000000000000000000000000000000000000000i0"; @@ -1839,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(); @@ -1847,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(&bitcoin_rpc_server, &ord_rpc_server); - let (delegate, _) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + 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") @@ -1865,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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let delegate = "0000000000000000000000000000000000000000000000000000000000000000i0"; @@ -1899,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..e7ede054af 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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, reveal) = inscribe(&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(&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(&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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); - let (inscription, _) = inscribe(&rpc_server); + let (inscription, _) = inscribe(&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..bb8202a141 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(&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(&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(&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 3f5f9120d4..1d69f47190 100644 --- a/tests/wallet/receive.rs +++ b/tests/wallet/receive.rs @@ -1,13 +1,16 @@ -use {super::*, ord::subcommand::wallet::receive::Output}; +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, &[], &["--enable-json-api"]); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let output = CommandBuilder::new("wallet receive") - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .bitcoin_rpc_server(&bitcoin_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..47936fe9c1 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(&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(&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(&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(&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(&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 c8fb2d73f8..2d2ce4dc00 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -1,31 +1,40 @@ -use {super::*, ord::subcommand::wallet::send::Output, std::collections::BTreeMap}; +use { + super::*, + ord::subcommand::wallet::{balance, create, send}, + std::collections::BTreeMap, +}; #[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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe(&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::(); + .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

.*
.* @@ -45,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(&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(); @@ -61,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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe(&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) - .run_and_deserialize_output::(); + .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
.*", @@ -90,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) - .run_and_deserialize_output::(); + .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) - .run_and_deserialize_output::(); + .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(&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", ) @@ -123,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(&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) - .run_and_deserialize_output::(); + .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(&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(); @@ -161,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(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe(&rpc_server); + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = OutPoint { txid: reveal, @@ -176,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" @@ -186,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(&bitcoin_rpc_server, &ord_rpc_server); - let (_, reveal) = inscribe(&rpc_server); + let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = OutPoint { txid: reveal, @@ -201,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) - .run_and_deserialize_output::(); + .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(&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()), @@ -224,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(); @@ -253,7 +311,7 @@ fn splitting_merged_inscriptions_is_possible() { }, ], indexed: true, - runes: BTreeMap::new(), + runes: Vec::new(), sat_ranges: Some(vec![ (5000000000, 10000000000,), (10000000000, 15000000000,), @@ -271,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 @@ -283,43 +342,51 @@ fn splitting_merged_inscriptions_is_possible() { "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {}i2", reveal_txid, )) - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .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) - .run_and_deserialize_output::(); + .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) - .run_and_deserialize_output::(); + .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(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + let (_, reveal) = inscribe(&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(); @@ -327,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(&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) - .run_and_deserialize_output::(); + .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(); @@ -355,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" @@ -369,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(&bitcoin_rpc_server, &ord_rpc_server); - let (_, reveal) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 1btc") - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .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" @@ -398,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(&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(); @@ -414,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(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription} --fee-rate 2.0" )) - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .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(); @@ -445,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(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe(&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: @@ -465,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(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription} --fee-rate 2.0 --postage 77000sat" )) - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .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(); @@ -497,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(&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(); @@ -514,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(&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(); @@ -534,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(&bitcoin_rpc_server, &ord_rpc_server); - etch(&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{}", 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(); @@ -554,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(&bitcoin_rpc_server, &ord_rpc_server); - etch(&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{}", 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(); @@ -574,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(&bitcoin_rpc_server, &ord_rpc_server); - etch(&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{}", Rune(RUNE) )) - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .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!( @@ -618,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(&bitcoin_rpc_server, &ord_rpc_server); - etch(&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", ) - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .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!( @@ -661,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(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let rune = Rune(RUNE); @@ -677,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) - .run_and_deserialize_output::(); + .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!( @@ -727,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(&bitcoin_rpc_server, &ord_rpc_server); - etch(&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{}", Rune(RUNE) )) - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .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!( @@ -777,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)); @@ -791,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(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, 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{}", - rune, + Rune(RUNE), )) - .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .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 { @@ -843,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); @@ -868,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(&bitcoin_rpc_server, &ord_rpc_server); - etch(&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", ) - .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(); @@ -893,28 +1066,36 @@ 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(&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) - .run_and_deserialize_output::(), - ord::subcommand::wallet::balance::Output { + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(), + balance::Output { cardinal: 10000, ordinal: 10000, runic: Some(0), @@ -929,16 +1110,18 @@ 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) - .run_and_deserialize_output::(), - ord::subcommand::wallet::balance::Output { + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) + .run_and_deserialize_output::(), + balance::Output { cardinal: 0, ordinal: 10000, runic: Some(10000), @@ -951,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..61eb85ac19 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, &[], &["--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") - .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") - .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, &[], &["--enable-json-api"]); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet transactions") - .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") - .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") - .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") - .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 4db705f2973d425b734600b3ca8ba3b565f0902f Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 16:32:53 -0800 Subject: [PATCH 03/11] Break deploy recipes into multiple lines (#3026) --- justfile | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/justfile b/justfile index 216b2fae37..b248230f39 100644 --- a/justfile +++ b/justfile @@ -25,17 +25,23 @@ deploy branch remote chain domain: rsync -avz deploy/checkout root@{{domain}}:deploy/checkout ssh root@{{domain}} 'cd deploy && ./checkout {{branch}} {{remote}} {{chain}} {{domain}}' -deploy-mainnet-alpha branch='master' remote='ordinals/ord': (deploy branch remote 'main' 'alpha.ordinals.net') +deploy-mainnet-alpha branch='master' remote='ordinals/ord': \ + (deploy branch remote 'main' 'alpha.ordinals.net') -deploy-mainnet-bravo branch='master' remote='ordinals/ord': (deploy branch remote 'main' 'bravo.ordinals.net') +deploy-mainnet-bravo branch='master' remote='ordinals/ord': \ + (deploy branch remote 'main' 'bravo.ordinals.net') -deploy-mainnet-charlie branch='master' remote='ordinals/ord': (deploy branch remote 'main' 'charlie.ordinals.net') +deploy-mainnet-charlie branch='master' remote='ordinals/ord': \ + (deploy branch remote 'main' 'charlie.ordinals.net') -deploy-regtest branch='master' remote='ordinals/ord': (deploy branch remote 'regtest' 'regtest.ordinals.net') +deploy-regtest branch='master' remote='ordinals/ord': \ + (deploy branch remote 'regtest' 'regtest.ordinals.net') -deploy-signet branch='master' remote='ordinals/ord': (deploy branch remote 'signet' 'signet.ordinals.net') +deploy-signet branch='master' remote='ordinals/ord': \ + (deploy branch remote 'signet' 'signet.ordinals.net') -deploy-testnet branch='master' remote='ordinals/ord': (deploy branch remote 'test' 'testnet.ordinals.net') +deploy-testnet branch='master' remote='ordinals/ord': \ + (deploy branch remote 'test' 'testnet.ordinals.net') deploy-all: \ deploy-regtest \ @@ -175,7 +181,9 @@ convert-logo-to-favicon: convert -background none -resize 256x256 logo.svg static/favicon.png update-mdbook-theme: - curl https://raw.githubusercontent.com/rust-lang/mdBook/v0.4.35/src/theme/index.hbs > docs/theme/index.hbs + curl \ + https://raw.githubusercontent.com/rust-lang/mdBook/v0.4.35/src/theme/index.hbs \ + > docs/theme/index.hbs audit-cache: cargo run --package audit-cache From df04d3501c86321934f6bf73e787458d1b31445a Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 16:38:46 -0800 Subject: [PATCH 04/11] Test fee-spent inscription numbering (#3032) --- docs/src/inscriptions.md | 16 ++ src/index.rs | 312 +++++------------------ src/index/updater/inscription_updater.rs | 11 +- 3 files changed, 83 insertions(+), 256 deletions(-) diff --git a/docs/src/inscriptions.md b/docs/src/inscriptions.md index 3104cee8b9..3aa05ee833 100644 --- a/docs/src/inscriptions.md +++ b/docs/src/inscriptions.md @@ -124,6 +124,22 @@ go through the inputs consecutively and look for all inscription `envelopes`. | 3 | 0 | | | 4 | 1 | i6 | +Inscription Numbers +------------------- + +Inscriptions are assigned inscription numbers starting at zero, first by the +order reveal transactions appear in blocks, and the order that reveal envelopes +appear in those transactions. + +Due to a historical bug in `ord` which cannot be fixed without changing a great +many inscription numbers, inscriptions which are revealed and then immediately +spent to fees are numbered as if they appear last in the block in which they +are revealed. + +Inscriptions which are cursed are numbered starting at negative one, counting +down. Cursed inscriptions on and after the jubilee at block 824544 are +vindicated, and are assigned positive inscription numbers. + Sandboxing ---------- diff --git a/src/index.rs b/src/index.rs index 2991ddaf20..fa14f2637e 100644 --- a/src/index.rs +++ b/src/index.rs @@ -722,6 +722,15 @@ impl Index { .unwrap_or_default() } + #[cfg(test)] + pub(crate) fn inscription_number(&self, inscription_id: InscriptionId) -> i32 { + self + .get_inscription_entry(inscription_id) + .unwrap() + .unwrap() + .inscription_number + } + pub(crate) fn block_count(&self) -> Result { self.begin_read()?.block_count() } @@ -3290,15 +3299,7 @@ mod tests { None, ); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(inscription_id), -1); } } @@ -3335,15 +3336,7 @@ mod tests { None, ); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - 0 - ); + assert_eq!(context.index.inscription_number(inscription_id), 0); } } @@ -3371,15 +3364,7 @@ mod tests { assert_eq!(context.rpc_server.height(), 109); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(inscription_id), -1); let txid = context.rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(2, 0, 0, witness)], @@ -3392,15 +3377,7 @@ mod tests { assert_eq!(context.rpc_server.height(), 110); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - 0 - ); + assert_eq!(context.index.inscription_number(inscription_id), 0); } } @@ -3426,15 +3403,7 @@ mod tests { context.mine_blocks(1); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(inscription_id), -1); } } @@ -3454,15 +3423,7 @@ mod tests { context.mine_blocks(1); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(inscription_id), -1); } } @@ -3491,15 +3452,7 @@ mod tests { context.mine_blocks(1); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(inscription_id), -1); } } @@ -3529,15 +3482,7 @@ mod tests { context.mine_blocks(1); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(inscription_id), -1); } } @@ -3573,15 +3518,7 @@ mod tests { None, ); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - 0 - ); + assert_eq!(context.index.inscription_number(inscription_id), 0); } } @@ -3619,15 +3556,7 @@ mod tests { None, ); - assert_eq!( - context - .index - .get_inscription_entry(second_inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(second_inscription_id), -1); } } @@ -3682,35 +3611,9 @@ mod tests { Some(150 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(first) - .unwrap() - .unwrap() - .inscription_number, - 0 - ); - - assert_eq!( - context - .index - .get_inscription_entry(second) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); - - assert_eq!( - context - .index - .get_inscription_entry(third) - .unwrap() - .unwrap() - .inscription_number, - -2 - ); + assert_eq!(context.index.inscription_number(first), 0); + assert_eq!(context.index.inscription_number(second), -1); + assert_eq!(context.index.inscription_number(third), -2); } } @@ -3786,35 +3689,9 @@ mod tests { Some(50 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(first) - .unwrap() - .unwrap() - .inscription_number, - 0 - ); - - assert_eq!( - context - .index - .get_inscription_entry(second) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); - - assert_eq!( - context - .index - .get_inscription_entry(third) - .unwrap() - .unwrap() - .inscription_number, - -2 - ); + assert_eq!(context.index.inscription_number(first), 0); + assert_eq!(context.index.inscription_number(second), -1); + assert_eq!(context.index.inscription_number(third), -2); } } @@ -3906,15 +3783,7 @@ mod tests { Some(150 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(first) - .unwrap() - .unwrap() - .inscription_number, - 0 - ); + assert_eq!(context.index.inscription_number(first), 0); assert_eq!( context @@ -3925,25 +3794,9 @@ mod tests { fourth ); - assert_eq!( - context - .index - .get_inscription_entry(fourth) - .unwrap() - .unwrap() - .inscription_number, - -3 - ); + assert_eq!(context.index.inscription_number(fourth), -3); - assert_eq!( - context - .index - .get_inscription_entry(ninth) - .unwrap() - .unwrap() - .inscription_number, - -8 - ); + assert_eq!(context.index.inscription_number(ninth), -8); } } @@ -4047,15 +3900,7 @@ mod tests { Some(100 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(cursed) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(cursed), -1); let witness = envelope(&[ b"ord", @@ -4083,15 +3928,7 @@ mod tests { Some(100 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(reinscription_on_cursed) - .unwrap() - .unwrap() - .inscription_number, - 1 - ); + assert_eq!(context.index.inscription_number(reinscription_on_cursed), 1); } } @@ -4128,15 +3965,7 @@ mod tests { Some(100 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(cursed) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(cursed), -1); let witness = envelope(&[ b"ord", @@ -4164,15 +3993,7 @@ mod tests { Some(100 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(reinscription_on_cursed) - .unwrap() - .unwrap() - .inscription_number, - 1 - ); + assert_eq!(context.index.inscription_number(reinscription_on_cursed), 1); let witness = envelope(&[ b"ord", @@ -4203,10 +4024,7 @@ mod tests { assert_eq!( context .index - .get_inscription_entry(second_reinscription_on_cursed) - .unwrap() - .unwrap() - .inscription_number, + .inscription_number(second_reinscription_on_cursed), -2 ); @@ -4934,15 +4752,7 @@ mod tests { Some(50 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(inscription_id), -1); } } @@ -4998,15 +4808,7 @@ mod tests { Some(50 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(child_inscription_id) - .unwrap() - .unwrap() - .inscription_number, - -1 - ); + assert_eq!(context.index.inscription_number(child_inscription_id), -1); assert_eq!( context @@ -5351,23 +5153,10 @@ mod tests { Some(50 * COIN_VALUE), ); - assert_eq!( - context - .index - .get_inscription_entry(inscription_id) - .unwrap() - .unwrap() - .inscription_number, - 0 - ); + assert_eq!(context.index.inscription_number(inscription_id), 0); assert_eq!( - context - .index - .get_inscription_entry(cursed_reinscription_id) - .unwrap() - .unwrap() - .inscription_number, + context.index.inscription_number(cursed_reinscription_id), -1 ); } @@ -5840,4 +5629,31 @@ mod tests { }) .unwrap()); } + + #[test] + fn fee_spent_inscriptions_are_numbered_last_in_block() { + for context in Context::configurations() { + context.mine_blocks(2); + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0, inscription("text/plain", "hello").to_witness())], + fee: 50 * COIN_VALUE, + ..Default::default() + }); + + let a = InscriptionId { txid, index: 0 }; + + let txid = context.rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(2, 0, 0, inscription("text/plain", "hello").to_witness())], + ..Default::default() + }); + + let b = InscriptionId { txid, index: 0 }; + + context.mine_blocks(1); + + assert_eq!(context.index.inscription_number(a), 1); + assert_eq!(context.index.inscription_number(b), 0); + } + } } diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index 19f99033cc..8c65881014 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -188,10 +188,6 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { None }; - let unbound = current_input_value == 0 - || curse == Some(Curse::UnrecognizedEvenField) - || inscription.payload.unrecognized_even_field; - let offset = inscription .payload .pointer() @@ -208,7 +204,9 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { parent: inscription.payload.parent(), pointer: inscription.payload.pointer(), reinscription: inscribed_offsets.get(&offset).is_some(), - unbound, + unbound: current_input_value == 0 + || curse == Some(Curse::UnrecognizedEvenField) + || inscription.payload.unrecognized_even_field, vindicated: curse.is_some() && jubilant, }, }); @@ -411,13 +409,10 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { let inscription_number = if cursed { let number: i32 = self.cursed_inscription_count.try_into().unwrap(); self.cursed_inscription_count += 1; - - // because cursed numbers start at -1 -(number + 1) } else { let number: i32 = self.blessed_inscription_count.try_into().unwrap(); self.blessed_inscription_count += 1; - number }; From e967a8c897689fdfa17c6a032596ae7606f61422 Mon Sep 17 00:00:00 2001 From: Jeremy Rubin Date: Tue, 23 Jan 2024 05:24:31 +0000 Subject: [PATCH 05/11] Remove uneccessary allocations in Inscription Script Creation (#3039) --- src/inscriptions/inscription.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/inscriptions/inscription.rs b/src/inscriptions/inscription.rs index 271fa75efd..057f34f76e 100644 --- a/src/inscriptions/inscription.rs +++ b/src/inscriptions/inscription.rs @@ -2,10 +2,7 @@ use { super::*, anyhow::ensure, bitcoin::{ - blockdata::{ - opcodes, - script::{self, PushBytesBuf}, - }, + blockdata::{opcodes, script}, ScriptBuf, }, brotli::enc::{writer::CompressorWriter, BrotliEncoderParams}, @@ -141,7 +138,7 @@ impl Inscription { if let Some(body) = &self.body { builder = builder.push_slice(envelope::BODY_TAG); for chunk in body.chunks(MAX_SCRIPT_ELEMENT_SIZE) { - builder = builder.push_slice(PushBytesBuf::try_from(chunk.to_vec()).unwrap()); + builder = builder.push_slice::<&script::PushBytes>(chunk.try_into().unwrap()); } } From 841e70c34c6eea2ab3908957977459bea167748b Mon Sep 17 00:00:00 2001 From: raph Date: Tue, 23 Jan 2024 22:45:29 +0100 Subject: [PATCH 06/11] Better wallet error messages (#3041) --- src/wallet.rs | 37 ++++++++++++++++++++++--------------- tests/wallet/balance.rs | 6 ++---- tests/wallet/inscribe.rs | 2 +- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index df0308c46f..f8beecf5a0 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 { - bail!("wallet failed to synchronize to index"); + bail!("wallet failed to synchronize with ord server"); } thread::sleep(Duration::from_millis(50)); @@ -92,10 +92,14 @@ impl Wallet { .get(self.ord_url.join(&format!("/output/{output}")).unwrap()) .send()?; + if !response.status().is_success() { + bail!("wallet failed get output: {}", response.text()?); + } + let output_json: OutputJson = serde_json::from_str(&response.text()?)?; if !output_json.indexed { - bail!("output in Bitcoin Core wallet but not in ord index: {output}"); + bail!("output in wallet but not in ord server: {output}"); } Ok(output_json) @@ -141,7 +145,7 @@ impl Wallet { pub(crate) fn get_output_sat_ranges(&self) -> Result)>> { ensure!( self.has_sat_index()?, - "index must be built with `--index-sats` to use `--sat`" + "ord index must be built with `--index-sats` to use `--sat`" ); let mut output_sat_ranges = Vec::new(); @@ -149,7 +153,7 @@ impl Wallet { 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"); + bail!("output {output} in wallet but is spent according to ord server"); } } @@ -163,7 +167,7 @@ impl Wallet { ) -> Result { ensure!( self.has_sat_index()?, - "index must be built with `--index-sats` to use `--sat`" + "ord index must be built with `--index-sats` to use `--sat`" ); for output in utxos.keys() { @@ -215,7 +219,7 @@ impl Wallet { ) .send()?; - if response.status().is_client_error() { + if !response.status().is_success() { bail!("inscription {inscription_id} not found"); } @@ -251,7 +255,7 @@ impl Wallet { ) .send()?; - if response.status().is_client_error() { + if !response.status().is_success() { return Ok(None); } @@ -332,7 +336,7 @@ impl Wallet { tx_out: self .bitcoin_client()? .get_raw_transaction(&satpoint.outpoint.txid, None) - .expect("parent transaction not found in index") + .expect("parent transaction not found in ord server") .output .into_iter() .nth(satpoint.outpoint.vout.try_into().unwrap()) @@ -354,13 +358,16 @@ impl Wallet { } pub(crate) fn get_server_status(&self) -> Result { - Ok(serde_json::from_str( - &self - .ord_client()? - .get(self.ord_url.join("/status").unwrap()) - .send()? - .text()?, - )?) + let response = self + .ord_client()? + .get(self.ord_url.join("/status").unwrap()) + .send()?; + + if !response.status().is_success() { + bail!("could not get status: {}", response.text()?) + } + + Ok(serde_json::from_str(&response.text()?)?) } pub(crate) fn has_rune_index(&self) -> Result { diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index 5fa48f4e77..5e60fd6b2b 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -154,15 +154,13 @@ fn unsynced_wallet_fails_with_unindexed_output() { .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") + .expected_stderr("error: wallet failed to synchronize with ord server\n") .run_and_extract_stdout(); CommandBuilder::new("wallet --no-sync balance") .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+.*", - ) + .stderr_regex(r"error: output in wallet but not in ord server: [[:xdigit:]]{64}:\d+.*") .run_and_extract_stdout(); } diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index bbb5e2c873..d7fb538faa 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -1807,7 +1807,7 @@ fn inscribe_with_sat_arg_fails_if_no_index_or_not_found() { .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") + .expected_stderr("error: ord 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") From 3fdb8c52dcfc6d7e4404e4c5a46c4d87e02a9e3f Mon Sep 17 00:00:00 2001 From: mj10021 Date: Tue, 23 Jan 2024 17:47:38 -0500 Subject: [PATCH 07/11] Add documentation for reinscriptions (#2963) --- docs/src/inscriptions.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/src/inscriptions.md b/docs/src/inscriptions.md index 3aa05ee833..f6524c0ad7 100644 --- a/docs/src/inscriptions.md +++ b/docs/src/inscriptions.md @@ -149,3 +149,16 @@ off-chain content, thus keeping inscriptions immutable and self-contained. This is accomplished by loading HTML and SVG inscriptions inside `iframes` with the `sandbox` attribute, as well as serving inscription content with `Content-Security-Policy` headers. + +Reinscriptions +-------------- + +Previously inscribed sats can be reinscribed with the `--reinscribe` command if +the inscription is present in the wallet. This will only append an inscription to +a sat, not change the initial inscription. + +Reinscribe with satpoint: +`ord wallet inscribe --fee-rate --reinscribe --file --satpoint ` + +Reinscribe on a sat (requires sat index): +`ord --index-sats wallet inscribe --fee-rate --reinscribe --file --sat ` From 5bdd723b808f9708873b1b9eeb6af8bd7c1a2efc Mon Sep 17 00:00:00 2001 From: raph Date: Wed, 24 Jan 2024 03:49:05 +0100 Subject: [PATCH 08/11] Exclude unnecessary docs (#3043) --- justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/justfile b/justfile index b248230f39..96d802bf17 100644 --- a/justfile +++ b/justfile @@ -97,7 +97,7 @@ open: open http://localhost doc: - cargo doc --all --open + cargo doc --workspace --exclude audit-content-security-policy --exclude audit-cache --open prepare-release revision='master': #!/usr/bin/env bash From 5d32e909bf01b3220ecf3ed1130af06b350aac96 Mon Sep 17 00:00:00 2001 From: raph Date: Thu, 25 Jan 2024 02:41:49 +0100 Subject: [PATCH 09/11] Enable JSON API by default (#3047) --- deploy/ord.service | 3 +- docs/src/guides/explorer.md | 8 +- src/server_config.rs | 2 +- src/subcommand/server.rs | 10 +- src/subcommand/server/accept_encoding.rs | 6 +- src/subcommand/server/accept_json.rs | 4 +- tests/balances.rs | 7 +- tests/decode.rs | 2 +- tests/etch.rs | 94 ++++-------- tests/index.rs | 2 +- tests/json_api.rs | 71 +++------ tests/runes.rs | 17 +-- tests/server.rs | 27 ++-- tests/test_server.rs | 8 +- tests/wallet/balance.rs | 22 +-- tests/wallet/cardinals.rs | 3 +- tests/wallet/inscribe.rs | 183 ++++++++--------------- tests/wallet/inscriptions.rs | 9 +- tests/wallet/outputs.rs | 9 +- tests/wallet/receive.rs | 3 +- tests/wallet/sats.rs | 31 ++-- tests/wallet/send.rs | 128 +++++----------- tests/wallet/transactions.rs | 6 +- 23 files changed, 222 insertions(+), 433 deletions(-) diff --git a/deploy/ord.service b/deploy/ord.service index c795ef06d6..d43f14b810 100644 --- a/deploy/ord.service +++ b/deploy/ord.service @@ -19,7 +19,8 @@ ExecStart=/usr/local/bin/ord \ --acme-contact mailto:casey@rodarmor.com \ --csp-origin https://${CSP_ORIGIN} \ --http \ - --https + --https \ + --disable-json-api Group=ord LimitNOFILE=65536 MemoryDenyWriteExecute=true diff --git a/docs/src/guides/explorer.md b/docs/src/guides/explorer.md index ad5cc9cbcb..1f805f2714 100644 --- a/docs/src/guides/explorer.md +++ b/docs/src/guides/explorer.md @@ -14,10 +14,10 @@ To specify a port add the `--http-port` flag: `ord server --http-port 8080` -To enable the JSON-API endpoints add the `--enable-json-api` or `-j` flag (see -[here](#json-api) for more info): +The JSON-API endpoints are enabled by default, to disable them add the +`--disable-json-api` flag (see [here](#json-api) for more info): -`ord server --enable-json-api` +`ord server --disable-json-api` Search ------ @@ -72,7 +72,7 @@ been issued when they are mined: JSON-API -------- -You can run `ord server` with the `--enable-json-api` flag to access endpoints that +By default the `ord server` gives access to endpoints that return JSON instead of HTML if you set the HTTP `Accept: application/json` header. The structure of these objects closely follows what is shown in the HTML. These endpoints are: diff --git a/src/server_config.rs b/src/server_config.rs index 86b363dc5b..b22688d880 100644 --- a/src/server_config.rs +++ b/src/server_config.rs @@ -7,5 +7,5 @@ pub(crate) struct ServerConfig { pub(crate) decompress: bool, pub(crate) domain: Option, pub(crate) index_sats: bool, - pub(crate) is_json_api_enabled: bool, + pub(crate) json_api_enabled: bool, } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 4655416e05..52c138b2b5 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -162,8 +162,8 @@ pub struct Server { pub(crate) https: bool, #[arg(long, help = "Redirect HTTP traffic to HTTPS.")] pub(crate) redirect_http_to_https: bool, - #[arg(long, short = 'j', help = "Enable JSON API.")] - pub(crate) enable_json_api: bool, + #[arg(long, help = "Disable JSON API.")] + pub(crate) disable_json_api: bool, #[arg( long, help = "Decompress encoded content. Currently only supports brotli. Be careful using this on production instances. A decompressed inscription may be arbitrarily large, making decompression a DoS vector." @@ -206,7 +206,7 @@ impl Server { csp_origin: self.csp_origin.clone(), domain: acme_domains.first().cloned(), index_sats: index.has_sat_index(), - is_json_api_enabled: self.enable_json_api, + json_api_enabled: !self.disable_json_api, decompress: self.decompress, }); @@ -1665,7 +1665,7 @@ mod tests { .build(), None, &["--chain", "regtest"], - &["--enable-json-api"], + &[], ) } @@ -1687,7 +1687,7 @@ mod tests { .build(), None, &["--chain", "regtest", "--index-runes"], - &["--enable-json-api"], + &[], ) } diff --git a/src/subcommand/server/accept_encoding.rs b/src/subcommand/server/accept_encoding.rs index 9aff921693..0143339a59 100644 --- a/src/subcommand/server/accept_encoding.rs +++ b/src/subcommand/server/accept_encoding.rs @@ -57,7 +57,7 @@ mod tests { let encodings = AcceptEncoding::from_request_parts( &mut req.into_parts().0, &Arc::new(ServerConfig { - is_json_api_enabled: false, + json_api_enabled: false, decompress: false, ..Default::default() }), @@ -78,7 +78,7 @@ mod tests { let encodings = AcceptEncoding::from_request_parts( &mut req.into_parts().0, &Arc::new(ServerConfig { - is_json_api_enabled: false, + json_api_enabled: false, decompress: false, ..Default::default() }), @@ -107,7 +107,7 @@ mod tests { let encodings = AcceptEncoding::from_request_parts( &mut req.into_parts().0, &Arc::new(ServerConfig { - is_json_api_enabled: false, + json_api_enabled: false, decompress: false, ..Default::default() }), diff --git a/src/subcommand/server/accept_json.rs b/src/subcommand/server/accept_json.rs index a471c3d049..c748baf97e 100644 --- a/src/subcommand/server/accept_json.rs +++ b/src/subcommand/server/accept_json.rs @@ -15,7 +15,7 @@ where state: &S, ) -> Result { let state = Arc::from_ref(state); - let json_api_enabled = state.is_json_api_enabled; + let json_api_enabled = state.json_api_enabled; let json_header = parts .headers .get("accept") @@ -24,7 +24,7 @@ where if json_header && json_api_enabled { Ok(Self(true)) } else if json_header && !json_api_enabled { - Err((StatusCode::NOT_ACCEPTABLE, "JSON API not enabled")) + Err((StatusCode::NOT_ACCEPTABLE, "JSON API disabled")) } else { Ok(Self(false)) } diff --git a/tests/balances.rs b/tests/balances.rs index 4deb478dfc..79de88aae7 100644 --- a/tests/balances.rs +++ b/tests/balances.rs @@ -37,11 +37,8 @@ fn with_runes() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/decode.rs b/tests/decode.rs index 04693aedb1..d27d855d8b 100644 --- a/tests/decode.rs +++ b/tests/decode.rs @@ -93,7 +93,7 @@ fn from_stdin() { #[test] fn from_core() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/etch.rs b/tests/etch.rs index 73226e3982..2b9c5edef5 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -12,8 +12,7 @@ fn flag_is_required() { .network(Network::Regtest) .build(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -34,11 +33,8 @@ fn divisibility_over_max_is_an_error() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -62,11 +58,8 @@ fn supply_over_max_is_an_error() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -90,11 +83,8 @@ fn rune_below_minimum_is_an_error() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -118,11 +108,8 @@ fn reserved_rune_is_an_error() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -144,11 +131,8 @@ fn trying_to_etch_an_existing_rune_is_an_error() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -174,11 +158,8 @@ fn runes_can_be_etched() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -237,11 +218,8 @@ fn etch_sets_integer_fee_rate_correctly() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -273,11 +251,8 @@ fn etch_sets_decimal_fee_rate_correctly() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -309,11 +284,8 @@ fn etch_does_not_select_inscribed_utxos() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -359,11 +331,8 @@ fn inscribe_does_not_select_runic_utxos() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -404,11 +373,8 @@ fn send_amount_does_not_select_runic_utxos() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -439,11 +405,8 @@ fn send_satpoint_does_not_send_runic_utxos() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -474,11 +437,8 @@ fn send_inscription_does_not_select_runic_utxos() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/index.rs b/tests/index.rs index 9a9a3f3ab7..5d44449cac 100644 --- a/tests/index.rs +++ b/tests/index.rs @@ -84,7 +84,7 @@ fn index_runs_with_rpc_user_and_pass_as_env_vars() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/json_api.rs b/tests/json_api.rs index 15b283dc85..ff16c9c4d5 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -4,9 +4,8 @@ use {super::*, bitcoin::BlockHash}; fn get_sat_without_sat_index() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let response = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]) - .json_request("/sat/2099999997689999"); + let response = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]) + .json_request("/sat/2099999997689999"); assert_eq!(response.status(), StatusCode::OK); @@ -40,11 +39,8 @@ fn get_sat_without_sat_index() { fn get_sat_with_inscription_and_sat_index() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -81,11 +77,8 @@ fn get_sat_with_inscription_and_sat_index() { fn get_sat_with_inscription_on_common_sat_and_more_inscriptions() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -140,11 +133,8 @@ fn get_sat_with_inscription_on_common_sat_and_more_inscriptions() { fn get_inscription() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -187,11 +177,8 @@ fn get_inscription() { fn get_inscriptions() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -247,7 +234,7 @@ fn get_inscriptions_in_block() { let ord_rpc_server = TestServer::spawn_with_server_args( &bitcoin_rpc_server, &["--index-sats", "--first-inscription-height", "0"], - &["--enable-json-api"], + &[], ); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -301,7 +288,7 @@ fn get_inscriptions_in_block() { #[test] fn get_output() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(3); @@ -319,11 +306,8 @@ fn get_output() { bitcoin_rpc_server.mine_blocks(1); - let server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-sats"], - &["--no-sync", "--enable-json-api"], - ); + let server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &["--no-sync"]); let response = reqwest::blocking::Client::new() .get(server.url().join(&format!("/output/{}:0", txid)).unwrap()) @@ -339,11 +323,7 @@ fn get_output() { .indexed ); - let server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-sats"], - &["--enable-json-api"], - ); + let server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); let response = server.json_request(format!("/output/{}:0", txid)); assert_eq!(response.status(), StatusCode::OK); @@ -375,11 +355,12 @@ fn get_output() { } #[test] -fn json_request_fails_when_not_enabled() { +fn json_request_fails_when_disabled() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let response = - TestServer::spawn_with_args(&bitcoin_rpc_server, &[]).json_request("/sat/2099999997689999"); + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--disable-json-api"]) + .json_request("/sat/2099999997689999"); assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE); } @@ -391,8 +372,7 @@ fn get_block() { bitcoin_rpc_server.mine_blocks(1); let response = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]) - .json_request("/block/0"); + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]).json_request("/block/0"); assert_eq!(response.status(), StatusCode::OK); @@ -417,7 +397,7 @@ fn get_block() { #[test] fn get_blocks() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); let blocks: Vec = bitcoin_rpc_server .mine_blocks(101) @@ -453,7 +433,7 @@ fn get_blocks() { fn get_transaction() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); let transaction = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].clone(); @@ -484,7 +464,7 @@ fn get_status() { let ord_rpc_server = TestServer::spawn_with_server_args( &bitcoin_rpc_server, &["--regtest", "--index-sats", "--index-runes"], - &["--enable-json-api"], + &[], ); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -534,11 +514,8 @@ fn get_runes() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/runes.rs b/tests/runes.rs index 2b3c529334..4437089781 100644 --- a/tests/runes.rs +++ b/tests/runes.rs @@ -6,8 +6,7 @@ fn flag_is_required() { .network(Network::Regtest) .build(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &[]); CommandBuilder::new("--regtest runes") .bitcoin_rpc_server(&bitcoin_rpc_server) @@ -39,11 +38,8 @@ fn one_rune() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -90,11 +86,8 @@ fn two_runes() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/server.rs b/tests/server.rs index 2449fde82b..cca4cc99b6 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -40,7 +40,7 @@ fn run() { #[test] fn inscription_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -98,7 +98,7 @@ fn inscription_page() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -115,7 +115,7 @@ fn inscription_appears_on_reveal_transaction_page() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -147,7 +147,7 @@ fn multiple_inscriptions_appear_on_reveal_transaction_page() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -164,7 +164,7 @@ fn inscription_appears_on_output_page() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -201,7 +201,7 @@ fn inscription_page_after_send() { #[test] fn inscription_content() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -247,7 +247,7 @@ fn inscription_metadata() { ciborium::ser::into_writer(&cbor_map, &mut encoded_metadata).unwrap(); let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -284,7 +284,7 @@ fn inscription_metadata() { #[test] fn inscriptions_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -305,7 +305,7 @@ fn inscriptions_page() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -322,7 +322,7 @@ fn inscriptions_page_is_sorted() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -484,11 +484,8 @@ fn sat_recursive_endpoints_without_sat_index_return_404() { fn inscription_transactions_are_stored_with_transaction_index() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-transactions"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/test_server.rs b/tests/test_server.rs index 6637d0653c..e027914f17 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -17,6 +17,10 @@ pub(crate) struct TestServer { } impl TestServer { + pub(crate) fn spawn(bitcoin_rpc_server: &test_bitcoincore_rpc::Handle) -> Self { + Self::spawn_with_server_args(bitcoin_rpc_server, &[], &[]) + } + pub(crate) fn spawn_with_args( bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, ord_args: &[&str], @@ -24,10 +28,6 @@ impl TestServer { Self::spawn_with_server_args(bitcoin_rpc_server, ord_args, &[]) } - 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( bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, ord_args: &[&str], diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index 5e60fd6b2b..120029a895 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -4,8 +4,7 @@ use {super::*, ord::subcommand::wallet::balance::Output}; fn wallet_balance() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -39,8 +38,7 @@ fn wallet_balance() { fn inscribed_utxos_are_deducted_from_cardinal() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -81,11 +79,8 @@ fn runic_utxos_are_deducted_from_cardinal() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--regtest", "--index-runes"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest", "--index-runes"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -122,7 +117,7 @@ fn runic_utxos_are_deducted_from_cardinal() { #[test] fn unsynced_wallet_fails_with_unindexed_output() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -142,11 +137,8 @@ fn unsynced_wallet_fails_with_unindexed_output() { } ); - let no_sync_ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &[], - &["--no-sync", "--enable-json-api"], - ); + let no_sync_ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--no-sync"]); inscribe(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/cardinals.rs b/tests/wallet/cardinals.rs index b2815b36ef..f33896cb5e 100644 --- a/tests/wallet/cardinals.rs +++ b/tests/wallet/cardinals.rs @@ -7,8 +7,7 @@ use { fn cardinals() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index d7fb538faa..31c85daa40 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -7,7 +7,7 @@ use { #[test] fn inscribe_creates_inscriptions() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -32,7 +32,7 @@ fn inscribe_creates_inscriptions() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -50,7 +50,7 @@ fn inscribe_works_with_huge_expensive_inscriptions() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -75,7 +75,7 @@ fn metaprotocol_appears_on_inscription_page() { #[test] fn inscribe_fails_if_bitcoin_core_is_too_old() { let bitcoin_rpc_server = test_bitcoincore_rpc::builder().version(230000).build(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); CommandBuilder::new("wallet inscribe --file hello.txt --fee-rate 1") .write("hello.txt", "HELLOWORLD") @@ -89,7 +89,7 @@ fn inscribe_fails_if_bitcoin_core_is_too_old() { #[test] fn inscribe_no_backup() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -109,7 +109,7 @@ fn inscribe_no_backup() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -151,8 +151,7 @@ fn regtest_has_no_content_size_limit() { .network(Network::Regtest) .build(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -172,7 +171,7 @@ fn mainnet_has_no_content_size_limit() { .network(Network::Bitcoin) .build(); - let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -189,7 +188,7 @@ fn mainnet_has_no_content_size_limit() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -209,7 +208,7 @@ fn inscribe_does_not_use_inscribed_sats_as_cardinal_utxos() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -233,7 +232,7 @@ fn refuse_to_reinscribe_sats() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -260,11 +259,8 @@ fn refuse_to_inscribe_already_inscribed_utxo() { #[test] fn inscribe_with_optional_satpoint_arg() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -293,11 +289,8 @@ fn inscribe_with_optional_satpoint_arg() { fn inscribe_with_fee_rate() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -350,11 +343,8 @@ fn inscribe_with_fee_rate() { #[test] fn inscribe_with_commit_fee_rate() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -401,8 +391,7 @@ fn inscribe_with_commit_fee_rate() { #[test] fn inscribe_with_wallet_named_foo() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); CommandBuilder::new("wallet --name foo create") .bitcoin_rpc_server(&bitcoin_rpc_server) @@ -421,8 +410,7 @@ fn inscribe_with_wallet_named_foo() { #[test] fn inscribe_with_dry_run_flag() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -448,8 +436,7 @@ fn inscribe_with_dry_run_flag() { #[test] fn inscribe_with_dry_run_flag_fees_increase() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -477,8 +464,7 @@ fn inscribe_with_dry_run_flag_fees_increase() { #[test] fn inscribe_to_specific_destination() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -511,7 +497,7 @@ fn inscribe_to_specific_destination() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -531,7 +517,7 @@ fn inscribe_to_address_on_different_network() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -547,7 +533,7 @@ fn inscribe_with_no_limit() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -572,7 +558,7 @@ fn inscribe_works_with_postage() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -594,7 +580,7 @@ fn inscribe_with_non_existent_parent_inscription() { #[test] 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); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -666,11 +652,8 @@ fn inscribe_with_parent_inscription_and_fee_rate() { #[test] fn reinscribe_with_flag() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); bitcoin_rpc_server.mine_blocks(1); @@ -718,8 +701,7 @@ fn reinscribe_with_flag() { fn with_reinscribe_flag_but_not_actually_a_reinscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -748,8 +730,7 @@ fn with_reinscribe_flag_but_not_actually_a_reinscription() { fn try_reinscribe_without_flag() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -783,8 +764,7 @@ fn try_reinscribe_without_flag() { fn no_metadata_appears_on_inscription_page_if_no_metadata_is_passed() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -812,8 +792,7 @@ fn no_metadata_appears_on_inscription_page_if_no_metadata_is_passed() { fn json_metadata_appears_on_inscription_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -841,8 +820,7 @@ fn json_metadata_appears_on_inscription_page() { #[test] fn cbor_metadata_appears_on_inscription_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -906,8 +884,7 @@ fn error_message_when_parsing_cbor_metadata_is_reasonable() { fn batch_inscribe_fails_if_batchfile_has_no_inscriptions() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -927,8 +904,7 @@ fn batch_inscribe_fails_if_batchfile_has_no_inscriptions() { fn batch_inscribe_can_create_one_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -967,8 +943,7 @@ fn batch_inscribe_can_create_one_inscription() { fn batch_inscribe_with_multiple_inscriptions() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1011,8 +986,7 @@ fn batch_inscribe_with_multiple_inscriptions() { fn batch_inscribe_with_multiple_inscriptions_with_parent() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1063,8 +1037,7 @@ fn batch_inscribe_with_multiple_inscriptions_with_parent() { fn batch_inscribe_respects_dry_run_flag() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1093,8 +1066,7 @@ fn batch_inscribe_respects_dry_run_flag() { fn batch_in_same_output_but_different_satpoints() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1161,8 +1133,7 @@ fn batch_in_same_output_but_different_satpoints() { fn batch_in_same_output_with_non_default_postage() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1229,8 +1200,7 @@ fn batch_in_same_output_with_non_default_postage() { fn batch_in_separate_outputs_with_parent() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1307,8 +1277,7 @@ fn batch_in_separate_outputs_with_parent() { fn batch_in_separate_outputs_with_parent_and_non_default_postage() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1386,8 +1355,7 @@ fn batch_in_separate_outputs_with_parent_and_non_default_postage() { fn inscribe_does_not_pick_locked_utxos() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1409,8 +1377,7 @@ fn inscribe_does_not_pick_locked_utxos() { fn inscribe_can_compress() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1473,8 +1440,7 @@ fn inscribe_can_compress() { fn inscriptions_are_not_compressed_if_no_space_is_saved_by_compression() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1518,8 +1484,7 @@ fn batch_inscribe_fails_if_invalid_network_destination_address() { .network(Network::Regtest) .build(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1539,8 +1504,7 @@ fn batch_inscribe_fails_if_invalid_network_destination_address() { fn batch_inscribe_fails_with_shared_output_and_destination_set() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1561,8 +1525,7 @@ fn batch_inscribe_fails_with_shared_output_and_destination_set() { fn batch_inscribe_works_with_some_destinations_set_and_others_not() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1613,8 +1576,7 @@ fn batch_inscribe_works_with_some_destinations_set_and_others_not() { fn batch_same_sat() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1679,8 +1641,7 @@ fn batch_same_sat() { fn batch_same_sat_with_parent() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1763,11 +1724,8 @@ fn batch_same_sat_with_parent() { fn inscribe_with_sat_arg() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1797,8 +1755,7 @@ fn inscribe_with_sat_arg() { fn inscribe_with_sat_arg_fails_if_no_index_or_not_found() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1816,7 +1773,7 @@ fn inscribe_with_sat_arg_fails_if_no_index_or_not_found() { .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") @@ -1827,11 +1784,8 @@ fn inscribe_with_sat_arg_fails_if_no_index_or_not_found() { fn batch_inscribe_with_sat_argument_with_parent() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1877,8 +1831,7 @@ fn batch_inscribe_with_sat_argument_with_parent() { fn batch_inscribe_with_sat_arg_fails_if_wrong_mode() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1903,11 +1856,8 @@ fn batch_inscribe_with_sat_arg_fails_if_wrong_mode() { fn batch_inscribe_with_fee_rate() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1965,8 +1915,7 @@ fn batch_inscribe_with_fee_rate() { fn server_can_decompress_brotli() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -2029,8 +1978,7 @@ fn server_can_decompress_brotli() { fn file_inscribe_with_delegate_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -2060,8 +2008,7 @@ fn file_inscribe_with_delegate_inscription() { fn file_inscribe_with_non_existent_delegate_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -2084,8 +2031,7 @@ fn file_inscribe_with_non_existent_delegate_inscription() { fn batch_inscribe_with_delegate_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -2123,8 +2069,7 @@ inscriptions: fn batch_inscribe_with_non_existent_delegate_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index e7ede054af..89dc79f833 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -7,8 +7,7 @@ use { fn inscriptions() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -62,8 +61,7 @@ fn inscriptions() { fn inscriptions_includes_locked_utxos() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -92,8 +90,7 @@ fn inscriptions_includes_locked_utxos() { fn inscriptions_with_postage() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/outputs.rs b/tests/wallet/outputs.rs index bb8202a141..cc7bd9c824 100644 --- a/tests/wallet/outputs.rs +++ b/tests/wallet/outputs.rs @@ -4,8 +4,7 @@ use {super::*, ord::subcommand::wallet::outputs::Output}; fn outputs() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -26,8 +25,7 @@ fn outputs() { fn outputs_includes_locked_outputs() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -50,8 +48,7 @@ fn outputs_includes_locked_outputs() { fn outputs_includes_unbound_outputs() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/receive.rs b/tests/wallet/receive.rs index 1d69f47190..55f33c6910 100644 --- a/tests/wallet/receive.rs +++ b/tests/wallet/receive.rs @@ -3,8 +3,7 @@ use {super::*, ord::subcommand::wallet::receive}; #[test] fn receive() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index 47936fe9c1..86c91c4d66 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -7,8 +7,7 @@ use { fn requires_sat_index() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -24,11 +23,8 @@ fn requires_sat_index() { fn sats() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -47,11 +43,8 @@ fn sats() { fn sats_from_tsv_success() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -71,11 +64,8 @@ fn sats_from_tsv_success() { fn sats_from_tsv_parse_error() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -94,11 +84,8 @@ fn sats_from_tsv_parse_error() { fn sats_from_tsv_file_not_found() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 2d2ce4dc00..293b6b6c88 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -8,8 +8,7 @@ use { fn inscriptions_can_be_sent() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -56,8 +55,7 @@ fn inscriptions_can_be_sent() { fn send_unknown_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -77,8 +75,7 @@ fn send_unknown_inscription() { fn send_inscribed_sat() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -111,8 +108,7 @@ fn send_inscribed_sat() { fn send_on_mainnnet_works_with_wallet_named_foo() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -133,8 +129,7 @@ fn send_on_mainnnet_works_with_wallet_named_foo() { fn send_addresses_must_be_valid_for_network() { let bitcoin_rpc_server = test_bitcoincore_rpc::builder().build(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -156,8 +151,7 @@ fn send_addresses_must_be_valid_for_network() { fn send_on_mainnnet_works_with_wallet_named_ord() { let bitcoin_rpc_server = test_bitcoincore_rpc::builder().build(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -177,8 +171,7 @@ fn send_on_mainnnet_works_with_wallet_named_ord() { fn send_does_not_use_inscribed_sats_as_cardinal_utxos() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -206,8 +199,7 @@ fn send_does_not_use_inscribed_sats_as_cardinal_utxos() { fn do_not_send_within_dust_limit_of_an_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -236,8 +228,7 @@ fn do_not_send_within_dust_limit_of_an_inscription() { fn can_send_after_dust_limit_from_an_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -262,11 +253,8 @@ fn can_send_after_dust_limit_from_an_inscription() { fn splitting_merged_inscriptions_is_possible() { 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"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -373,8 +361,7 @@ fn splitting_merged_inscriptions_is_possible() { fn inscriptions_cannot_be_sent_by_satpoint() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -396,8 +383,7 @@ fn inscriptions_cannot_be_sent_by_satpoint() { fn send_btc_with_fee_rate() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -443,8 +429,7 @@ fn send_btc_with_fee_rate() { fn send_btc_locks_inscriptions() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -479,8 +464,7 @@ fn send_btc_fails_if_lock_unspent_fails() { .fail_lock_unspent(true) .build(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -498,8 +482,7 @@ fn send_btc_fails_if_lock_unspent_fails() { fn wallet_send_with_fee_rate() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -535,8 +518,7 @@ fn wallet_send_with_fee_rate() { fn user_must_provide_fee_rate_to_send() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -561,8 +543,7 @@ fn user_must_provide_fee_rate_to_send() { fn wallet_send_with_fee_rate_and_target_postage() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -599,8 +580,7 @@ fn wallet_send_with_fee_rate_and_target_postage() { fn send_btc_does_not_send_locked_utxos() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -623,11 +603,8 @@ fn sending_rune_that_has_not_been_etched_is_an_error() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -650,11 +627,8 @@ fn sending_rune_with_excessive_precision_is_an_error() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -677,11 +651,8 @@ fn sending_rune_with_insufficient_balance_is_an_error() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -704,11 +675,8 @@ fn sending_rune_works() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -756,11 +724,8 @@ fn sending_spaced_rune_works() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -807,11 +772,8 @@ fn sending_rune_with_divisibility_works() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -882,11 +844,8 @@ fn sending_rune_leaves_unspent_runes_in_wallet() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -954,11 +913,8 @@ fn sending_rune_creates_transaction_with_expected_runestone() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1037,11 +993,8 @@ fn error_messages_use_spaced_runes() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -1070,11 +1023,8 @@ fn sending_rune_does_not_send_inscription() { .network(Network::Regtest) .build(); - let ord_rpc_server = TestServer::spawn_with_server_args( - &bitcoin_rpc_server, - &["--index-runes", "--regtest"], - &["--enable-json-api"], - ); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-runes", "--regtest"], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/transactions.rs b/tests/wallet/transactions.rs index 61eb85ac19..259d3c7356 100644 --- a/tests/wallet/transactions.rs +++ b/tests/wallet/transactions.rs @@ -3,8 +3,7 @@ use {super::*, ord::subcommand::wallet::transactions::Output}; #[test] fn transactions() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -30,8 +29,7 @@ fn transactions() { #[test] fn transactions_with_limit() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &[]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); From 17236d90d7f2a9f2260b4d4289de8e1ffd58681a Mon Sep 17 00:00:00 2001 From: zhiqiangxu <652732310@qq.com> Date: Fri, 26 Jan 2024 00:10:01 +0800 Subject: [PATCH 10/11] Forbid destinations in same-sat mode (#3038) --- src/wallet/inscribe/batch_file.rs | 4 ++-- tests/wallet/inscribe.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/inscribe/batch_file.rs b/src/wallet/inscribe/batch_file.rs index 1f08dafc5c..b1d74e616d 100644 --- a/src/wallet/inscribe/batch_file.rs +++ b/src/wallet/inscribe/batch_file.rs @@ -35,10 +35,10 @@ impl Batchfile { .inscriptions .iter() .any(|entry| entry.destination.is_some()) - && self.mode == Mode::SharedOutput + && (self.mode == Mode::SharedOutput || self.mode == Mode::SameSat) { return Err(anyhow!( - "individual inscription destinations cannot be set in shared-output mode" + "individual inscription destinations cannot be set in shared-output or same-sat mode" )); } diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 31c85daa40..4bc58f3820 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -1517,7 +1517,7 @@ fn batch_inscribe_fails_with_shared_output_and_destination_set() { .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") + .stderr_regex("error: individual inscription destinations cannot be set in shared-output or same-sat mode\n") .run_and_extract_stdout(); } From 13c0fa1efc8b849a55c7febaccd49b948e9edcfd Mon Sep 17 00:00:00 2001 From: raph Date: Fri, 26 Jan 2024 03:42:13 +0100 Subject: [PATCH 11/11] Dump and restore wallet from descriptors (#3048) --- src/lib.rs | 10 ++- src/options.rs | 2 + src/subcommand.rs | 10 ++- src/subcommand/wallet.rs | 5 ++ src/subcommand/wallet/dump.rs | 14 ++++ src/subcommand/wallet/restore.rs | 50 +++++++++++++-- src/wallet.rs | 82 +++++++++++++++++------ test-bitcoincore-rpc/src/api.rs | 8 ++- test-bitcoincore-rpc/src/lib.rs | 3 +- test-bitcoincore-rpc/src/server.rs | 15 ++++- tests/lib.rs | 1 + tests/wallet.rs | 1 + tests/wallet/dump.rs | 72 +++++++++++++++++++++ tests/wallet/restore.rs | 100 ++++++++++++++++++++++++++++- 14 files changed, 339 insertions(+), 34 deletions(-) create mode 100644 src/subcommand/wallet/dump.rs create mode 100644 tests/wallet/dump.rs diff --git a/src/lib.rs b/src/lib.rs index 2ee4bc0706..3372c9e4e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,7 @@ use { env, fmt::{self, Display, Formatter}, fs::{self, File}, - io::{self, Cursor}, + io::{self, Cursor, Read}, mem, net::ToSocketAddrs, ops::{Add, AddAssign, Sub}, @@ -242,7 +242,11 @@ pub fn main() { }) .expect("Error setting handler"); - match Arguments::parse().run() { + let args = Arguments::parse(); + + let minify = args.options.minify; + + match args.run() { Err(err) => { eprintln!("error: {err}"); err @@ -262,7 +266,7 @@ pub fn main() { } Ok(output) => { if let Some(output) = output { - output.print_json(); + output.print_json(minify); } gracefully_shutdown_indexer(); } diff --git a/src/options.rs b/src/options.rs index 5668ecb54d..22c978e46d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -7,6 +7,8 @@ use {super::*, bitcoincore_rpc::Auth}; .args(&["chain_argument", "signet", "regtest", "testnet"]), ))] pub struct Options { + #[arg(long, help = "Minify JSON output.")] + pub(crate) minify: bool, #[arg(long, help = "Load Bitcoin Core data dir from .")] pub(crate) bitcoin_data_dir: Option, #[arg(long, help = "Authenticate to Bitcoin Core RPC with .")] diff --git a/src/subcommand.rs b/src/subcommand.rs index 3f0e57e75b..36abd81436 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -74,15 +74,19 @@ impl Subcommand { } pub trait Output: Send { - fn print_json(&self); + fn print_json(&self, minify: bool); } impl Output for T where T: Serialize + Send, { - fn print_json(&self) { - serde_json::to_writer_pretty(io::stdout(), self).ok(); + fn print_json(&self, minify: bool) { + if minify { + serde_json::to_writer(io::stdout(), self).ok(); + } else { + serde_json::to_writer_pretty(io::stdout(), self).ok(); + } println!(); } } diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 9aac669fc3..e8a78669c5 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -4,12 +4,14 @@ use { inscribe::{Batch, Batchfile, Mode}, Wallet, }, + bitcoincore_rpc::bitcoincore_rpc_json::ListDescriptorsResult, reqwest::Url, }; pub mod balance; pub mod cardinals; pub mod create; +pub mod dump; pub mod etch; pub mod inscribe; pub mod inscriptions; @@ -43,6 +45,8 @@ pub(crate) enum Subcommand { Balance, #[command(about = "Create new wallet")] Create(create::Create), + #[command(about = "Dump wallet descriptors")] + Dump, #[command(about = "Create rune")] Etch(etch::Etch), #[command(about = "Create inscription")] @@ -77,6 +81,7 @@ impl WalletCommand { match self.subcommand { Subcommand::Balance => balance::run(wallet), Subcommand::Create(create) => create.run(wallet), + Subcommand::Dump => dump::run(wallet), Subcommand::Etch(etch) => etch.run(wallet), Subcommand::Inscribe(inscribe) => inscribe.run(wallet), Subcommand::Inscriptions => inscriptions::run(wallet), diff --git a/src/subcommand/wallet/dump.rs b/src/subcommand/wallet/dump.rs new file mode 100644 index 0000000000..bd9641b8d4 --- /dev/null +++ b/src/subcommand/wallet/dump.rs @@ -0,0 +1,14 @@ +use super::*; + +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + eprintln!( + "========================================== += THIS STRING CONTAINS YOUR PRIVATE KEYS = += DO NOT SHARE WITH ANYONE = +==========================================" + ); + + Ok(Some(Box::new( + wallet.bitcoin_client()?.list_descriptors(Some(true))?, + ))) +} diff --git a/src/subcommand/wallet/restore.rs b/src/subcommand/wallet/restore.rs index 9513594f9b..76b61beb80 100644 --- a/src/subcommand/wallet/restore.rs +++ b/src/subcommand/wallet/restore.rs @@ -1,21 +1,61 @@ use super::*; #[derive(Debug, Parser)] +#[clap(group( + ArgGroup::new("source").required(true).args(&["descriptor", "mnemonic"])) +)] pub(crate) struct Restore { - #[arg(help = "Restore wallet from ")] - mnemonic: Mnemonic, + #[arg(long, help = "Restore wallet from from stdin.")] + descriptor: bool, + #[arg(long, help = "Restore wallet from .")] + mnemonic: Option, #[arg( long, - default_value = "", + requires = "mnemonic", help = "Use when deriving wallet" )] - pub(crate) passphrase: String, + pub(crate) passphrase: Option, } impl Restore { pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - wallet.initialize(self.mnemonic.to_seed(self.passphrase))?; + ensure!(!wallet.exists()?, "wallet `{}` already exists", wallet.name); + + if self.descriptor { + let mut buffer = Vec::new(); + std::io::stdin().read_to_end(&mut buffer)?; + + let wallet_descriptors: ListDescriptorsResult = serde_json::from_slice(&buffer)?; + + wallet.initialize_from_descriptors(wallet_descriptors.descriptors)?; + } else if let Some(mnemonic) = self.mnemonic { + wallet.initialize(mnemonic.to_seed(self.passphrase.unwrap_or_default()))?; + } else { + unreachable!(); + } Ok(None) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn descriptor_and_mnemonic_conflict() { + assert_regex_match!( + Arguments::try_parse_from([ + "ord", + "wallet", + "restore", + "--descriptor", + "--mnemonic", + "oil oil oil oil oil oil oil oil oil oil oil oil" + ]) + .unwrap_err() + .to_string(), + ".*--descriptor.*cannot be used with.*--mnemonic.*" + ); + } +} diff --git a/src/wallet.rs b/src/wallet.rs index f8beecf5a0..b21fe7551a 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -5,11 +5,11 @@ use { bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint}, Network, }, - bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, + bitcoincore_rpc::bitcoincore_rpc_json::{Descriptor, ImportDescriptors, Timestamp}, fee_rate::FeeRate, http::StatusCode, inscribe::ParentInfo, - miniscript::descriptor::{Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard}, + miniscript::descriptor::{DescriptorSecretKey, DescriptorXKey, Wildcard}, reqwest::{header, Url}, transaction_builder::TransactionBuilder, }; @@ -32,21 +32,7 @@ impl Wallet { client.load_wallet(&self.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.name); - } + self.check_descriptors(client.list_descriptors(None)?.descriptors)?; Ok(client) } @@ -382,6 +368,64 @@ impl Wallet { self.options.chain() } + pub(crate) fn exists(&self) -> Result { + Ok( + self + .options + .bitcoin_rpc_client(None)? + .list_wallet_dir()? + .iter() + .any(|name| name == &self.name), + ) + } + + pub(crate) fn check_descriptors(&self, descriptors: Vec) -> Result> { + 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.name); + } + + Ok(descriptors) + } + + pub(crate) fn initialize_from_descriptors(&self, descriptors: Vec) -> Result { + let client = check_version(self.options.bitcoin_rpc_client(Some(self.name.clone()))?)?; + + let descriptors = self.check_descriptors(descriptors)?; + + client.create_wallet(&self.name, None, Some(true), None, None)?; + + for descriptor in descriptors { + client.import_descriptors(ImportDescriptors { + descriptor: descriptor.desc, + timestamp: descriptor.timestamp, + active: Some(true), + range: descriptor.range.map(|(start, end)| { + ( + usize::try_from(start).unwrap_or(0), + usize::try_from(end).unwrap_or(0), + ) + }), + next_index: descriptor + .next + .map(|next| usize::try_from(next).unwrap_or(0)), + internal: descriptor.internal, + label: None, + })?; + } + + Ok(()) + } + pub(crate) fn initialize(&self, seed: [u8; 64]) -> Result { check_version(self.options.bitcoin_rpc_client(None)?)?.create_wallet( &self.name, @@ -441,13 +485,13 @@ impl Wallet { let mut key_map = std::collections::HashMap::new(); key_map.insert(public_key.clone(), secret_key); - let desc = Descriptor::new_tr(public_key, None)?; + let descriptor = miniscript::descriptor::Descriptor::new_tr(public_key, None)?; self .options .bitcoin_rpc_client(Some(self.name.clone()))? .import_descriptors(ImportDescriptors { - descriptor: desc.to_string_with_secret(&key_map), + descriptor: descriptor.to_string_with_secret(&key_map), timestamp: Timestamp::Now, active: Some(true), range: None, diff --git a/test-bitcoincore-rpc/src/api.rs b/test-bitcoincore-rpc/src/api.rs index 8bef7734f2..8472da4aa2 100644 --- a/test-bitcoincore-rpc/src/api.rs +++ b/test-bitcoincore-rpc/src/api.rs @@ -163,11 +163,17 @@ pub trait Api { ) -> Result; #[rpc(name = "listdescriptors")] - fn list_descriptors(&self) -> Result; + fn list_descriptors( + &self, + _with_private_keys: Option, + ) -> Result; #[rpc(name = "loadwallet")] fn load_wallet(&self, wallet: String) -> Result; #[rpc(name = "listwallets")] fn list_wallets(&self) -> Result, jsonrpc_core::Error>; + + #[rpc(name = "listwalletdir")] + fn list_wallet_dir(&self) -> Result; } diff --git a/test-bitcoincore-rpc/src/lib.rs b/test-bitcoincore-rpc/src/lib.rs index ed6b68fe39..05e33af726 100644 --- a/test-bitcoincore-rpc/src/lib.rs +++ b/test-bitcoincore-rpc/src/lib.rs @@ -23,7 +23,8 @@ use { GetRawTransactionResultVoutScriptPubKey, GetTransactionResult, GetTransactionResultDetail, GetTransactionResultDetailCategory, GetTxOutResult, GetWalletInfoResult, ImportDescriptors, ImportMultiResult, ListDescriptorsResult, ListTransactionResult, ListUnspentResultEntry, - LoadWalletResult, SignRawTransactionInput, SignRawTransactionResult, Timestamp, WalletTxInfo, + ListWalletDirItem, ListWalletDirResult, LoadWalletResult, SignRawTransactionInput, + SignRawTransactionResult, Timestamp, WalletTxInfo, }, jsonrpc_core::{IoHandler, Value}, jsonrpc_http_server::{CloseHandle, ServerBuilder}, diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index 2e60d30358..f65237f49a 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -732,7 +732,10 @@ impl Api for Server { Ok(true) } - fn list_descriptors(&self) -> Result { + fn list_descriptors( + &self, + _with_private_keys: Option, + ) -> Result { Ok(ListDescriptorsResult { wallet_name: "ord".into(), descriptors: self @@ -773,4 +776,14 @@ impl Api for Server { .collect::>(), ) } + + fn list_wallet_dir(&self) -> Result { + Ok(ListWalletDirResult { + wallets: self + .list_wallets()? + .into_iter() + .map(|name| ListWalletDirItem { name }) + .collect(), + }) + } } diff --git a/tests/lib.rs b/tests/lib.rs index 3eb9ded79a..1abb3d8738 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -7,6 +7,7 @@ use { blockdata::constants::COIN_VALUE, Network, OutPoint, Txid, }, + bitcoincore_rpc::bitcoincore_rpc_json::ListDescriptorsResult, chrono::{DateTime, Utc}, executable_path::executable_path, ord::{ diff --git a/tests/wallet.rs b/tests/wallet.rs index 3e04064183..2e56e7526a 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -3,6 +3,7 @@ use super::*; mod balance; mod cardinals; mod create; +mod dump; mod inscribe; mod inscriptions; mod outputs; diff --git a/tests/wallet/dump.rs b/tests/wallet/dump.rs new file mode 100644 index 0000000000..1de10774c4 --- /dev/null +++ b/tests/wallet/dump.rs @@ -0,0 +1,72 @@ +use super::*; + +#[test] +fn dumped_descriptors_match_wallet_descriptors() { + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + let output = CommandBuilder::new("wallet dump") + .bitcoin_rpc_server(&bitcoin_rpc_server) + .stderr_regex(".*") + .run_and_deserialize_output::(); + + assert!(bitcoin_rpc_server + .descriptors() + .iter() + .zip(output.descriptors.iter()) + .all(|(wallet_descriptor, output_descriptor)| *wallet_descriptor == output_descriptor.desc)); +} + +#[test] +fn dumped_descriptors_restore() { + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + let output = CommandBuilder::new("wallet dump") + .bitcoin_rpc_server(&bitcoin_rpc_server) + .stderr_regex(".*") + .run_and_deserialize_output::(); + + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + CommandBuilder::new("wallet restore --descriptor") + .stdin(serde_json::to_string(&output).unwrap().as_bytes().to_vec()) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .run_and_extract_stdout(); + + assert!(bitcoin_rpc_server + .descriptors() + .iter() + .zip(output.descriptors.iter()) + .all(|(wallet_descriptor, output_descriptor)| *wallet_descriptor == output_descriptor.desc)); +} + +#[test] +fn dump_and_restore_descriptors_with_minify() { + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + let output = CommandBuilder::new("--minify wallet dump") + .bitcoin_rpc_server(&bitcoin_rpc_server) + .stderr_regex(".*") + .run_and_deserialize_output::(); + + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + CommandBuilder::new("wallet restore --descriptor") + .stdin(serde_json::to_string(&output).unwrap().as_bytes().to_vec()) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .run_and_extract_stdout(); + + assert!(bitcoin_rpc_server + .descriptors() + .iter() + .zip(output.descriptors.iter()) + .all(|(wallet_descriptor, output_descriptor)| *wallet_descriptor == output_descriptor.desc)); +} diff --git a/tests/wallet/restore.rs b/tests/wallet/restore.rs index 15c6ba20c3..ab360c3af9 100644 --- a/tests/wallet/restore.rs +++ b/tests/wallet/restore.rs @@ -14,7 +14,7 @@ fn restore_generates_same_descriptors() { let rpc_server = test_bitcoincore_rpc::spawn(); - CommandBuilder::new(["wallet", "restore", &mnemonic.to_string()]) + CommandBuilder::new(["wallet", "restore", "--mnemonic", &mnemonic.to_string()]) .bitcoin_rpc_server(&rpc_server) .run_and_extract_stdout(); @@ -42,6 +42,7 @@ fn restore_generates_same_descriptors_with_passphrase() { "restore", "--passphrase", passphrase, + "--mnemonic", &mnemonic.to_string(), ]) .bitcoin_rpc_server(&rpc_server) @@ -49,3 +50,100 @@ fn restore_generates_same_descriptors_with_passphrase() { assert_eq!(rpc_server.descriptors(), descriptors); } + +#[test] +fn restore_to_existing_wallet_fails() { + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn(&bitcoin_rpc_server); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + let descriptors = bitcoin_rpc_server.descriptors(); + + let output = CommandBuilder::new("wallet dump") + .bitcoin_rpc_server(&bitcoin_rpc_server) + .stderr_regex(".*") + .run_and_deserialize_output::(); + + CommandBuilder::new("wallet restore --descriptor") + .stdin(serde_json::to_string(&output).unwrap().as_bytes().to_vec()) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .expected_exit_code(1) + .expected_stderr("error: wallet `ord` already exists\n") + .run_and_extract_stdout(); + + assert_eq!( + descriptors, + output + .descriptors + .into_iter() + .map(|descriptor| descriptor.desc) + .collect::>() + ); +} + +#[test] +fn restore_with_wrong_descriptors_fails() { + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + CommandBuilder::new("wallet --name foo restore --descriptor") + .stdin(r#" +{ + "wallet_name": "bar", + "descriptors": [ + { + "desc": "rawtr(cVMYXp8uf1yFU9AAY6NJu1twA2uT94mHQBGkfgqCCzp6RqiTWCvP)#tah5crv7", + "timestamp": 1706047934, + "active": false, + "internal": null, + "range": null, + "next": null + }, + { + "desc": "rawtr(cVdVu6VRwYXsTPMiptqVYLcp7EtQi5sjxLzbPTSNwW6CkCxBbEFs)#5afaht8d", + "timestamp": 1706047934, + "active": false, + "internal": null, + "range": null, + "next": null + }, + { + "desc": "wpkh([c0b9536d/86'/1'/0']tprv8fXhtVjj3vb7kgxKuiWXzcUsur44gbLbbtwxL4HKmpzkBNoMrYqbQhMe7MWhrZjLFc9RBpTRYZZkrS8HH1Q3SmD5DkfpjKqtd97q1JWfqzr/0/*)#dweuu0ww", + "timestamp": 1706047839, + "active": true, + "internal": false, + "range": [ + 0, + 1000 + ], + "next": 1 + }, + { + "desc": "tr([c0b9536d/86'/1'/0']tprv8fXhtVjj3vb7kgxKuiWXzcUsur44gbLbbtwxL4HKmpzkBNoMrYqbQhMe7MWhrZjLFc9RBpTRYZZkrS8HH1Q3SmD5DkfpjKqtd97q1JWfqzr/1/*)#u6uap67k", + "timestamp": 1706047839, + "active": true, + "internal": true, + "range": [ + 0, + 1013 + ], + "next": 14 + } + ] +}"#.into()) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .expected_exit_code(1) + .expected_stderr("error: wallet \"foo\" contains unexpected output descriptors, and does not appear to be an `ord` wallet, create a new wallet with `ord wallet create`\n") + .run_and_extract_stdout(); +} + +#[test] +fn restore_with_compact_works() { + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + CommandBuilder::new("wallet restore --descriptor") + .stdin(r#"{"wallet_name":"foo","descriptors":[{"desc":"rawtr(cVMYXp8uf1yFU9AAY6NJu1twA2uT94mHQBGkfgqCCzp6RqiTWCvP)#tah5crv7","timestamp":1706047934,"active":false,"internal":null,"range":null,"next":null},{"desc":"rawtr(cVdVu6VRwYXsTPMiptqVYLcp7EtQi5sjxLzbPTSNwW6CkCxBbEFs)#5afaht8d","timestamp":1706047934,"active":false,"internal":null,"range":null,"next":null},{"desc":"tr([c0b9536d/86'/1'/0']tprv8fXhtVjj3vb7kgxKuiWXzcUsur44gbLbbtwxL4HKmpzkBNoMrYqbQhMe7MWhrZjLFc9RBpTRYZZkrS8HH1Q3SmD5DkfpjKqtd97q1JWfqzr/0/*)#dweuu0ww","timestamp":1706047839,"active":true,"internal":false,"range":[0,1000],"next":1},{"desc":"tr([c0b9536d/86'/1'/0']tprv8fXhtVjj3vb7kgxKuiWXzcUsur44gbLbbtwxL4HKmpzkBNoMrYqbQhMe7MWhrZjLFc9RBpTRYZZkrS8HH1Q3SmD5DkfpjKqtd97q1JWfqzr/1/*)#u6uap67k","timestamp":1706047839,"active":true,"internal":true,"range":[0,1013],"next":14}]}"#.into()) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .expected_exit_code(0) + .run_and_extract_stdout(); +}