Auto merge of #2913 - bitcartel:2910_z_listunspent, r=bitcartel
Closes #2910. Add z_listunspent RPC call. Have tested on network. Opening up for review.
This commit is contained in:
commit
5bdce01848
|
@ -8,7 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework
|
|||
from test_framework.authproxy import JSONRPCException
|
||||
from test_framework.mininode import COIN
|
||||
from test_framework.util import assert_equal, initialize_chain_clean, \
|
||||
start_nodes, connect_nodes_bi, stop_node, wait_and_assert_operationid_status
|
||||
start_nodes, connect_nodes_bi, wait_and_assert_operationid_status
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
@ -96,8 +96,6 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||
break
|
||||
assert_equal("failed", status)
|
||||
assert_equal("no UTXOs found for taddr from address" in errorString, True)
|
||||
stop_node(self.nodes[3], 3)
|
||||
self.nodes.pop()
|
||||
|
||||
# This send will fail because our wallet does not allow any change when protecting a coinbase utxo,
|
||||
# as it's currently not possible to specify a change address in z_sendmany.
|
||||
|
@ -129,6 +127,10 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||
assert_equal("failed", status)
|
||||
assert_equal("wallet does not allow any change" in errorString, True)
|
||||
|
||||
# Add viewing key for myzaddr to Node 3
|
||||
myviewingkey = self.nodes[0].z_exportviewingkey(myzaddr)
|
||||
self.nodes[3].z_importviewingkey(myviewingkey, "no")
|
||||
|
||||
# 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 = []
|
||||
|
@ -136,9 +138,43 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||
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 that has zero confirmations
|
||||
results = self.nodes[0].z_listunspent()
|
||||
assert(len(results) == 0)
|
||||
results = self.nodes[0].z_listunspent(0) # set minconf to zero
|
||||
assert(len(results) == 1)
|
||||
assert_equal(results[0]["address"], myzaddr)
|
||||
assert_equal(results[0]["amount"], shieldvalue)
|
||||
assert_equal(results[0]["confirmations"], 0)
|
||||
|
||||
# Mine the tx
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
# Verify that z_listunspent returns one note which has been confirmed
|
||||
results = self.nodes[0].z_listunspent()
|
||||
assert(len(results) == 1)
|
||||
assert_equal(results[0]["address"], myzaddr)
|
||||
assert_equal(results[0]["amount"], shieldvalue)
|
||||
assert_equal(results[0]["confirmations"], 1)
|
||||
assert_equal(results[0]["spendable"], True)
|
||||
|
||||
# Verify that z_listunspent returns note for watchonly address on node 3.
|
||||
results = self.nodes[3].z_listunspent(1, 999, True)
|
||||
assert(len(results) == 1)
|
||||
assert_equal(results[0]["address"], myzaddr)
|
||||
assert_equal(results[0]["amount"], shieldvalue)
|
||||
assert_equal(results[0]["confirmations"], 1)
|
||||
assert_equal(results[0]["spendable"], False)
|
||||
|
||||
# Verify that z_listunspent returns error when address spending key from node 0 is not available in wallet of node 1.
|
||||
try:
|
||||
results = self.nodes[1].z_listunspent(1, 999, False, [myzaddr])
|
||||
except JSONRPCException as e:
|
||||
errorString = e.error['message']
|
||||
assert_equal("Invalid parameter, spending key for address does not belong to wallet" in errorString, True)
|
||||
|
||||
# Verify that debug=zrpcunsafe logs params, and that full txid is associated with opid
|
||||
logpath = self.options.tmpdir+"/node0/regtest/debug.log"
|
||||
logcounter = 0
|
||||
|
@ -333,13 +369,22 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
|
|||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
# check balances
|
||||
# check balances and unspent notes
|
||||
resp = self.nodes[2].z_gettotalbalance()
|
||||
assert_equal(Decimal(resp["private"]), send_amount)
|
||||
|
||||
notes = self.nodes[2].z_listunspent()
|
||||
sum_of_notes = sum([note["amount"] for note in notes])
|
||||
assert_equal(Decimal(resp["private"]), sum_of_notes)
|
||||
|
||||
resp = self.nodes[0].z_getbalance(myzaddr)
|
||||
assert_equal(Decimal(resp), zbalance - custom_fee - send_amount)
|
||||
sproutvalue -= custom_fee
|
||||
check_value_pool(self.nodes[0], 'sprout', sproutvalue)
|
||||
|
||||
notes = self.nodes[0].z_listunspent(1, 99999, False, [myzaddr])
|
||||
sum_of_notes = sum([note["amount"] for note in notes])
|
||||
assert_equal(Decimal(resp), sum_of_notes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletProtectCoinbaseTest().main()
|
||||
|
|
|
@ -105,6 +105,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "getblocksubsidy", 0},
|
||||
{ "z_listaddresses", 0},
|
||||
{ "z_listreceivedbyaddress", 1},
|
||||
{ "z_listunspent", 0 },
|
||||
{ "z_listunspent", 1 },
|
||||
{ "z_listunspent", 2 },
|
||||
{ "z_listunspent", 3 },
|
||||
{ "z_getbalance", 1},
|
||||
{ "z_gettotalbalance", 0},
|
||||
{ "z_gettotalbalance", 1},
|
||||
|
|
|
@ -385,6 +385,7 @@ static const CRPCCommand vRPCCommands[] =
|
|||
{ "wallet", "zcrawreceive", &zc_raw_receive, true },
|
||||
{ "wallet", "zcsamplejoinsplit", &zc_sample_joinsplit, true },
|
||||
{ "wallet", "z_listreceivedbyaddress",&z_listreceivedbyaddress,false },
|
||||
{ "wallet", "z_listunspent", &z_listunspent, false },
|
||||
{ "wallet", "z_getbalance", &z_getbalance, false },
|
||||
{ "wallet", "z_gettotalbalance", &z_gettotalbalance, false },
|
||||
{ "wallet", "z_mergetoaddress", &z_mergetoaddress, false },
|
||||
|
|
|
@ -287,6 +287,7 @@ extern UniValue z_listaddresses(const UniValue& params, bool fHelp); // in rpcwa
|
|||
extern UniValue z_exportwallet(const UniValue& params, bool fHelp); // in rpcdump.cpp
|
||||
extern UniValue z_importwallet(const UniValue& params, bool fHelp); // in rpcdump.cpp
|
||||
extern UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_listunspent(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_getbalance(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_gettotalbalance(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
extern UniValue z_mergetoaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp
|
||||
|
|
|
@ -1266,6 +1266,54 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys)
|
|||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rpc_z_listunspent_parameters)
|
||||
{
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
|
||||
UniValue retValue;
|
||||
|
||||
// too many args
|
||||
BOOST_CHECK_THROW(CallRPC("z_listunspent 1 2 3 4 5"), runtime_error);
|
||||
|
||||
// minconf must be >= 0
|
||||
BOOST_CHECK_THROW(CallRPC("z_listunspent -1"), runtime_error);
|
||||
|
||||
// maxconf must be > minconf
|
||||
BOOST_CHECK_THROW(CallRPC("z_listunspent 2 1"), runtime_error);
|
||||
|
||||
// maxconf must not be out of range
|
||||
BOOST_CHECK_THROW(CallRPC("z_listunspent 1 9999999999"), runtime_error);
|
||||
|
||||
// must be an array of addresses
|
||||
BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 false ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP"), runtime_error);
|
||||
|
||||
// address must be string
|
||||
BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 false [123456]"), runtime_error);
|
||||
|
||||
// no spending key
|
||||
BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 false [\"ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP\"]"), runtime_error);
|
||||
|
||||
// allow watch only
|
||||
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);
|
||||
|
||||
// create shielded address so we have the spending key
|
||||
BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getnewaddress"));
|
||||
std::string myzaddr = retValue.get_str();
|
||||
|
||||
// return empty array for this address
|
||||
BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listunspent 1 999 false [\"" + myzaddr + "\"]"));
|
||||
UniValue arr = retValue.get_array();
|
||||
BOOST_CHECK_EQUAL(0, arr.size());
|
||||
|
||||
// duplicate address error
|
||||
BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 false [\"" + myzaddr + "\", \"" + myzaddr + "\"]"), runtime_error);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters)
|
||||
{
|
||||
|
|
|
@ -2428,6 +2428,138 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
|||
return results;
|
||||
}
|
||||
|
||||
|
||||
UniValue z_listunspent(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 4)
|
||||
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"
|
||||
"When minconf is 0, unspent notes with zero confirmations are returned, even though they are not immediately spendable.\n"
|
||||
"Results are an array of Objects, each of which has:\n"
|
||||
"{txid, jsindex, jsoutindex, confirmations, address, amount, memo}\n"
|
||||
"\nArguments:\n"
|
||||
"1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
|
||||
"2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
|
||||
"3. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n"
|
||||
"4. \"addresses\" (string) A json array of zaddrs to filter on. Duplicate addresses not allowed.\n"
|
||||
" [\n"
|
||||
" \"address\" (string) zaddr\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"\nResult\n"
|
||||
"[ (array of json object)\n"
|
||||
" {\n"
|
||||
" \"txid\" : \"txid\", (string) the transaction id \n"
|
||||
" \"jsindex\" : n (numeric) the joinsplit index\n"
|
||||
" \"jsoutindex\" : n (numeric) the output index of the joinsplit\n"
|
||||
" \"confirmations\" : n (numeric) the number of confirmations\n"
|
||||
" \"spendable\" : true|false (boolean) true if note can be spent by wallet, false if note has zero confirmations, false if address is watchonly\n"
|
||||
" \"address\" : \"address\", (string) the shielded address\n"
|
||||
" \"amount\": xxxxx, (numeric) the amount of value in the note\n"
|
||||
" \"memo\": xxxxx, (string) hexademical string representation of memo field\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
"]\n"
|
||||
|
||||
"\nExamples\n"
|
||||
+ HelpExampleCli("z_listunspent", "")
|
||||
+ HelpExampleCli("z_listunspent", "6 9999999 false \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"")
|
||||
+ HelpExampleRpc("z_listunspent", "6 9999999 false \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"")
|
||||
);
|
||||
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VBOOL)(UniValue::VARR));
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 0) {
|
||||
nMinDepth = params[0].get_int();
|
||||
}
|
||||
if (nMinDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
|
||||
int nMaxDepth = 9999999;
|
||||
if (params.size() > 1) {
|
||||
nMaxDepth = params[1].get_int();
|
||||
}
|
||||
if (nMaxDepth < nMinDepth) {
|
||||
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 = {};
|
||||
|
||||
bool fIncludeWatchonly = false;
|
||||
if (params.size() > 2) {
|
||||
fIncludeWatchonly = params[2].get_bool();
|
||||
}
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
// User has supplied zaddrs to filter on
|
||||
if (params.size() > 3) {
|
||||
UniValue addresses = params[3].get_array();
|
||||
if (addresses.size()==0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, addresses array is empty.");
|
||||
|
||||
// Keep track of addresses to spot duplicates
|
||||
set<std::string> setAddress;
|
||||
|
||||
// Sources
|
||||
for (const UniValue& o : addresses.getValues()) {
|
||||
if (!o.isStr()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string");
|
||||
}
|
||||
string address = o.get_str();
|
||||
try {
|
||||
CZCPaymentAddress zaddr(address);
|
||||
libzcash::PaymentAddress addr = zaddr.Get();
|
||||
if (!fIncludeWatchonly && !pwalletMain->HaveSpendingKey(addr)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, spending key for address does not belong to wallet: ") + address);
|
||||
}
|
||||
zaddrs.insert(addr);
|
||||
} catch (const std::runtime_error&) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address);
|
||||
}
|
||||
|
||||
if (setAddress.count(address)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + address);
|
||||
}
|
||||
setAddress.insert(address);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// User did not provide zaddrs, so use default i.e. all addresses
|
||||
pwalletMain->GetPaymentAddresses(zaddrs);
|
||||
}
|
||||
|
||||
UniValue results(UniValue::VARR);
|
||||
|
||||
if (zaddrs.size() > 0) {
|
||||
std::vector<CUnspentNotePlaintextEntry> entries;
|
||||
pwalletMain->GetUnspentFilteredNotes(entries, zaddrs, nMinDepth, nMaxDepth, !fIncludeWatchonly);
|
||||
for (CUnspentNotePlaintextEntry & entry : entries) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("txid",entry.jsop.hash.ToString()));
|
||||
obj.push_back(Pair("jsindex", (int)entry.jsop.js ));
|
||||
obj.push_back(Pair("jsoutindex", (int)entry.jsop.n));
|
||||
obj.push_back(Pair("confirmations", entry.nHeight));
|
||||
obj.push_back(Pair("spendable", pwalletMain->HaveSpendingKey(entry.address)));
|
||||
obj.push_back(Pair("address", CZCPaymentAddress(entry.address).ToString()));
|
||||
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value))));
|
||||
std::string data(entry.plaintext.memo.begin(), entry.plaintext.memo.end());
|
||||
obj.push_back(Pair("memo", HexStr(data)));
|
||||
results.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
UniValue fundrawtransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
|
|
|
@ -3758,3 +3758,80 @@ void CWallet::GetFilteredNotes(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Find unspent notes filtered by payment address, min depth and max depth */
|
||||
void CWallet::GetUnspentFilteredNotes(
|
||||
std::vector<CUnspentNotePlaintextEntry>& outEntries,
|
||||
std::set<PaymentAddress>& filterAddresses,
|
||||
int minDepth,
|
||||
int maxDepth,
|
||||
bool requireSpendingKey)
|
||||
{
|
||||
LOCK2(cs_main, cs_wallet);
|
||||
|
||||
for (auto & p : mapWallet) {
|
||||
CWalletTx wtx = p.second;
|
||||
|
||||
// Filter the transactions before checking for notes
|
||||
if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < minDepth || wtx.GetDepthInMainChain() > maxDepth) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wtx.mapNoteData.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto & pair : wtx.mapNoteData) {
|
||||
JSOutPoint jsop = pair.first;
|
||||
CNoteData nd = pair.second;
|
||||
PaymentAddress pa = nd.address;
|
||||
|
||||
// skip notes which belong to a different payment address in the wallet
|
||||
if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip note which has been spent
|
||||
if (nd.nullifier && IsSpent(*nd.nullifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip notes where the spending key is not available
|
||||
if (requireSpendingKey && !HaveSpendingKey(pa)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int i = jsop.js; // Index into CTransaction.vjoinsplit
|
||||
int j = jsop.n; // Index into JSDescription.ciphertexts
|
||||
|
||||
// Get cached decryptor
|
||||
ZCNoteDecryption decryptor;
|
||||
if (!GetNoteDecryptor(pa, decryptor)) {
|
||||
// Note decryptors are created when the wallet is loaded, so it should always exist
|
||||
throw std::runtime_error(strprintf("Could not find note decryptor for payment address %s", CZCPaymentAddress(pa).ToString()));
|
||||
}
|
||||
|
||||
// determine amount of funds in the note
|
||||
auto hSig = wtx.vjoinsplit[i].h_sig(*pzcashParams, wtx.joinSplitPubKey);
|
||||
try {
|
||||
NotePlaintext plaintext = NotePlaintext::decrypt(
|
||||
decryptor,
|
||||
wtx.vjoinsplit[i].ciphertexts[j],
|
||||
wtx.vjoinsplit[i].ephemeralKey,
|
||||
hSig,
|
||||
(unsigned char) j);
|
||||
|
||||
outEntries.push_back(CUnspentNotePlaintextEntry{jsop, pa, plaintext, wtx.GetDepthInMainChain()});
|
||||
|
||||
} 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) {
|
||||
// Unexpected failure
|
||||
throw std::runtime_error(strprintf("Error while decrypting note for payment address %s: %s", CZCPaymentAddress(pa).ToString(), exc.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -271,7 +271,13 @@ struct CNotePlaintextEntry
|
|||
libzcash::NotePlaintext plaintext;
|
||||
};
|
||||
|
||||
|
||||
/** Decrypted note, location in a transaction, and confirmation height. */
|
||||
struct CUnspentNotePlaintextEntry {
|
||||
JSOutPoint jsop;
|
||||
libzcash::PaymentAddress address;
|
||||
libzcash::NotePlaintext plaintext;
|
||||
int nHeight;
|
||||
};
|
||||
|
||||
/** A transaction with a merkle branch linking it to the block chain. */
|
||||
class CMerkleTx : public CTransaction
|
||||
|
@ -1135,6 +1141,12 @@ public:
|
|||
bool ignoreSpent=true,
|
||||
bool ignoreUnspendable=true);
|
||||
|
||||
/* Find unspent notes filtered by payment address, min depth and max depth */
|
||||
void GetUnspentFilteredNotes(std::vector<CUnspentNotePlaintextEntry>& outEntries,
|
||||
std::set<libzcash::PaymentAddress>& filterAddresses,
|
||||
int minDepth=1,
|
||||
int maxDepth=INT_MAX,
|
||||
bool requireSpendingKey=true);
|
||||
};
|
||||
|
||||
/** A key allocated from the key pool. */
|
||||
|
|
Loading…
Reference in New Issue