From fbace0f036a448f6fb8ef32bd3a2d1a74bae1adc Mon Sep 17 00:00:00 2001 From: Buffrr Date: Mon, 27 Jan 2025 14:02:48 +0100 Subject: [PATCH 1/6] Clean up tx event types --- node/src/wallets.rs | 6 +-- wallet/src/lib.rs | 2 +- wallet/src/tx_event.rs | 96 +++++++++++++++++++++--------------------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/node/src/wallets.rs b/node/src/wallets.rs index 53e4f07..e8044a1 100644 --- a/node/src/wallets.rs +++ b/node/src/wallets.rs @@ -182,7 +182,7 @@ impl RpcWallet { let (_, fullspaceout) = SpacesWallet::verify_listing::(state, &listing)?; let space = fullspaceout.spaceout.space.as_ref().expect("space").name.to_string(); - let foreign_input = fullspaceout.outpoint(); + let previous_spaceout = fullspaceout.outpoint(); let tx = wallet.buy::(state, &listing, fee_rate)?; if !skip_tx_check { @@ -197,7 +197,7 @@ impl RpcWallet { let tx_record = TxRecord::new_with_events(tx, vec![TxEvent { kind: TxEventKind::Buy, space: Some(space), - foreign_input: Some(foreign_input), + previous_spaceout: Some(previous_spaceout), details: None, }]); @@ -599,7 +599,7 @@ impl RpcWallet { continue; } - if event.foreign_input.is_some_and(|input| input == space.outpoint()) { + if event.previous_spaceout.is_some_and(|input| input == space.outpoint()) { continue; } res.outbid.push(space); diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index bf93124..2eb3e1f 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -551,7 +551,7 @@ impl SpacesWallet { txid, event.kind, event.space, - event.foreign_input, + event.previous_spaceout, event.details, ).context("could not insert tx event into wallet db")?; } diff --git a/wallet/src/tx_event.rs b/wallet/src/tx_event.rs index 72667b7..fdb34f0 100644 --- a/wallet/src/tx_event.rs +++ b/wallet/src/tx_event.rs @@ -26,20 +26,20 @@ pub struct TxEvent { #[serde(skip_serializing_if = "Option::is_none")] pub space: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub foreign_input: Option, + pub previous_spaceout: Option, #[serde(skip_serializing_if = "Option::is_none", flatten)] pub details: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct BidEventDetails { - pub bid_current: Amount, - pub bid_previous: Amount, + pub current_bid: Amount, + pub previous_bid: Amount, } #[derive(Debug, Serialize, Deserialize)] pub struct TransferEventDetails { - pub to: ScriptBuf, + pub script_pubkey: ScriptBuf, } #[derive(Debug, Serialize, Deserialize)] @@ -50,21 +50,21 @@ pub struct BidoutEventDetails { #[derive(Debug, Serialize, Deserialize)] pub struct SendEventDetails { #[serde(skip_serializing_if = "Option::is_none")] - pub to_space: Option, - pub script_pubkey: ScriptBuf, + pub recipient_space: Option, + pub recipient_script_pubkey: ScriptBuf, pub amount: Amount, } #[derive(Debug, Serialize, Deserialize)] pub struct OpenEventDetails { - pub bid_initial: Amount, + pub initial_bid: Amount, } #[derive(Debug, Serialize, Deserialize)] pub struct CommitEventDetails { - pub commit_script_pubkey: protocol::Bytes, + pub script_pubkey: protocol::Bytes, /// [SpaceScriptSigningInfo] in raw format - pub commit_signing_info: protocol::Bytes, + pub signing_info: protocol::Bytes, } #[derive(Debug, Serialize, Deserialize)] @@ -100,7 +100,7 @@ impl TxEvent { txid TEXT NOT NULL, \ type TEXT NOT NULL, \ space TEXT, \ - foreign_input TEXT, \ + previous_spaceout TEXT, \ details TEXT, \ created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')) ) STRICT;", @@ -112,7 +112,7 @@ impl TxEvent { pub fn all(db_tx: &rusqlite::Transaction, txid: Txid) -> rusqlite::Result> { let stmt = db_tx.prepare(&format!( - "SELECT type, space, foreign_input, details + "SELECT type, space, previous_spaceout, details FROM {} WHERE txid = ?1", Self::TX_EVENTS_TABLE_NAME, ))?; @@ -121,7 +121,7 @@ impl TxEvent { pub fn bids(db_tx: &rusqlite::Transaction, space: String) -> rusqlite::Result> { let stmt = db_tx.prepare(&format!( - "SELECT type, space, foreign_input, details + "SELECT type, space, previous_spaceout, details FROM {} WHERE type = 'bid' AND space = ?1", Self::TX_EVENTS_TABLE_NAME, ))?; @@ -138,9 +138,9 @@ impl TxEvent { let query_placeholders = txids.iter().map(|_| "?").collect::>().join(","); let mut stmt = db_tx.prepare(&format!( - "SELECT txid, foreign_input + "SELECT txid, previous_spaceout FROM {} - WHERE foreign_input IS NOT NULL AND type = 'bid' AND txid IN ({})", + WHERE previous_spaceout IS NOT NULL AND type = 'bid' AND txid IN ({})", Self::TX_EVENTS_TABLE_NAME, query_placeholders ))?; @@ -149,8 +149,8 @@ impl TxEvent { rusqlite::params_from_iter(txids.into_iter().map(|t| Impl(t))), |row| { let txid: Impl = row.get(0)?; - let foreign_input: Option> = row.get(1)?; - Ok((txid, foreign_input.map(|x| x.0).unwrap())) + let previous_spaceout: Option> = row.get(1)?; + Ok((txid, previous_spaceout.map(|x| x.0).unwrap())) }, )?; let mut results = Vec::new(); @@ -164,7 +164,7 @@ impl TxEvent { pub fn all_bid_txs(db_tx: &rusqlite::Transaction, txid: Txid) -> rusqlite::Result> { let stmt = db_tx.prepare(&format!( - "SELECT type, space, foreign_input, details + "SELECT type, space, previous_spaceout, details FROM {} WHERE type = 'bid' AND txid = ?1", Self::TX_EVENTS_TABLE_NAME, ))?; @@ -174,7 +174,7 @@ impl TxEvent { pub fn get_signing_info(db_tx: &rusqlite::Transaction, txid: Txid, script_pubkey: &ScriptBuf) -> rusqlite::Result> { let stmt = db_tx.prepare(&format!( - "SELECT type, space, foreign_input, details + "SELECT type, space, previous_spaceout, details FROM {} WHERE type = 'commit' AND txid = ?1", Self::TX_EVENTS_TABLE_NAME, ))?; @@ -185,8 +185,8 @@ impl TxEvent { let details = result.details.expect("signing details in tx event"); let details: CommitEventDetails = serde_json::from_value(details) .expect("signing details"); - if details.commit_script_pubkey.as_slice() == script_pubkey.as_bytes() { - let raw = details.commit_signing_info.to_vec(); + if details.script_pubkey.as_slice() == script_pubkey.as_bytes() { + let raw = details.signing_info.to_vec(); let info = SpaceScriptSigningInfo::from_slice(raw.as_slice()).expect("valid signing info"); return Ok(Some(info)) @@ -200,7 +200,7 @@ impl TxEvent { db_tx: &rusqlite::Transaction, ) -> rusqlite::Result> { let query = format!( - "SELECT txid, type, space, foreign_input, details + "SELECT txid, type, space, previous_spaceout, details FROM {table} WHERE id IN ( SELECT MAX(id) @@ -221,7 +221,7 @@ impl TxEvent { TxEvent { kind: row.get("type")?, space: row.get("space")?, - foreign_input: row.get::<_, Option>>("foreign_input")?.map(|x| x.0), + previous_spaceout: row.get::<_, Option>>("previous_spaceout")?.map(|x| x.0), details: row.get::<_, Option>>("details")?.map(|x| x.0), }, )) @@ -243,18 +243,18 @@ impl TxEvent { Ok(( row.get::<_, TxEventKind>("type")?, row.get::<_, Option>("space")?, - row.get::<_, Option>>("foreign_input")?, + row.get::<_, Option>>("previous_spaceout")?, row.get::<_, Option>>("details")?, )) })?; let mut events = Vec::new(); for row in row_iter { - let (event_type, space, foreign_input, details) = row?; + let (event_type, space, previous_spaceout, details) = row?; events.push(TxEvent { kind: event_type, space, - foreign_input: foreign_input.map(|x| x.0), + previous_spaceout: previous_spaceout.map(|x| x.0), details: details.map(|x| x.0), }) } @@ -267,11 +267,11 @@ impl TxEvent { txid: Txid, kind: TxEventKind, space: Option, - foreign_input: Option, + previous_spaceout: Option, details: Option, ) -> rusqlite::Result { let query = format!( - "INSERT INTO {} (txid, type, space, foreign_input, details) + "INSERT INTO {} (txid, type, space, previous_spaceout, details) VALUES (?1, ?2, ?3, ?4, ?5)", Self::TX_EVENTS_TABLE_NAME, ); @@ -282,7 +282,7 @@ impl TxEvent { txid.to_string(), kind, space, - foreign_input.map(|b| b.to_string()), + previous_spaceout.map(|b| b.to_string()), details.map(|d| d.to_string()) ], )?; @@ -309,7 +309,7 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::FeeBump, space: None, - foreign_input: None, + previous_spaceout: None, details: None, }); } @@ -318,8 +318,8 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::Transfer, space: Some(space), - foreign_input: None, - details: Some(serde_json::to_value(TransferEventDetails { to }).expect("json value")), + previous_spaceout: None, + details: Some(serde_json::to_value(TransferEventDetails { script_pubkey: to }).expect("json value")), }); } @@ -327,8 +327,8 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::Renew, space: Some(space), - foreign_input: None, - details: Some(serde_json::to_value(TransferEventDetails { to }).expect("json value")), + previous_spaceout: None, + details: Some(serde_json::to_value(TransferEventDetails { script_pubkey: to }).expect("json value")), }); } @@ -336,7 +336,7 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::Bidout, space: None, - foreign_input: None, + previous_spaceout: None, details: Some(serde_json::to_value(BidoutEventDetails { count }).expect("json value")), }); } @@ -350,11 +350,11 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::Send, space: None, - foreign_input: None, + previous_spaceout: None, details: Some( serde_json::to_value(SendEventDetails { - to_space, - script_pubkey: resolved_address, + recipient_space: to_space, + recipient_script_pubkey: resolved_address, amount, }) .expect("json value"), @@ -372,11 +372,11 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::Commit, space: Some(space), - foreign_input: None, + previous_spaceout: None, details: Some( serde_json::to_value(CommitEventDetails { - commit_script_pubkey: protocol::Bytes::new(reveal_address.to_bytes()), - commit_signing_info: protocol::Bytes::new(signing_info), + script_pubkey: protocol::Bytes::new(reveal_address.to_bytes()), + signing_info: protocol::Bytes::new(signing_info), }) .expect("json value"), ), @@ -387,10 +387,10 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::Open, space: Some(space), - foreign_input: None, + previous_spaceout: None, details: Some( serde_json::to_value(OpenEventDetails { - bid_initial: initial_bid, + initial_bid: initial_bid, }) .expect("json value"), ), @@ -402,7 +402,7 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::Script, space: Some(space), - foreign_input: None, + previous_spaceout: None, details: Some( serde_json::to_value(ExecuteEventDetails { n: reveal_input_index, @@ -418,12 +418,12 @@ impl TxRecord { Covenant::Bid { total_burned, .. } => total_burned, _ => panic!("expected a bid"), }; - let foreign_input = match wallet.is_mine(previous.spaceout.script_pubkey.clone()) { + let previous_spaceout = match wallet.is_mine(previous.spaceout.script_pubkey.clone()) { false => Some(previous.outpoint()), true => None }; - if foreign_input.is_some() { + if previous_spaceout.is_some() { self.txouts.push((previous.outpoint(), TxOut { value: previous.spaceout.value, script_pubkey: previous.spaceout.script_pubkey.clone(), @@ -433,11 +433,11 @@ impl TxRecord { self.events.push(TxEvent { kind: TxEventKind::Bid, space: Some(space.name.to_string()), - foreign_input, + previous_spaceout: previous_spaceout, details: Some( serde_json::to_value(BidEventDetails { - bid_current: amount, - bid_previous: previous_bid, + current_bid: amount, + previous_bid: previous_bid, }) .expect("json value"), ), From f6db735b3d5304b33aa481c58d1e8d613173c8be Mon Sep 17 00:00:00 2001 From: Buffrr Date: Tue, 28 Jan 2025 15:02:34 +0100 Subject: [PATCH 2/6] Add new simplified output format to space-cli --- Cargo.lock | 114 +++++++++-- node/Cargo.toml | 3 +- node/src/bin/space-cli.rs | 105 ++++------ node/src/format.rs | 401 ++++++++++++++++++++++++++++++++++++++ node/src/lib.rs | 1 + 5 files changed, 549 insertions(+), 75 deletions(-) create mode 100644 node/src/format.rs diff --git a/Cargo.lock b/Cargo.lock index 54fead4..3c068ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -429,6 +429,12 @@ version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "byteorder" version = "1.5.0" @@ -525,7 +531,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -540,6 +546,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "colored" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -833,7 +848,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -1303,7 +1318,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -1535,6 +1550,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "papergrid" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b0f8def1f117e13c895f3eda65a7b5650688da29d6ad04635f61bc7b92eebd" +dependencies = [ + "bytecount", + "fnv", + "unicode-width", +] + [[package]] name = "parking_lot" version = "0.12.2" @@ -1604,7 +1630,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -1676,6 +1702,28 @@ dependencies = [ "toml_edit 0.21.1", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "proc-macro2" version = "1.0.79" @@ -2127,7 +2175,7 @@ checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2267,6 +2315,7 @@ dependencies = [ "base64 0.22.1", "bincode", "clap", + "colored", "ctrlc", "directories", "env_logger", @@ -2280,6 +2329,7 @@ dependencies = [ "serde", "serde_json", "spacedb", + "tabled", "testutil", "threadpool", "tokio", @@ -2316,6 +2366,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.52" @@ -2333,6 +2394,29 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "tabled" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6709222f3973137427ce50559cd564dc187a95b9cfe01613d2f4e93610e510a" +dependencies = [ + "papergrid", + "tabled_derive", +] + +[[package]] +name = "tabled_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931be476627d4c54070a1f3a9739ccbfec9b36b39815106a20cce2243bbcefe1" +dependencies = [ + "heck 0.4.1", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "tar" version = "0.4.41" @@ -2391,7 +2475,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2463,7 +2547,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2607,7 +2691,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] @@ -2652,6 +2736,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "untrusted" version = "0.9.0" @@ -2758,7 +2848,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.52", "wasm-bindgen-shared", ] @@ -2792,7 +2882,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3028,7 +3118,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.52", ] [[package]] diff --git a/node/Cargo.toml b/node/Cargo.toml index 8641734..ca12be9 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -36,7 +36,8 @@ base64 = "0.22.1" futures = "0.3.30" reqwest = { version = "0.12.5", default-features = false, features = ["json", "blocking", "rustls-tls"] } threadpool = "1.8.1" - +tabled = "0.17.0" +colored = "3.0.0" [dev-dependencies] assert_cmd = "2.0.16" predicates = "3.1.2" diff --git a/node/src/bin/space-cli.rs b/node/src/bin/space-cli.rs index 66b58f3..4610a45 100644 --- a/node/src/bin/space-cli.rs +++ b/node/src/bin/space-cli.rs @@ -3,6 +3,7 @@ extern crate core; use std::{fs, path::PathBuf, str::FromStr}; use clap::{Parser, Subcommand}; +use colored::{Color, Colorize}; use jsonrpsee::{ core::{client::Error, ClientError}, http_client::{HttpClient, HttpClientBuilder}, @@ -12,7 +13,6 @@ use protocol::{ hasher::KeyHasher, slabel::SLabel, }; -use serde::{Deserialize, Serialize}; use spaced::{ config::{default_spaces_rpc_port, ExtendedNetwork}, rpc::{ @@ -22,7 +22,9 @@ use spaced::{ store::Sha256, wallets::AddressKind, }; +use spaced::format::{print_error_rpc_response, print_list_bidouts, print_list_spaces_response, print_list_transactions, print_list_unspent, print_server_info, print_wallet_balance_response, print_wallet_info, print_wallet_response, Format}; use spaced::rpc::SignedMessage; +use spaced::wallets::WalletResponse; use wallet::bitcoin::secp256k1::schnorr::Signature; use wallet::export::WalletExport; use wallet::Listing; @@ -33,6 +35,8 @@ pub struct Args { /// Bitcoin network to use #[arg(long, env = "SPACED_CHAIN", default_value = "mainnet")] chain: ExtendedNetwork, + #[arg(long, default_value = "text")] + output_format: Format, /// Spaced RPC URL [default: based on specified chain] #[arg(long)] spaced_rpc_url: Option, @@ -187,6 +191,14 @@ enum Commands { #[arg(long, short)] fee_rate: u64, }, + /// List a space you own for sale + #[command(name = "sell")] + Sell { + /// The space to sell + space: String, + /// Amount in satoshis + price: u64, + }, /// Buy a space from the specified listing #[command(name = "buy")] Buy { @@ -225,14 +237,7 @@ enum Commands { #[arg(long)] signature: String, }, - /// List a space you own for sale - #[command(name = "sell")] - Sell { - /// The space to sell - space: String, - /// Amount in satoshis - price: u64, - }, + /// Verify a listing #[command(name = "verifylisting")] VerifyListing { @@ -263,8 +268,7 @@ enum Commands { #[arg(default_value = "0")] target_interval: usize, }, - /// Associate the specified data with a given space (not recommended use Fabric instead) - /// If for whatever reason it's not possible to use other protocols, then you may use this. + /// Associate on-chain record data with a space as a fallback to P2P options like Fabric. #[command(name = "setrawfallback")] SetRawFallback { /// Space name @@ -302,13 +306,6 @@ enum Commands { /// compatible with most bitcoin wallets #[command(name = "getnewaddress")] GetCoinAddress, - /// Force spend an output owned by wallet (for testing only) - #[command(name = "forcespend")] - ForceSpend { - outpoint: OutPoint, - #[arg(long, short)] - fee_rate: u64, - }, /// DNS encodes the space and calculates the SHA-256 hash #[command(name = "hashspace")] HashSpace { space: String }, @@ -316,6 +313,7 @@ enum Commands { struct SpaceCli { wallet: String, + format: Format, dust: Option, force: bool, skip_tx_check: bool, @@ -335,6 +333,7 @@ impl SpaceCli { Ok(( Self { wallet: args.wallet.clone(), + format: args.output_format, dust: args.dust.map(|d| Amount::from_sat(d)), force: args.force, skip_tx_check: args.skip_tx_check, @@ -373,10 +372,8 @@ impl SpaceCli { ) .await?; - println!( - "{}", - serde_json::to_string_pretty(&result).expect("serialize") - ); + + print_wallet_response(self.network.fallback_network(), result, self.format); Ok(()) } } @@ -390,11 +387,7 @@ fn normalize_space(space: &str) -> String { } } -#[derive(Debug, Clone, Serialize, Deserialize)] -struct RpcError { - code: i32, - message: String, -} + #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -405,14 +398,7 @@ async fn main() -> anyhow::Result<()> { Ok(_) => {} Err(error) => match ClientError::from(error) { Error::Call(rpc) => { - let error = RpcError { - code: rpc.code(), - message: rpc.message().to_string(), - }; - println!( - "{}", - serde_json::to_string_pretty(&error).expect("serialize") - ); + print_error_rpc_response(rpc.code(), rpc.message().to_string(), cli.format); } Error::Transport(err) => { println!( @@ -506,11 +492,11 @@ async fn handle_commands( } Commands::GetWalletInfo => { let result = cli.client.wallet_get_info(&cli.wallet).await?; - println!("{}", serde_json::to_string_pretty(&result).expect("result")); + print_wallet_info(result, cli.format); } Commands::GetServerInfo => { let result = cli.client.get_server_info().await?; - println!("{}", serde_json::to_string_pretty(&result).expect("result")); + print_server_info(result, cli.format); } Commands::Open { ref space, @@ -640,27 +626,28 @@ async fn handle_commands( .await?; } Commands::ListUnspent => { - let spaces = cli.client.wallet_list_unspent(&cli.wallet).await?; - println!("{}", serde_json::to_string_pretty(&spaces)?); + let utxos = cli.client.wallet_list_unspent(&cli.wallet).await?; + print_list_unspent(utxos, cli.format); } Commands::ListBidOuts => { - let spaces = cli.client.wallet_list_bidouts(&cli.wallet).await?; - println!("{}", serde_json::to_string_pretty(&spaces)?); + let bidouts = cli.client.wallet_list_bidouts(&cli.wallet).await?; + print_list_bidouts(bidouts, cli.format); } Commands::ListTransactions { count, skip } => { let txs = cli .client .wallet_list_transactions(&cli.wallet, count, skip) .await?; - println!("{}", serde_json::to_string_pretty(&txs)?); + print_list_transactions(txs, cli.format); } Commands::ListSpaces => { + let tip = cli.client.get_server_info().await?; let spaces = cli.client.wallet_list_spaces(&cli.wallet).await?; - println!("{}", serde_json::to_string_pretty(&spaces)?); + print_list_spaces_response(tip.tip.height, spaces, cli.format); } Commands::Balance => { let balance = cli.client.wallet_get_balance(&cli.wallet).await?; - println!("{}", serde_json::to_string_pretty(&balance)?); + print_wallet_balance_response(balance, cli.format); } Commands::GetCoinAddress => { let response = cli @@ -682,18 +669,10 @@ async fn handle_commands( .client .wallet_bump_fee(&cli.wallet, txid, fee_rate, cli.skip_tx_check) .await?; - println!("{}", serde_json::to_string_pretty(&response)?); - } - Commands::ForceSpend { outpoint, fee_rate } => { - let result = cli - .client - .wallet_force_spend( - &cli.wallet, - outpoint, - FeeRate::from_sat_per_vb(fee_rate).unwrap(), - ) - .await?; - println!("{}", serde_json::to_string_pretty(&result).expect("result")); + print_wallet_response( + cli.network.fallback_network(), WalletResponse { + result: response, + }, cli.format); } Commands::HashSpace { space } => { println!( @@ -718,7 +697,10 @@ async fn handle_commands( fee_rate.map(|rate| FeeRate::from_sat_per_vb(rate).expect("valid fee rate")), cli.skip_tx_check, ).await?; - println!("{}", serde_json::to_string_pretty(&result).expect("result")); + print_wallet_response(cli.network.fallback_network(), WalletResponse { + result: vec![result], + }, cli.format + ); } Commands::Sell { space, price, } => { let result = cli @@ -740,10 +722,9 @@ async fn handle_commands( .map_err(|_| ClientError::Custom("Invalid signature".to_string()))?, }; - let result = cli - .client + cli.client .verify_listing(listing).await?; - println!("{}", serde_json::to_string_pretty(&result).expect("result")); + println!("{} Listing verified", "✓".color(Color::Green)); } Commands::SignMessage { mut space, message } => { space = normalize_space(&space); @@ -757,12 +738,12 @@ async fn handle_commands( .map_err(|_| ClientError::Custom("Invalid signature".to_string()))?; let signature = Signature::from_slice(raw.as_slice()) .map_err(|_| ClientError::Custom("Invalid signature".to_string()))?; - let result = cli.client.verify_message(SignedMessage { + cli.client.verify_message(SignedMessage { space, message: protocol::Bytes::new(message.as_bytes().to_vec()), signature, }).await?; - println!("{}", serde_json::to_string_pretty(&result).expect("result")); + println!("{} Message verified", "✓".color(Color::Green)); } } diff --git a/node/src/format.rs b/node/src/format.rs new file mode 100644 index 0000000..886ae3d --- /dev/null +++ b/node/src/format.rs @@ -0,0 +1,401 @@ +use clap::ValueEnum; +use colored::{Color, Colorize}; +use jsonrpsee::core::Serialize; +use serde::Deserialize; +use tabled::{Table, Tabled}; +use protocol::bitcoin::{Amount, Network, OutPoint}; +use protocol::{Covenant}; +use wallet::address::SpaceAddress; +use wallet::{Balance, DoubleUtxo, WalletInfo, WalletOutput}; +use wallet::bdk_wallet::KeychainKind; +use wallet::bitcoin::{Address, Txid}; +use wallet::tx_event::{BidEventDetails, BidoutEventDetails, OpenEventDetails, SendEventDetails, TransferEventDetails, TxEventKind}; +use crate::rpc::ServerInfo; +use crate::wallets::{ListSpacesResponse, TxInfo, TxResponse, WalletResponse}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, ValueEnum, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Format { + Text, + Json, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct FormatRpcError { + code: i32, + message: String, +} + +#[derive(Tabled)] +#[tabled(rename_all = "UPPERCASE")] +struct WinningSpaces { + space: String, + bid: u64, + #[tabled(rename = "CLAIM AT")] + claim_at: String, + #[tabled(rename = "DAYS LEFT")] + days_left: String, +} + +#[derive(Tabled)] +#[tabled(rename_all = "UPPERCASE")] +struct OutbidSpaces { + space: String, + #[tabled(rename = "LAST CONFIRMED BID")] + last_confirmed_bid: u64, + #[tabled(rename = "DAYS LEFT")] + days_left: String, +} + +#[derive(Tabled)] +#[tabled(rename_all = "UPPERCASE")] +struct RegisteredSpaces { + space: String, + #[tabled(rename = "EXPIRE AT")] + expire_at: usize, + #[tabled(rename = "DAYS LEFT")] + days_left: String, + #[tabled(rename = "UTXO")] + utxo: OutPoint +} + +#[derive(Tabled)] +#[tabled(rename_all = "UPPERCASE")] +struct UnspentOutput { + outpoint: OutPoint, + value: Amount, + confirmed: bool, + external: bool, +} + +#[derive(Tabled)] +#[tabled(rename_all = "UPPERCASE")] +struct Bidout { + txid: Txid, + vout_1: u32, + vout_2: u32, + confirmed: bool, +} + +fn format_days_left(current_block: u32, claim_height: Option) -> String { + match claim_height { + Some(height) => { + let blocks_remaining = height as isize - current_block as isize; + let days_remaining = (blocks_remaining as f64 / 6.0 / 24.0).max(0.0); + format!("{:.2}", days_remaining) + } + None => "--".to_string(), + } +} + +pub fn print_list_bidouts(bidouts: Vec, format: Format) { + match format { + Format::Text => { + let all : Vec<_> = bidouts.into_iter().map(|out| Bidout { + txid: out.spend.outpoint.txid, + vout_1: out.spend.outpoint.vout, + vout_2: out.auction.outpoint.vout, + confirmed: out.confirmed, + }).collect(); + println!("{}", ascii_table(all)); + } + Format::Json => { + println!("{}", serde_json::to_string_pretty(&bidouts).unwrap()); + + } + } +} + +pub fn print_list_transactions(txs: Vec, format: Format) { + match format { + Format::Text => { + println!("{}", ascii_table(txs)); + } + Format::Json => { + println!("{}", serde_json::to_string_pretty(&txs).unwrap()); + } + } +} + + + +pub fn print_list_unspent(utxos: Vec, format: Format) { + match format { + Format::Text => { + let utxos : Vec<_> = utxos.iter().map(|utxo| UnspentOutput { + outpoint: utxo.output.outpoint, + confirmed: utxo.output.chain_position.is_confirmed(), + value: utxo.output.txout.value, + external: utxo.output.keychain == KeychainKind::External, + }).collect(); + println!("{}", ascii_table(utxos)) + } + Format::Json => { + println!("{}", serde_json::to_string_pretty(&utxos).unwrap()); + } + } +} + +pub fn print_server_info(info: ServerInfo, format: Format) { + match format { + Format::Text => { + println!("CHAIN: {}", info.chain); + println!(" Height {}", info.tip.height); + println!(" Hash {}", info.tip.hash); + } + Format::Json => { + println!("{}", serde_json::to_string_pretty(&info).unwrap()); + } + } +} + +pub fn print_wallet_info(info: WalletInfo, format: Format) { + match format { + Format::Text => { + println!("WALLET: {}", info.label); + println!(" Tip {}\n Birthday {}", + info.tip, + info.start_block + ); + + println!(" Public descriptors"); + for desc in info.descriptors { + println!(" {}", desc.descriptor); + } + println!(); + } + Format::Json => { + println!("{}", serde_json::to_string_pretty(&info).unwrap()); + } + } +} + +fn ascii_table(iter: I) -> String +where + I: IntoIterator, + T: Tabled, +{ + Table::new(iter) + .with(tabled::settings::Style::modern_rounded()) + .to_string() +} + +pub fn print_wallet_balance_response(balance: Balance, format: Format) { + match format { + Format::Text => { + println!("Balance: {}", balance.balance.to_sat()); + println!(" Confirmed {:>14}", balance.details.balance.confirmed.to_sat()); + println!(" Trusted pending {:>14}", balance.details.balance.trusted_pending.to_sat()); + println!(" Untrusted pending {:>14}", balance.details.balance.untrusted_pending.to_sat()); + println!(" Dust & in-auction {:>14}", balance.details.dust.to_sat()); + } + Format::Json => { + println!("{}", serde_json::to_string_pretty(&balance).unwrap()); + } + } +} + +pub fn print_list_spaces_response(current_block: u32, response: ListSpacesResponse, format: Format) { + match format { + Format::Text => { + let mut outbids = Vec::new(); + let mut winnings = Vec::new(); + let mut owned = Vec::new(); + for res in response.outbid { + let space = res.spaceout.space.as_ref().expect("space"); + let mut outbid = OutbidSpaces { + space: space.name.to_string(), + last_confirmed_bid: 0, + days_left: "".to_string(), + }; + match space.covenant { + Covenant::Bid { total_burned, claim_height, .. } => { + outbid.last_confirmed_bid = total_burned.to_sat(); + outbid.days_left = format_days_left(current_block, claim_height); + } + _ => {} + } + outbids.push(outbid); + } + + for res in response.winning { + let space = res.spaceout.space.as_ref().expect("space"); + let mut winning = WinningSpaces { + space: space.name.to_string(), + bid: 0, + days_left: "--".to_string(), + claim_at: "--".to_string(), + }; + match space.covenant { + Covenant::Bid { total_burned, claim_height, .. } => { + winning.bid = total_burned.to_sat(); + winning.claim_at = claim_height.map(|h| h.to_string()).unwrap_or("--".to_string()); + winning.days_left = format_days_left(current_block, claim_height); + if winning.days_left == "0.00" { + winning.days_left = "Ready to claim".to_string(); + } + } + _ => {} + } + winnings.push(winning); + } + for res in response.owned { + let space = res.spaceout.space.as_ref().expect("space"); + let mut registered = RegisteredSpaces { + space: space.name.to_string(), + expire_at: 0, + days_left: "--".to_string(), + utxo: res.outpoint(), + }; + match &space.covenant { + Covenant::Transfer { expire_height, .. } => { + registered.expire_at = *expire_height as _; + registered.days_left = format_days_left(current_block, Some(*expire_height)); + } + _ => {} + } + owned.push(registered); + } + + + if !outbids.is_empty() { + println!("⚠️ OUTBID ({} spaces): ", outbids.len().to_string().bold()); + let table = ascii_table(outbids); + println!("{}", table); + } + + if !winnings.is_empty() { + println!("{} WINNING ({} spaces):","✓".color(Color::Green), winnings.len().to_string().bold()); + let table = ascii_table(winnings); + println!("{}", table); + } + + if !owned.is_empty() { + println!("{} ({} spaces): ", "🔑 OWNED", owned.len().to_string().bold()); + let table = ascii_table(owned); + println!("{}", table); + } + } + Format::Json => println!("{}", serde_json::to_string_pretty(&response).unwrap()), + } +} +pub fn print_wallet_response(network: Network, response: WalletResponse, format: Format) { + match format { + Format::Text => print_wallet_response_text(network, response), + Format::Json => println!("{}", serde_json::to_string_pretty(&response).unwrap()), + } +} + +pub fn print_wallet_response_text(network: Network, response: WalletResponse) { + let mut main_txs = Vec::new(); + let mut secondary_txs = Vec::new(); + + for tx in response.result { + if tx.events.iter().any(|event| match event.kind { + TxEventKind::Open | TxEventKind::Bid | TxEventKind::Register | + TxEventKind::Transfer | TxEventKind::Send | TxEventKind::Renew | TxEventKind::Buy + => true, + _ => false, + }) { + main_txs.push(tx); + } else { secondary_txs.push(tx); } + } + + for tx in main_txs { + print_tx_response(network, tx); + } + + for tx in secondary_txs { + print_tx_response(network, tx); + } +} + +pub fn print_error_rpc_response(code: i32, message: String, format: Format) { + match format { + Format::Text => { + println!("⚠️ {}", message); + } + Format::Json => { + let error = FormatRpcError { + code, + message: message.to_string(), + }; + println!("{}", serde_json::to_string_pretty(&error).unwrap()); + } + } +} + +fn print_tx_response(network: Network, response: TxResponse) { + match response.error { + None => { + println!("{} Transaction {}","✓".color(Color::Green), response.txid); + } + Some(errors) => { + println!("⚠️ Transaction failed to broadcast"); + for (key, value) in errors.iter() { + println!("{}: {}", key, value); + } + + println!("\nAttempted actions:") + } + } + + for event in response.events { + println!(" - {} {}", capitalize(event.kind.to_string()), event.space.unwrap_or("".to_string())); + + match event.kind { + TxEventKind::Open => { + let open_details: OpenEventDetails = serde_json::from_value( + event.details.expect("details")) + .expect("deserialize open event"); + + println!(" Initial bid: {}", open_details.initial_bid.to_sat()); + } + TxEventKind::Bid => { + let bid_details: BidEventDetails = serde_json::from_value( + event.details.expect("details")) + .expect("deserialize bid event"); + println!(" New bid: {} (previous {})", + bid_details.current_bid.to_sat(), + bid_details.previous_bid.to_sat() + ); + } + TxEventKind::Send => { + let send_details: SendEventDetails = serde_json::from_value( + event.details.expect("details")) + .expect("deserialize send event"); + + let addr = Address::from_script(send_details.recipient_script_pubkey.as_script(), network) + .expect("valid address"); + + println!(" Amount: {}", send_details.amount.to_sat()); + println!(" Recipient: {}", addr); + } + TxEventKind::Transfer => { + let transfer_details: TransferEventDetails = serde_json::from_value( + event.details.expect("details")) + .expect("deserialize transfer event"); + + let addr = SpaceAddress( + Address::from_script(transfer_details.script_pubkey.as_script(), network) + .expect("valid address") + ); + println!(" Recipient: {}", addr); + } + TxEventKind::Bidout => { + let bidout: BidoutEventDetails = serde_json::from_value( + event.details.expect("details")) + .expect("deserialize bidout event"); + println!(" Count: {}", bidout.count); + } + _ => {} + } + } +} + +fn capitalize(mut s: String) -> String { + if let Some(first) = s.get_mut(0..1) { + first.make_ascii_uppercase(); + } + s +} diff --git a/node/src/lib.rs b/node/src/lib.rs index 69b0b86..d528d5d 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -14,6 +14,7 @@ pub mod source; pub mod store; pub mod sync; pub mod wallets; +pub mod format; fn std_wait(mut predicate: F, wait: Duration) where From 525b43605d95be7457fbdc8d2e3fda9939ead440 Mon Sep 17 00:00:00 2001 From: Buffrr Date: Tue, 28 Jan 2025 15:13:42 +0100 Subject: [PATCH 3/6] Fix rpc retry shutdown during SIGINT & implement tabled for TxInfo --- node/src/sync.rs | 6 ++++-- node/src/wallets.rs | 34 +++++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/node/src/sync.rs b/node/src/sync.rs index b4145d2..d2fd4bf 100644 --- a/node/src/sync.rs +++ b/node/src/sync.rs @@ -171,7 +171,8 @@ impl Spaced { return Err(e); } warn!("Restore: {} - retrying in 1s", e); - std_wait(|| shutdown_signal.try_recv().is_ok(), Duration::from_secs(1)); + let mut wait_recv = shutdown.subscribe(); + std_wait(|| wait_recv.try_recv().is_ok(), Duration::from_secs(1)); } // Even if we couldn't restore just attempt to re-sync let new_tip = self.chain.state.tip.read().expect("read").clone(); @@ -179,7 +180,8 @@ impl Spaced { } BlockEvent::Error(e) => { warn!("Fetcher: {} - retrying in 1s", e); - std_wait(|| shutdown_signal.try_recv().is_ok(), Duration::from_secs(1)); + let mut wait_recv = shutdown.subscribe(); + std_wait(|| wait_recv.try_recv().is_ok(), Duration::from_secs(1)); // Even if we couldn't restore just attempt to re-sync let new_tip = self.chain.state.tip.read().expect("read").clone(); fetcher.restart(new_tip, &receiver); diff --git a/node/src/wallets.rs b/node/src/wallets.rs index e8044a1..8d651c2 100644 --- a/node/src/wallets.rs +++ b/node/src/wallets.rs @@ -10,6 +10,7 @@ use log::{info, warn}; use protocol::{bitcoin::Txid, constants::ChainAnchor, hasher::{KeyHasher, SpaceKey}, script::SpaceScript, slabel::SLabel, FullSpaceOut, SpaceOut}; use serde::{Deserialize, Serialize}; use serde_json::json; +use tabled::Tabled; use tokio::{ select, sync::{broadcast, mpsc, mpsc::Receiver, oneshot}, @@ -45,16 +46,37 @@ pub struct ListSpacesResponse { pub owned: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] + +#[derive(Tabled, Debug, Clone, Serialize, Deserialize)] +#[tabled(rename_all = "UPPERCASE")] pub struct TxInfo { pub txid: Txid, pub confirmed: bool, pub sent: Amount, pub received: Amount, + #[tabled(display_with = "display_fee")] pub fee: Option, + #[tabled(rename = "DETAILS", display_with = "display_events")] pub events: Vec, } +fn display_fee(fee: &Option) -> String { + match fee { + None => "--".to_string(), + Some(fee) => fee.to_string() + } +} + +fn display_events(events: &Vec) -> String { + events + .iter() + .map(|e| + format!("{} {}", + e.kind, + e.space.as_ref().map(|s| s.clone()).unwrap_or("".to_string()))) + .collect::>().join("\n") +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WalletResponse { pub result: Vec, @@ -429,7 +451,7 @@ impl RpcWallet { mut state: LiveSnapshot, mut wallet: SpacesWallet, mut commands: Receiver, - mut shutdown: broadcast::Receiver<()>, + shutdown: broadcast::Sender<()>, num_workers: usize, ) -> anyhow::Result<()> { let (fetcher, receiver) = BlockFetcher::new(source.clone(), num_workers); @@ -442,11 +464,12 @@ impl RpcWallet { } }; + let mut shutdown_recv = shutdown.subscribe(); fetcher.start(wallet_tip); let mut synced_at_least_once = false; let mut last_mempool_check = Instant::now(); loop { - if shutdown.try_recv().is_ok() { + if shutdown_recv.try_recv().is_ok() { info!("Shutting down wallet sync"); break; } @@ -542,7 +565,8 @@ impl RpcWallet { } BlockEvent::Error(e) => { warn!("Fetcher: {} - retrying in 1s", e); - std_wait(|| shutdown.try_recv().is_ok(), Duration::from_secs(1)); + let mut wait_recv = shutdown.subscribe(); + std_wait(|| wait_recv.try_recv().is_ok(), Duration::from_secs(1)); fetcher.restart(wallet_tip, &receiver); } } @@ -1091,7 +1115,7 @@ impl RpcWallet { let wallet_name = loaded.export.label.clone(); let wallet_chain = store.clone(); let rpc = rpc.clone(); - let wallet_shutdown = shutdown.subscribe(); + let wallet_shutdown = shutdown.clone(); let (tx, rx) = oneshot::channel(); std::thread::spawn(move || { From 777756fe5bd531774b45532535eecc4d45c25e4a Mon Sep 17 00:00:00 2001 From: Buffrr Date: Tue, 28 Jan 2025 15:36:30 +0100 Subject: [PATCH 4/6] Increase mempool check interval --- node/src/wallets.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/wallets.rs b/node/src/wallets.rs index 8d651c2..d6c7191 100644 --- a/node/src/wallets.rs +++ b/node/src/wallets.rs @@ -25,8 +25,8 @@ use crate::{checker::TxChecker, config::ExtendedNetwork, node::BlockSource, rpc: }, std_wait, store::{ChainState, LiveSnapshot, Sha256}}; use crate::rpc::SignedMessage; -const MEMPOOL_CHECK_INTERVAL: Duration = Duration::from_millis( - if cfg!(debug_assertions) { 500 } else { 10_000 } +const MEMPOOL_CHECK_INTERVAL: Duration = Duration::from_secs( + if cfg!(debug_assertions) { 1 } else { 5 * 60 } ); #[derive(Debug, Clone, Serialize, Deserialize)] From 6e77eef5ea03ad2893fa6da00785c350bc044658 Mon Sep 17 00:00:00 2001 From: Buffrr Date: Tue, 28 Jan 2025 16:00:22 +0100 Subject: [PATCH 5/6] Normalize space name with sell command --- node/src/bin/space-cli.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/src/bin/space-cli.rs b/node/src/bin/space-cli.rs index 4610a45..2287ae1 100644 --- a/node/src/bin/space-cli.rs +++ b/node/src/bin/space-cli.rs @@ -702,7 +702,8 @@ async fn handle_commands( }, cli.format ); } - Commands::Sell { space, price, } => { + Commands::Sell { mut space, price, } => { + space = normalize_space(&space); let result = cli .client .wallet_sell( From 74ed87457a683373f38a25709272bba40ddc589b Mon Sep 17 00:00:00 2001 From: Buffrr Date: Tue, 28 Jan 2025 17:30:21 +0100 Subject: [PATCH 6/6] Bump version --- Cargo.lock | 6 +++--- node/Cargo.toml | 2 +- protocol/Cargo.toml | 2 +- wallet/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c068ca..572a243 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1735,7 +1735,7 @@ dependencies = [ [[package]] name = "protocol" -version = "0.0.5" +version = "0.0.6" dependencies = [ "bincode", "bitcoin", @@ -2308,7 +2308,7 @@ dependencies = [ [[package]] name = "spaced" -version = "0.0.5" +version = "0.0.6" dependencies = [ "anyhow", "assert_cmd", @@ -2794,7 +2794,7 @@ dependencies = [ [[package]] name = "wallet" -version = "0.0.5" +version = "0.0.6" dependencies = [ "anyhow", "bdk_wallet", diff --git a/node/Cargo.toml b/node/Cargo.toml index ca12be9..54f3783 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spaced" -version = "0.0.5" +version = "0.0.6" edition = "2021" diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index afba213..9b8dbf6 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "protocol" -version = "0.0.5" +version = "0.0.6" edition = "2021" [dependencies] diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 63a94cf..61bec4e 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wallet" -version = "0.0.5" +version = "0.0.6" edition = "2021" [dependencies]