Conversation
4f60c0b to
542c349
Compare
| legacy_transparent: Option<TransparentBalance>, | ||
|
|
||
| /// The total of all funds for which this wallet controls spending keys. | ||
| total: Balance, |
There was a problem hiding this comment.
A downside to the current design including total fields for each account as well as the wallet balance, is that it is very duplicative:
$ zallet -d local/wallet-testnet rpc z_getbalances 1 true
{
"accounts": [
{
"account_uuid": "46416286-4799-4ac4-be4b-dd879274d340",
"sapling": {
"value": 4.69997000,
"valueZat": 469997000
},
"total": {
"value": 4.69997000,
"valueZat": 469997000
}
}
],
"total": {
"value": 4.69997000,
"valueZat": 469997000
}
}But maybe this is only visually annoying, and doesn't actually matter? From an automated consumption perspective, this mostly just doubles the JSON-RPC response size, which is linear in the number of accounts in the wallet, so likely not large.
There was a problem hiding this comment.
It's fine I think. But personally, I would just drop "value" and only include "valueZat". Decimals were always a mistake.
There was a problem hiding this comment.
I've now made things slightly more verbose by including pending etc. value, and choosing to always show a spendable object anywhere a balance is show (so always for totals, and present whenever pending etc is present even if spendable is zero).
For a wallet with a single empty account (either due to being new, or just after starting account recovery), we see this in the current PR:
$ zallet -d local/wallet-testnet rpc z_getbalances 1 true
{
"accounts": [
{
"account_uuid": "6ee0fac8-0621-4dc3-a507-9107ad47dc07",
"total": {
"spendable": {
"value": 0.00000000,
"valueZat": 0
}
}
}
],
"total": {
"spendable": {
"value": 0.00000000,
"valueZat": 0
}
}
}After starting a previously-synced wallet that has been offline for some time, we see this in the current PR:
$ zallet -d local/wallet-testnet rpc z_getbalances 1 true
{
"accounts": [
{
"account_uuid": "46416286-4799-4ac4-be4b-dd879274d340",
"sapling": {
"spendable": {
"value": 0.00000000,
"valueZat": 0
},
"pending": {
"value": 4.69997000,
"valueZat": 469997000
}
},
"orchard": {
"spendable": {
"value": 0.00000000,
"valueZat": 0
},
"pending": {
"value": 9.60000000,
"valueZat": 960000000
}
},
"total": {
"spendable": {
"value": 0.00000000,
"valueZat": 0
},
"pending": {
"value": 14.29997000,
"valueZat": 1429997000
}
}
}
],
"total": {
"spendable": {
"value": 0.00000000,
"valueZat": 0
},
"pending": {
"value": 14.29997000,
"valueZat": 1429997000
}
}
}And once synced, we see this:
$ zallet -d local/wallet-testnet rpc z_getbalances 1 true
{
"accounts": [
{
"account_uuid": "46416286-4799-4ac4-be4b-dd879274d340",
"sapling": {
"spendable": {
"value": 4.69997000,
"valueZat": 469997000
}
},
"orchard": {
"spendable": {
"value": 9.60000000,
"valueZat": 960000000
}
},
"total": {
"spendable": {
"value": 14.29997000,
"valueZat": 1429997000
}
}
}
],
"total": {
"spendable": {
"value": 14.29997000,
"valueZat": 1429997000
}
}
}There was a problem hiding this comment.
The question of whether the duplication is useful is actually the same question as whether or not we should include the "value" field. If our position is that this API output is not intended to be directly consumed by humans, then both "value" and the overall total have no utility; an API consumer is going to be able to compute an overall total and doesn't need the decimal notation. In general, I don't think we should be pursuing a "humans should read JSON" path. So: I vote for "valueZat" only, and no overall total.
| // TODO: Separate out transparent coinbase. | ||
| let transparent_regular = account.unshielded_balance().spendable_value(); | ||
| let transparent_coinbase = Zatoshis::ZERO; | ||
| let transparent_coinbase_immature = Zatoshis::ZERO; | ||
|
|
||
| let sapling = account.sapling_balance().spendable_value(); | ||
| let orchard = account.orchard_balance().spendable_value(); |
There was a problem hiding this comment.
I'm using Balance::spendable_value() here because, at least as being designed for zcashd, the returned values were gated on the provided minconf argument. Setting that to 0 should result in spendable_value() being equal to the total discovered balance, for wallets that are not in the middle of being recovered.
We should consider whether this RPC should return something indicating that the wallet is fully-synced or not (i.e. whether minconf=0 will show all unspent funds or not).
There was a problem hiding this comment.
After reading the relevant zcashd issues and PR again, I decided to have fields for every part of the balance:
spendable: what the user expects to be their "balance".locked: anything that is spendable and trying to be spent elsewhere. Always omitted until we add support for locking outputs.pending: Anything that is temporarily unspendable (e.g. due to the requestedminconf).dust: Anything that is (essentially, modulo usage of already-paid-for dummy spends) permanently unspendable.
9e6b5c0 to
8cc445d
Compare
Closes #101.
| /// - `minconf` (numeric, optional, default=1) Only include transactions confirmed at | ||
| /// least this many times. |
There was a problem hiding this comment.
| /// - `minconf` (numeric, optional, default=1) Only include transactions confirmed at | |
| /// least this many times. | |
| /// - `minconf` (numeric, optional, default=1) Only include unspent outputs confirmed at | |
| /// least this many times. |
| legacy_transparent: Option<TransparentBalance>, | ||
|
|
||
| /// The total of all funds for which this wallet controls spending keys. | ||
| total: Balance, |
There was a problem hiding this comment.
The question of whether the duplication is useful is actually the same question as whether or not we should include the "value" field. If our position is that this API output is not intended to be directly consumed by humans, then both "value" and the overall total have no utility; an API consumer is going to be able to compute an overall total and doesn't need the decimal notation. In general, I don't think we should be pursuing a "humans should read JSON" path. So: I vote for "valueZat" only, and no overall total.
| /// | ||
| /// Omitted if no coinbase funds are present. | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| coinbase: Option<Balance>, |
There was a problem hiding this comment.
Don't we also need immature_coinbase in order to present a complete picture? Or is the documentation here wrong, and immature coinbase outputs will be reflected in the pending field of the balance?
| pub(super) const PARAM_INCLUDE_WATCHONLY_DESC: &str = | ||
| "Also include balance in watchonly addresses."; |
There was a problem hiding this comment.
nit: I don't like the phrase "in watchonly addresses" because it's the wrong mental model for Sapling/Orchard funds, which aren't "in an address" per se.
| None | Some(false) => Err(LegacyCode::Misc | ||
| .with_message("include_watchonly argument must be set to true (for now)")), |
There was a problem hiding this comment.
I think the default for include_watchonly should be true and only Some(false) should raise this error. In the future, the balance should report whether or not the wallet controls spending keys for the value it represents.
Thinking further, even though include_watchonly follows the pattern of the existing RPC APIs, I don't think that it's the right concept at all. The only important consideration is whether or not the wallet has local spend authority for a balance. Balance is not available at all for IVK-only accounts, and any account associated with an FVK could potentially be spendable via a hardware wallet, so the only relevant information that the caller needs is whether the balance represented can be spent using spending keys that are directly available to the wallet.
Closes #101.