Skip to content

Commit 99f4a35

Browse files
committed
Add gettransaction and z_viewtransaction
Closes #38.
1 parent 77e7f9e commit 99f4a35

File tree

6 files changed

+759
-1
lines changed

6 files changed

+759
-1
lines changed

zallet/src/components/json_rpc.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
use abscissa_core::tracing::{info, warn};
1010
use jsonrpsee::tracing::Instrument;
11+
use zcash_protocol::value::{ZatBalance, Zatoshis, COIN};
1112

1213
use crate::{
1314
config::ZalletConfig,
@@ -17,11 +18,17 @@ use crate::{
1718
use super::{TaskHandle, chain_view::ChainView, database::Database, keystore::KeyStore};
1819

1920
mod asyncop;
21+
mod balance;
2022
pub(crate) mod methods;
2123
mod payments;
2224
pub(crate) mod server;
2325
pub(crate) mod utils;
2426

27+
// TODO: https://github.com/zcash/wallet/issues/15
28+
fn value_from_zat_balance(value: ZatBalance) -> f64 {
29+
(i64::from(value) as f64) / (COIN as f64)
30+
}
31+
2532
#[derive(Debug)]
2633
pub(crate) struct JsonRpc {}
2734

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use transparent::bundle::TxOut;
2+
use zcash_client_backend::data_api::WalletRead;
3+
use zcash_primitives::transaction::Transaction;
4+
use zcash_protocol::{consensus::BlockHeight, value::Zatoshis};
5+
6+
use crate::components::database::DbConnection;
7+
8+
/// Equivalent to `CTransaction::GetValueOut` from `zcashd`.
9+
pub(super) fn wtx_get_value_out(tx: &Transaction) -> Option<Zatoshis> {
10+
tx.transparent_bundle()
11+
.map(|bundle| bundle.vout.iter().map(|txout| txout.value).sum())
12+
.unwrap_or(Some(Zatoshis::ZERO))
13+
}
14+
15+
/// Equivalent to `CWalletTx::GetDebit` from `zcashd`.
16+
pub(super) fn wtx_get_debit(
17+
wallet: &DbConnection,
18+
tx: &Transaction,
19+
filter: impl Fn(&TxOut) -> bool,
20+
) -> Option<Zatoshis> {
21+
match tx.transparent_bundle() {
22+
None => Some(Zatoshis::ZERO),
23+
Some(bundle) if bundle.vin.is_empty() => Some(Zatoshis::ZERO),
24+
Some(bundle) => bundle
25+
.vin
26+
.iter()
27+
.map(|txin| {
28+
wallet
29+
.get_transaction(*txin.prevout.txid())
30+
.ok()
31+
.flatten()
32+
.as_ref()
33+
.and_then(|prev_tx| prev_tx.transparent_bundle())
34+
.and_then(|bundle| bundle.vout.get(txin.prevout.n() as usize))
35+
.filter(|txout| filter(txout))
36+
.map(|txout| txout.value)
37+
.unwrap_or(Zatoshis::ZERO)
38+
})
39+
.sum::<Option<Zatoshis>>(),
40+
}
41+
}
42+
43+
/// Equivalent to `CWalletTx::GetCredit` from `zcashd`.
44+
pub(super) fn wtx_get_credit(
45+
wallet: &DbConnection,
46+
tx: &Transaction,
47+
as_of_height: Option<BlockHeight>,
48+
filter: impl Fn(&TxOut) -> bool,
49+
) -> Option<Zatoshis> {
50+
match tx.transparent_bundle() {
51+
None => Some(Zatoshis::ZERO),
52+
// Must wait until coinbase is safely deep enough in the chain before valuing it.
53+
Some(bundle) if bundle.is_coinbase() && GetBlocksToMaturity(as_of_height) > 0 => {
54+
Some(Zatoshis::ZERO)
55+
}
56+
Some(bundle) => bundle
57+
.vout
58+
.iter()
59+
.map(|txout| {
60+
if filter(txout) {
61+
txout.value
62+
} else {
63+
Zatoshis::ZERO
64+
}
65+
})
66+
.sum::<Option<Zatoshis>>(),
67+
}
68+
}
69+
70+
pub(super) fn wtx_is_from_me(
71+
wallet: &DbConnection,
72+
tx: &Transaction,
73+
filter: impl Fn(&TxOut) -> bool,
74+
) -> Option<bool> {
75+
if wtx_get_debit(wallet, tx, filter)? > Zatoshis::ZERO {
76+
return Some(true);
77+
}
78+
79+
if let Some(bundle) = tx.sapling_bundle() {
80+
for spend in bundle.shielded_spends() {
81+
// TODO: Check wallet for (spent) nullifiers.
82+
// spend.nullifier()
83+
}
84+
}
85+
86+
// TODO: Fix bug in `zcashd` where we forgot to add Orchard here.
87+
if let Some(bundle) = tx.orchard_bundle() {
88+
for action in bundle.actions() {
89+
// TODO: Check wallet for (spent) nullifiers.
90+
// action.nullifier()
91+
}
92+
}
93+
94+
Some(false)
95+
}

zallet/src/components/json_rpc/methods.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod get_address_for_account;
1919
mod get_new_account;
2020
mod get_notes_count;
2121
mod get_operation;
22+
mod get_transaction;
2223
mod get_wallet_info;
2324
mod help;
2425
mod list_accounts;
@@ -31,6 +32,7 @@ mod openrpc;
3132
mod recover_accounts;
3233
mod unlock_wallet;
3334
mod z_send_many;
35+
mod view_transaction;
3436

3537
#[rpc(server)]
3638
pub(crate) trait Rpc {
@@ -209,6 +211,41 @@ pub(crate) trait Rpc {
209211
#[method(name = "z_listunifiedreceivers")]
210212
fn list_unified_receivers(&self, unified_address: &str) -> list_unified_receivers::Response;
211213

214+
/// Returns detailed information about in-wallet transaction `txid`.
215+
///
216+
/// This does not include complete information about shielded components of the
217+
/// transaction; to obtain details about shielded components of the transaction use
218+
/// `z_viewtransaction`.
219+
///
220+
/// # Parameters
221+
///
222+
/// - `includeWatchonly` (bool, optional, default=false): Whether to include watchonly
223+
/// addresses in balance calculation and `details`.
224+
/// - `verbose`: Must be `false` or omitted.
225+
/// - `asOfHeight` (numeric, optional, default=-1): Execute the query as if it were
226+
/// run when the blockchain was at the height specified by this argument. The
227+
/// default is to use the entire blockchain that the node is aware of. -1 can be
228+
/// used as in other RPC calls to indicate the current height (including the
229+
/// mempool), but this does not support negative values in general. A "future"
230+
/// height will fall back to the current height. Any explicit value will cause the
231+
/// mempool to be ignored, meaning no unconfirmed tx will be considered.
232+
///
233+
/// # Bitcoin compatibility
234+
///
235+
/// Compatible up to three arguments, but can only use the default value for `verbose`.
236+
#[method(name = "gettransaction")]
237+
async fn get_transaction(
238+
&self,
239+
txid: &str,
240+
include_watchonly: Option<bool>,
241+
verbose: Option<bool>,
242+
as_of_height: Option<i64>,
243+
) -> get_transaction::Response;
244+
245+
/// Returns detailed shielded information about in-wallet transaction `txid`.
246+
#[method(name = "z_viewtransaction")]
247+
async fn view_transaction(&self, txid: &str) -> view_transaction::Response;
248+
212249
/// Returns an array of unspent shielded notes with between minconf and maxconf
213250
/// (inclusive) confirmations.
214251
///
@@ -436,6 +473,27 @@ impl RpcServer for RpcImpl {
436473
list_unified_receivers::call(unified_address)
437474
}
438475

476+
async fn get_transaction(
477+
&self,
478+
txid: &str,
479+
include_watchonly: Option<bool>,
480+
verbose: Option<bool>,
481+
as_of_height: Option<i64>,
482+
) -> get_transaction::Response {
483+
get_transaction::call(
484+
self.wallet().await?.as_ref(),
485+
self.chain().await?,
486+
txid,
487+
include_watchonly.unwrap_or(false),
488+
verbose.unwrap_or(false),
489+
as_of_height.unwrap_or(-1),
490+
)
491+
}
492+
493+
async fn view_transaction(&self, txid: &str) -> view_transaction::Response {
494+
view_transaction::call(self.wallet().await?.as_ref(), txid)
495+
}
496+
439497
async fn list_unspent(&self) -> list_unspent::Response {
440498
list_unspent::call(self.wallet().await?.as_ref())
441499
}

0 commit comments

Comments
 (0)