Add `list-tx` and `list-unspent` commands
This commit is contained in:
parent
b5a5a1d68d
commit
44f151c750
|
@ -2747,8 +2747,10 @@ dependencies = [
|
|||
"gumdrop",
|
||||
"prost",
|
||||
"rayon",
|
||||
"rusqlite",
|
||||
"schemer",
|
||||
"secrecy",
|
||||
"time",
|
||||
"tokio",
|
||||
"tonic",
|
||||
"tracing",
|
||||
|
|
|
@ -12,8 +12,10 @@ futures-util = "0.3"
|
|||
gumdrop = "0.8"
|
||||
prost = "0.11"
|
||||
rayon = "1.7"
|
||||
rusqlite = { version = "0.25", features = ["time"] }
|
||||
schemer = "0.2"
|
||||
secrecy = "0.8"
|
||||
time = "0.2"
|
||||
tokio = { version = "1.21.0", features = ["fs", "macros", "rt-multi-thread"] }
|
||||
tonic = { version = "0.9", features = ["gzip", "tls-webpki-roots"] }
|
||||
tracing = "0.1"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
pub(crate) mod balance;
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod list_tx;
|
||||
pub(crate) mod list_unspent;
|
||||
pub(crate) mod send;
|
||||
pub(crate) mod sync;
|
||||
pub(crate) mod upgrade;
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
use anyhow::anyhow;
|
||||
use gumdrop::Options;
|
||||
|
||||
use rusqlite::{named_params, Connection};
|
||||
use zcash_primitives::{
|
||||
consensus::BlockHeight,
|
||||
transaction::{
|
||||
components::{amount::NonNegativeAmount, Amount},
|
||||
TxId,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::data::get_db_paths;
|
||||
|
||||
// Options accepted for the `list` command
|
||||
#[derive(Debug, Options)]
|
||||
pub(crate) struct Command {}
|
||||
|
||||
impl Command {
|
||||
pub(crate) fn run(self, wallet_dir: Option<String>) -> anyhow::Result<()> {
|
||||
let (_, db_data) = get_db_paths(wallet_dir);
|
||||
|
||||
let conn = Connection::open(db_data)?;
|
||||
rusqlite::vtab::array::load_module(&conn)?;
|
||||
|
||||
let mut stmt_txs = conn.prepare(
|
||||
"SELECT mined_height,
|
||||
txid,
|
||||
expiry_height,
|
||||
fee_paid,
|
||||
sent_note_count,
|
||||
received_note_count,
|
||||
memo_count,
|
||||
block_time,
|
||||
expired_unmined
|
||||
FROM v_transactions
|
||||
WHERE account_id = :account_id",
|
||||
)?;
|
||||
|
||||
println!("Transactions:");
|
||||
for row in stmt_txs.query_and_then(
|
||||
named_params! {":account_id": 0},
|
||||
|row| -> anyhow::Result<_> {
|
||||
Transaction::from_parts(
|
||||
row.get(0)?,
|
||||
row.get(1)?,
|
||||
row.get(2)?,
|
||||
row.get(3)?,
|
||||
row.get(4)?,
|
||||
row.get(5)?,
|
||||
row.get(6)?,
|
||||
row.get(7)?,
|
||||
row.get(8)?,
|
||||
)
|
||||
},
|
||||
)? {
|
||||
let tx = row?;
|
||||
println!("");
|
||||
tx.print();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Transaction {
|
||||
mined_height: Option<BlockHeight>,
|
||||
txid: TxId,
|
||||
expiry_height: Option<BlockHeight>,
|
||||
fee_paid: Option<NonNegativeAmount>,
|
||||
sent_note_count: usize,
|
||||
received_note_count: usize,
|
||||
memo_count: usize,
|
||||
block_time: Option<i64>,
|
||||
expired_unmined: bool,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
fn from_parts(
|
||||
mined_height: Option<u32>,
|
||||
txid: Vec<u8>,
|
||||
expiry_height: Option<u32>,
|
||||
fee_paid: Option<u64>,
|
||||
sent_note_count: usize,
|
||||
received_note_count: usize,
|
||||
memo_count: usize,
|
||||
block_time: Option<i64>,
|
||||
expired_unmined: bool,
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Transaction {
|
||||
mined_height: mined_height.map(BlockHeight::from_u32),
|
||||
txid: TxId::from_bytes(txid.try_into().map_err(|_| anyhow!("Invalid TxId"))?),
|
||||
expiry_height: expiry_height.map(BlockHeight::from_u32),
|
||||
fee_paid: fee_paid
|
||||
.map(|v| NonNegativeAmount::from_u64(v).map_err(|()| anyhow!("Fee out of range")))
|
||||
.transpose()?,
|
||||
sent_note_count,
|
||||
received_note_count,
|
||||
memo_count,
|
||||
block_time,
|
||||
expired_unmined,
|
||||
})
|
||||
}
|
||||
|
||||
fn print(&self) {
|
||||
let height_to_str = |height: Option<BlockHeight>, def: &str| {
|
||||
height.map(|h| h.to_string()).unwrap_or(def.to_owned())
|
||||
};
|
||||
|
||||
println!("{}", self.txid);
|
||||
if let Some((height, block_time)) = self.mined_height.zip(self.block_time) {
|
||||
println!(
|
||||
" Mined: {} ({})",
|
||||
height,
|
||||
time::OffsetDateTime::from_unix_timestamp(block_time),
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
" {} (expiry height: {})",
|
||||
if self.expired_unmined {
|
||||
" Expired"
|
||||
} else {
|
||||
" Unmined"
|
||||
},
|
||||
height_to_str(self.expiry_height, "Unknown"),
|
||||
);
|
||||
}
|
||||
println!(
|
||||
" Fee paid: {}",
|
||||
self.fee_paid
|
||||
.map(|v| format!("{} zatoshis", u64::from(Amount::from(v))))
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("Unknown"),
|
||||
);
|
||||
println!(
|
||||
" Sent {} notes, received {} notes, {} memos",
|
||||
self.sent_note_count, self.received_note_count, self.memo_count,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
use anyhow::anyhow;
|
||||
use gumdrop::Options;
|
||||
|
||||
use zcash_client_backend::data_api::WalletRead;
|
||||
use zcash_client_sqlite::WalletDb;
|
||||
use zcash_primitives::{consensus::Parameters, zip32::AccountId};
|
||||
|
||||
use crate::{data::get_db_paths, error, MIN_CONFIRMATIONS};
|
||||
|
||||
// Options accepted for the `balance` command
|
||||
#[derive(Debug, Options)]
|
||||
pub(crate) struct Command {}
|
||||
|
||||
impl Command {
|
||||
pub(crate) fn run(
|
||||
self,
|
||||
params: impl Parameters + Copy + 'static,
|
||||
wallet_dir: Option<String>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let account = AccountId::from(0);
|
||||
let (_, db_data) = get_db_paths(wallet_dir);
|
||||
let db_data = WalletDb::for_path(db_data, params)?;
|
||||
|
||||
let (target_height, _) = db_data
|
||||
.get_target_and_anchor_heights(MIN_CONFIRMATIONS)?
|
||||
.ok_or(error::WalletErrorT::ScanRequired)
|
||||
.map_err(|e| anyhow!("{:?}", e))?;
|
||||
|
||||
let notes = db_data.get_spendable_sapling_notes(account, target_height, &[])?;
|
||||
|
||||
for note in notes {
|
||||
println!("{}: {} zatoshis", note.note_id, u64::from(note.note_value));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -43,6 +43,12 @@ enum Command {
|
|||
#[options(help = "get the balance in the wallet")]
|
||||
Balance(commands::balance::Command),
|
||||
|
||||
#[options(help = "list the transactions in the wallet")]
|
||||
ListTx(commands::list_tx::Command),
|
||||
|
||||
#[options(help = "list the unspent notes in the wallet")]
|
||||
ListUnspent(commands::list_unspent::Command),
|
||||
|
||||
#[options(help = "send funds to the given address")]
|
||||
Send(commands::send::Command),
|
||||
}
|
||||
|
@ -75,6 +81,8 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
Some(Command::Upgrade(command)) => command.run(params, opts.wallet_dir),
|
||||
Some(Command::Sync(command)) => command.run(params, opts.wallet_dir).await,
|
||||
Some(Command::Balance(command)) => command.run(params, opts.wallet_dir),
|
||||
Some(Command::ListTx(command)) => command.run(opts.wallet_dir),
|
||||
Some(Command::ListUnspent(command)) => command.run(params, opts.wallet_dir),
|
||||
Some(Command::Send(command)) => command.run(params, opts.wallet_dir).await,
|
||||
_ => Ok(()),
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue