-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closes #2910. Add z_listunspent RPC call. #2913
Conversation
This PR is helpful in debugging the merging of notes in PR #2797. At the time of writing, you can cherry-pick / rebase ths PR on top of #2797 without any conflict. Output looks like this:
|
c95d3a0
to
bbfa26f
Compare
I'd really like to get this into 1.0.15 (time-permitting) because it complements z_mergetoaddress (for example, a user doesn't know which zaddr has many notes and is a good candidate for consolidation) and users have requested both features. Can you review @arcalinea and @arielg? The PR is pretty much self-contained, adding a new RPC call, and should not change existing behaviour. |
bbfa26f
to
09f0f95
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK, pending 1 minor change. I tried the cli interface out and it looks good. Also ran the tests.
src/test/rpc_wallet_tests.cpp
Outdated
// minconf must be >= 0 | ||
BOOST_CHECK_THROW(CallRPC("z_listunspent -1"), runtime_error); | ||
|
||
// maxconf must be < minconf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maxconf must be > minconf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed update to fix this.
09f0f95
to
35c4f2d
Compare
Self-reminder:
|
35c4f2d
to
140f85f
Compare
@arcalinea I added support for viewing keys in a second commit which needs review. Thx. |
Added @braddmiller as reviewer, related to locking notes, as we may need a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested ACK. A few non-blocking comments and questions
throw JSONRPCError(RPC_INVALID_PARAMETER, "Maximum number of confirmations must be greater or equal to the minimum number of confirmations"); | ||
} | ||
|
||
std::set<libzcash::PaymentAddress> zaddrs = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
already using namespace std
, just a non-blocking consistency comment. Future instances of set
don't specify the namespace.
BOOST_CHECK_NO_THROW(CallRPC("z_listunspent 1 999 true [\"ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP\"]")); | ||
|
||
// wrong network, mainnet instead of testnet | ||
BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 true [\"zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U\"]"), runtime_error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear on the purpose of this test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just testing that you can't pass in a valid address for a different network. The test case is running on testnet, so we throw it a real mainnet address.
assert(len(results) == 1) | ||
assert_equal(results[0]["address"], myzaddr) | ||
assert_equal(results[0]["amount"], shieldvalue) | ||
assert_equal(results[0]["confirmations"], 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check assert_equal(results[0]["spendable"], True)
here with zero confirmations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I've added this, with a comment to clarify the note is unspent (which means its spendable) but it does need 1 confirmation before it can actually be spent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should "spendable" be true if the note has zero confirmations?
Oh, because "spendable" is a misnomer; this field is really designed to distinguish whether the wallet holds a spending key for the address. Let's make sure this is clearly documented in the RPC help.
3603748
to
9ac72ae
Compare
|
src/wallet/rpcwallet.cpp
Outdated
" \"js_index\" : n (numeric) the joinsplit index\n" | ||
" \"output_index\" : n (numeric) the output index of the joinsplit\n" | ||
" \"confirmations\" : n (numeric) the number of confirmations\n" | ||
" \"spendable\" : true|false (boolean) true if note is spendable, fale if watchonly\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/fale/false/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document that when minconf=0
, unspent notes with zero confirmations are included even though they aren't immediately spendable. Or possibly change the field name — either to "have_spending_key"
, or to "watchonly"
reversing the sense.
src/wallet/rpcwallet.cpp
Outdated
"\nExamples\n" | ||
+ HelpExampleCli("z_listunspent", "") | ||
+ HelpExampleCli("z_listunspent", "6 9999999 \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"") | ||
+ HelpExampleRpc("z_listunspent", "6, 9999999 \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Above 2 examples are missing a false
for watchonly flag:
+ HelpExampleCli("z_listunspent", "6 9999999 false \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"")
+ HelpExampleRpc("z_listunspent", "6, 9999999 false \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"")
This has been successfully ported to Hush: MyHush/hush#121 💯 One question I have is about amount=0 notes, which HushList uses. Should they be in the output of I look forward to using z_listunspent and it's improved ease + performance over current methods to get at various data. |
Reviewing now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments.
# This send will succeed. We send two coinbase utxos totalling 20.0 less a fee of 0.00010000, with no change. | ||
shieldvalue = Decimal('20.0') - Decimal('0.0001') | ||
recipients = [] | ||
recipients.append({"address":myzaddr, "amount": shieldvalue}) | ||
myopid = self.nodes[0].z_sendmany(mytaddr, recipients) | ||
mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) | ||
self.sync_all() | ||
|
||
# Verify that z_listunspent can return a note which has zero confirmations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"a note that has zero confirmations"
(having zero confirmations is a restrictive qualifier on the note)
assert(len(results) == 1) | ||
assert_equal(results[0]["address"], myzaddr) | ||
assert_equal(results[0]["amount"], shieldvalue) | ||
assert_equal(results[0]["confirmations"], 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should "spendable" be true if the note has zero confirmations?
Oh, because "spendable" is a misnomer; this field is really designed to distinguish whether the wallet holds a spending key for the address. Let's make sure this is clearly documented in the RPC help.
notes = self.nodes[2].z_listunspent() | ||
sum_of_notes = 0 | ||
for note in notes: | ||
sum_of_notes += note["amount"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be written as
sum_of_notes = sum([note["amount"] for note in notes])
(also below).
throw runtime_error( | ||
"z_listunspent ( minconf maxconf includeWatchonly [\"zaddr\",...] )\n" | ||
"\nReturns array of unspent shielded notes with between minconf and maxconf (inclusive) confirmations.\n" | ||
"Optionally filter to only include notes sent to specified addresses.\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document that addresses may not be duplicated (or remove that restriction).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would make more sense to me to just implicitly deduplicate, unless that's inconsistent with other RPC methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other RPC methods return a duplicated address error message so following that.
src/wallet/rpcwallet.cpp
Outdated
" \"js_index\" : n (numeric) the joinsplit index\n" | ||
" \"output_index\" : n (numeric) the output index of the joinsplit\n" | ||
" \"confirmations\" : n (numeric) the number of confirmations\n" | ||
" \"spendable\" : true|false (boolean) true if note is spendable, fale if watchonly\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document that when minconf=0
, unspent notes with zero confirmations are included even though they aren't immediately spendable. Or possibly change the field name — either to "have_spending_key"
, or to "watchonly"
reversing the sense.
src/wallet/wallet.cpp
Outdated
std::set<PaymentAddress>& filterAddresses, | ||
int minDepth, | ||
int maxDepth, | ||
bool ignoreUnspendable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename this parameter to requireSpendingKey
or similar.
src/wallet/wallet.cpp
Outdated
CWalletTx wtx = p.second; | ||
|
||
// Filter the transactions before checking for notes | ||
if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < minDepth || wtx.GetDepthInMainChain() > maxDepth ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: remove space before closing paren.
} | ||
} | ||
else { | ||
// User did not provide zaddrs, so use default i.e. all addresses |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetUnspentFilteredNotes
supports the unfiltered case without having to enumerate all addresses; use that support.
PaymentAddress pa = nd.address; | ||
|
||
// skip notes which belong to a different payment address in the wallet | ||
if (!(filterAddresses.empty() || filterAddresses.count(pa))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like a questionable API to have the empty set of addresses mean "don't filter by address"; I would expect that not to be treated as a special case and to just return no results. const std::set<PaymentAddress> *
with nullptr
meaning "don't filter", or boost::optional<const std::set<PaymentAddress>&>
(but note portability issues), seem like more appropriate types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach has been used in other related methods; so beyond scope to change in this PR, but have filed ticket: #3118
} catch (const note_decryption_failed &err) { | ||
// Couldn't decrypt with this spending key | ||
throw std::runtime_error(strprintf("Could not decrypt note for payment address %s", CZCPaymentAddress(pa).ToString())); | ||
} catch (const std::exception &exc) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be narrowed? (I'm concerned about losing stack trace information by rethrowing.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure. Won't change for now since its the same for GetFilteredNotes
@leto wrote:
Maybe an optional parameter to filter the minimum value of the notes returned? |
@daira I would definitely find it useful to have an optional parameter that filters out amount=0 notes, which are technically "can be spent" but which most wallet software would like to filter out. In particular, Hush uses many amount=0 notes and this proposed parameter would prevent our frontend from having to process and essentially ignore a lot of data |
Filed #3119 for optional parameter to filter based on note value. |
9ac72ae
to
f083d15
Compare
@daira Force pushed to address review comments. Squashed the two commits into one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK
📌 Commit f083d15 has been approved by |
⌛ Testing commit f083d1510f5cf26996e950dcf3bf3cffbabc013d with merge 262ff6c7e8c2afeffcc4e911759b2bbf23062e1f... |
💔 Test failed - pr-merge |
PyFlakes warnings 😄 |
f083d15
to
d72c19a
Compare
Pushed pyflakes fix. @zkbot r+ |
📌 Commit d72c19a has been approved by |
Closes #2910. Add z_listunspent RPC call. Have tested on network. Opening up for review.
Have tested on network. Opening up for review.