Auto merge of #3436 - str4d:3214-z_getbalance, r=str4d
Add Sapling support to z_getbalance and z_gettotalbalance Also includes preparatory changes for various other RPCs that depend on `GetFilteredNotes` etc. Closes #3214.
This commit is contained in:
commit
c53884d20a
|
@ -888,13 +888,14 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
|
|||
|
||||
|
||||
bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
||||
std::vector<CSproutNotePlaintextEntry> entries;
|
||||
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
pwalletMain->GetFilteredNotes(entries, fromaddress_, mindepth_);
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress_, mindepth_);
|
||||
}
|
||||
|
||||
for (CSproutNotePlaintextEntry & entry : entries) {
|
||||
for (CSproutNotePlaintextEntry & entry : sproutEntries) {
|
||||
z_inputs_.push_back(SendManyInputJSOP(entry.jsop, entry.plaintext.note(boost::get<libzcash::SproutPaymentAddress>(frompaymentaddress_)), CAmount(entry.plaintext.value())));
|
||||
std::string data(entry.plaintext.memo().begin(), entry.plaintext.memo().end());
|
||||
LogPrint("zrpcunsafe", "%s: found unspent note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, memo=%s)\n",
|
||||
|
@ -906,6 +907,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
|||
HexStr(data).substr(0, 10)
|
||||
);
|
||||
}
|
||||
// TODO: Do something with Sapling notes
|
||||
|
||||
if (z_inputs_.size() == 0) {
|
||||
return false;
|
||||
|
|
|
@ -186,13 +186,16 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||
|
||||
// We currently have an unspent and unconfirmed note in the wallet (depth of -1)
|
||||
std::vector<CSproutNotePlaintextEntry> entries;
|
||||
wallet.GetFilteredNotes(entries, "", 0);
|
||||
EXPECT_EQ(0, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(entries, "", -1);
|
||||
EXPECT_EQ(1, entries.size());
|
||||
entries.clear();
|
||||
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", -1);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
|
||||
// Fake-mine the transaction
|
||||
EXPECT_EQ(-1, chainActive.Height());
|
||||
|
@ -212,15 +215,18 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
|
||||
|
||||
// We now have an unspent and confirmed note in the wallet (depth of 1)
|
||||
wallet.GetFilteredNotes(entries, "", 0);
|
||||
EXPECT_EQ(1, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(entries, "", 1);
|
||||
EXPECT_EQ(1, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(entries, "", 2);
|
||||
EXPECT_EQ(0, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
|
||||
|
||||
// Let's spend the note.
|
||||
|
@ -247,21 +253,25 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
|
||||
|
||||
// The note has been spent. By default, GetFilteredNotes() ignores spent notes.
|
||||
wallet.GetFilteredNotes(entries, "", 0);
|
||||
EXPECT_EQ(0, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
// Let's include spent notes to retrieve it.
|
||||
wallet.GetFilteredNotes(entries, "", 0, false);
|
||||
EXPECT_EQ(1, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0, false);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
// The spent note has two confirmations.
|
||||
wallet.GetFilteredNotes(entries, "", 2, false);
|
||||
EXPECT_EQ(1, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
// It does not have 3 confirmations.
|
||||
wallet.GetFilteredNotes(entries, "", 3, false);
|
||||
EXPECT_EQ(0, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 3, false);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
|
||||
|
||||
// Let's receive a new note
|
||||
|
@ -301,21 +311,25 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
wallet.AddToWallet(wtx3, true, NULL);
|
||||
|
||||
// We now have an unspent note which has one confirmation, in addition to our spent note.
|
||||
wallet.GetFilteredNotes(entries, "", 1);
|
||||
EXPECT_EQ(1, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
// Let's return the spent note too.
|
||||
wallet.GetFilteredNotes(entries, "", 1, false);
|
||||
EXPECT_EQ(2, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1, false);
|
||||
EXPECT_EQ(2, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
// Increasing number of confirmations will exclude our new unspent note.
|
||||
wallet.GetFilteredNotes(entries, "", 2, false);
|
||||
EXPECT_EQ(1, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
// If we also ignore spent notes at this depth, we won't find any notes.
|
||||
wallet.GetFilteredNotes(entries, "", 2, true);
|
||||
EXPECT_EQ(0, entries.size());
|
||||
entries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, true);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
|
||||
// Tear down
|
||||
chainActive.SetTip(NULL);
|
||||
|
|
|
@ -2556,10 +2556,11 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
|
|||
UniValue results(UniValue::VARR);
|
||||
|
||||
if (zaddrs.size() > 0) {
|
||||
std::vector<CUnspentSproutNotePlaintextEntry> entries;
|
||||
pwalletMain->GetUnspentFilteredNotes(entries, zaddrs, nMinDepth, nMaxDepth, !fIncludeWatchonly);
|
||||
std::vector<CUnspentSproutNotePlaintextEntry> sproutEntries;
|
||||
std::vector<UnspentSaplingNoteEntry> saplingEntries;
|
||||
pwalletMain->GetUnspentFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, !fIncludeWatchonly);
|
||||
std::set<std::pair<PaymentAddress, uint256>> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs);
|
||||
for (CUnspentSproutNotePlaintextEntry & entry : entries) {
|
||||
for (CUnspentSproutNotePlaintextEntry & entry : sproutEntries) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
|
||||
obj.push_back(Pair("jsindex", (int)entry.jsop.js ));
|
||||
|
@ -2576,6 +2577,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
|
|||
}
|
||||
results.push_back(obj);
|
||||
}
|
||||
// TODO: Sapling
|
||||
}
|
||||
|
||||
return results;
|
||||
|
@ -3248,12 +3250,16 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ign
|
|||
|
||||
CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) {
|
||||
CAmount balance = 0;
|
||||
std::vector<CSproutNotePlaintextEntry> entries;
|
||||
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
pwalletMain->GetFilteredNotes(entries, address, minDepth, true, ignoreUnspendable);
|
||||
for (auto & entry : entries) {
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, address, minDepth, true, ignoreUnspendable);
|
||||
for (auto & entry : sproutEntries) {
|
||||
balance += CAmount(entry.plaintext.value());
|
||||
}
|
||||
for (auto & entry : saplingEntries) {
|
||||
balance += CAmount(entry.note.value());
|
||||
}
|
||||
return balance;
|
||||
}
|
||||
|
||||
|
@ -3309,10 +3315,11 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
}
|
||||
|
||||
UniValue result(UniValue::VARR);
|
||||
std::vector<CSproutNotePlaintextEntry> entries;
|
||||
pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false, false);
|
||||
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false);
|
||||
auto nullifierSet = hasSproutSpendingKey ? pwalletMain->GetNullifiersForAddresses({zaddr}) : std::set<std::pair<PaymentAddress, uint256>>();
|
||||
for (CSproutNotePlaintextEntry & entry : entries) {
|
||||
for (CSproutNotePlaintextEntry & entry : sproutEntries) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
|
||||
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value()))));
|
||||
|
@ -3326,6 +3333,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
}
|
||||
result.push_back(obj);
|
||||
}
|
||||
// TODO: Sapling
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -3338,8 +3346,8 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
|||
throw runtime_error(
|
||||
"z_getbalance \"address\" ( minconf )\n"
|
||||
"\nReturns the balance of a taddr or zaddr belonging to the node’s wallet.\n"
|
||||
"\nCAUTION: If address is a watch-only zaddr, the returned balance may be larger than the actual balance,"
|
||||
"\nbecause spends cannot be detected with incoming viewing keys.\n"
|
||||
"\nCAUTION: If the wallet has only an incoming viewing key for this address, then spends cannot be"
|
||||
"\ndetected, and so the returned balance may be larger than the actual balance.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string) The selected address. It may be a transparent or private address.\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
|
@ -3374,11 +3382,8 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
|||
if (!IsValidPaymentAddress(res)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr.");
|
||||
}
|
||||
// TODO: Add Sapling support. For now, ensure we can freely convert.
|
||||
assert(boost::get<libzcash::SproutPaymentAddress>(&res) != nullptr);
|
||||
auto zaddr = boost::get<libzcash::SproutPaymentAddress>(res);
|
||||
if (!(pwalletMain->HaveSproutSpendingKey(zaddr) || pwalletMain->HaveSproutViewingKey(zaddr))) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
|
||||
if (!boost::apply_visitor(PaymentAddressBelongsToWallet(pwalletMain), res)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, spending key or viewing key not found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3402,15 +3407,16 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
|
|||
throw runtime_error(
|
||||
"z_gettotalbalance ( minconf includeWatchonly )\n"
|
||||
"\nReturn the total value of funds stored in the node’s wallet.\n"
|
||||
"\nCAUTION: If the wallet contains watch-only zaddrs, the returned private balance may be larger than the actual balance,"
|
||||
"\nbecause spends cannot be detected with incoming viewing keys.\n"
|
||||
"\nCAUTION: If the wallet contains any addresses for which it only has incoming viewing keys,"
|
||||
"\nthe returned private balance may be larger than the actual balance, because spends cannot"
|
||||
"\nbe detected with incoming viewing keys.\n"
|
||||
"\nArguments:\n"
|
||||
"1. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\n"
|
||||
"2. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress' and 'z_importviewingkey')\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n"
|
||||
" \"private\": xxxxx, (numeric) the total balance of private funds\n"
|
||||
" \"private\": xxxxx, (numeric) the total balance of private funds (in both Sprout and Sapling addresses)\n"
|
||||
" \"total\": xxxxx, (numeric) the total balance of both transparent and private funds\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
|
@ -4247,11 +4253,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
|||
|
||||
if (useAny || useAnyNote || zaddrs.size() > 0) {
|
||||
// Get available notes
|
||||
std::vector<CSproutNotePlaintextEntry> entries;
|
||||
pwalletMain->GetFilteredNotes(entries, zaddrs);
|
||||
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs);
|
||||
|
||||
// Find unspent notes and update estimated size
|
||||
for (CSproutNotePlaintextEntry& entry : entries) {
|
||||
for (CSproutNotePlaintextEntry& entry : sproutEntries) {
|
||||
noteCounter++;
|
||||
CAmount nValue = entry.plaintext.value();
|
||||
|
||||
|
@ -4265,8 +4272,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
|||
maxedOutNotesFlag = true;
|
||||
} else {
|
||||
estimatedTxSize += increase;
|
||||
// TODO: Add Sapling support
|
||||
auto zaddr = boost::get<SproutPaymentAddress>(entry.address);
|
||||
auto zaddr = entry.address;
|
||||
SproutSpendingKey zkey;
|
||||
pwalletMain->GetSproutSpendingKey(zaddr, zkey);
|
||||
noteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey);
|
||||
|
@ -4278,6 +4284,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
|||
remainingNoteValue += nValue;
|
||||
}
|
||||
}
|
||||
// TODO: Add Sapling support
|
||||
}
|
||||
|
||||
size_t numUtxos = utxoInputs.size();
|
||||
|
|
|
@ -4091,7 +4091,13 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
|
|||
* Find notes in the wallet filtered by payment address, min depth and ability to spend.
|
||||
* These notes are decrypted and added to the output parameter vector, outEntries.
|
||||
*/
|
||||
void CWallet::GetFilteredNotes(std::vector<CSproutNotePlaintextEntry> & outEntries, std::string address, int minDepth, bool ignoreSpent, bool ignoreUnspendable)
|
||||
void CWallet::GetFilteredNotes(
|
||||
std::vector<CSproutNotePlaintextEntry>& sproutEntries,
|
||||
std::vector<SaplingNoteEntry>& saplingEntries,
|
||||
std::string address,
|
||||
int minDepth,
|
||||
bool ignoreSpent,
|
||||
bool ignoreUnspendable)
|
||||
{
|
||||
std::set<PaymentAddress> filterAddresses;
|
||||
|
||||
|
@ -4099,7 +4105,7 @@ void CWallet::GetFilteredNotes(std::vector<CSproutNotePlaintextEntry> & outEntri
|
|||
filterAddresses.insert(DecodePaymentAddress(address));
|
||||
}
|
||||
|
||||
GetFilteredNotes(outEntries, filterAddresses, minDepth, ignoreSpent, ignoreUnspendable);
|
||||
GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, ignoreSpent, ignoreUnspendable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4107,7 +4113,8 @@ void CWallet::GetFilteredNotes(std::vector<CSproutNotePlaintextEntry> & outEntri
|
|||
* These notes are decrypted and added to the output parameter vector, outEntries.
|
||||
*/
|
||||
void CWallet::GetFilteredNotes(
|
||||
std::vector<CSproutNotePlaintextEntry>& outEntries,
|
||||
std::vector<CSproutNotePlaintextEntry>& sproutEntries,
|
||||
std::vector<SaplingNoteEntry>& saplingEntries,
|
||||
std::set<PaymentAddress>& filterAddresses,
|
||||
int minDepth,
|
||||
bool ignoreSpent,
|
||||
|
@ -4123,10 +4130,6 @@ void CWallet::GetFilteredNotes(
|
|||
continue;
|
||||
}
|
||||
|
||||
if (wtx.mapSproutNoteData.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto & pair : wtx.mapSproutNoteData) {
|
||||
JSOutPoint jsop = pair.first;
|
||||
SproutNoteData nd = pair.second;
|
||||
|
@ -4172,7 +4175,7 @@ void CWallet::GetFilteredNotes(
|
|||
hSig,
|
||||
(unsigned char) j);
|
||||
|
||||
outEntries.push_back(CSproutNotePlaintextEntry{jsop, pa, plaintext});
|
||||
sproutEntries.push_back(CSproutNotePlaintextEntry{jsop, pa, plaintext});
|
||||
|
||||
} catch (const note_decryption_failed &err) {
|
||||
// Couldn't decrypt with this spending key
|
||||
|
@ -4182,13 +4185,61 @@ void CWallet::GetFilteredNotes(
|
|||
throw std::runtime_error(strprintf("Error while decrypting note for payment address %s: %s", EncodePaymentAddress(pa), exc.what()));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & pair : wtx.mapSaplingNoteData) {
|
||||
SaplingOutPoint op = pair.first;
|
||||
SaplingNoteData nd = pair.second;
|
||||
|
||||
auto maybe_pt = SaplingNotePlaintext::decrypt(
|
||||
wtx.vShieldedOutput[op.n].encCiphertext,
|
||||
nd.ivk,
|
||||
wtx.vShieldedOutput[op.n].ephemeralKey,
|
||||
wtx.vShieldedOutput[op.n].cm);
|
||||
assert(static_cast<bool>(maybe_pt));
|
||||
auto notePt = maybe_pt.get();
|
||||
|
||||
auto maybe_pa = nd.ivk.address(notePt.d);
|
||||
assert(static_cast<bool>(maybe_pa));
|
||||
auto pa = maybe_pa.get();
|
||||
|
||||
// skip notes which belong to a different payment address in the wallet
|
||||
if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ignoreSpent && nd.nullifier && IsSaplingSpent(*nd.nullifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip notes which cannot be spent
|
||||
if (ignoreUnspendable) {
|
||||
libzcash::SaplingIncomingViewingKey ivk;
|
||||
libzcash::SaplingFullViewingKey fvk;
|
||||
if (!(GetSaplingIncomingViewingKey(pa, ivk) &&
|
||||
GetSaplingFullViewingKey(ivk, fvk) &&
|
||||
HaveSaplingSpendingKey(fvk))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// skip locked notes
|
||||
// TODO: Add locking for Sapling notes
|
||||
// if (IsLockedNote(jsop)) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
auto note = notePt.note(nd.ivk).get();
|
||||
saplingEntries.push_back(SaplingNoteEntry {
|
||||
op, pa, note, notePt.memo() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Find unspent notes filtered by payment address, min depth and max depth */
|
||||
void CWallet::GetUnspentFilteredNotes(
|
||||
std::vector<CUnspentSproutNotePlaintextEntry>& outEntries,
|
||||
std::vector<CUnspentSproutNotePlaintextEntry>& sproutEntries,
|
||||
std::vector<UnspentSaplingNoteEntry>& saplingEntries,
|
||||
std::set<PaymentAddress>& filterAddresses,
|
||||
int minDepth,
|
||||
int maxDepth,
|
||||
|
@ -4204,10 +4255,6 @@ void CWallet::GetUnspentFilteredNotes(
|
|||
continue;
|
||||
}
|
||||
|
||||
if (wtx.mapSproutNoteData.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto & pair : wtx.mapSproutNoteData) {
|
||||
JSOutPoint jsop = pair.first;
|
||||
SproutNoteData nd = pair.second;
|
||||
|
@ -4248,7 +4295,7 @@ void CWallet::GetUnspentFilteredNotes(
|
|||
hSig,
|
||||
(unsigned char) j);
|
||||
|
||||
outEntries.push_back(CUnspentSproutNotePlaintextEntry{jsop, pa, plaintext, wtx.GetDepthInMainChain()});
|
||||
sproutEntries.push_back(CUnspentSproutNotePlaintextEntry{jsop, pa, plaintext, wtx.GetDepthInMainChain()});
|
||||
|
||||
} catch (const note_decryption_failed &err) {
|
||||
// Couldn't decrypt with this spending key
|
||||
|
@ -4258,6 +4305,71 @@ void CWallet::GetUnspentFilteredNotes(
|
|||
throw std::runtime_error(strprintf("Error while decrypting note for payment address %s: %s", EncodePaymentAddress(pa), exc.what()));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & pair : wtx.mapSaplingNoteData) {
|
||||
SaplingOutPoint op = pair.first;
|
||||
SaplingNoteData nd = pair.second;
|
||||
|
||||
auto maybe_pt = SaplingNotePlaintext::decrypt(
|
||||
wtx.vShieldedOutput[op.n].encCiphertext,
|
||||
nd.ivk,
|
||||
wtx.vShieldedOutput[op.n].ephemeralKey,
|
||||
wtx.vShieldedOutput[op.n].cm);
|
||||
assert(static_cast<bool>(maybe_pt));
|
||||
auto notePt = maybe_pt.get();
|
||||
|
||||
auto maybe_pa = nd.ivk.address(notePt.d);
|
||||
assert(static_cast<bool>(maybe_pa));
|
||||
auto pa = maybe_pa.get();
|
||||
|
||||
// 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 && IsSaplingSpent(*nd.nullifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip notes where the spending key is not available
|
||||
if (requireSpendingKey) {
|
||||
libzcash::SaplingIncomingViewingKey ivk;
|
||||
libzcash::SaplingFullViewingKey fvk;
|
||||
if (!(GetSaplingIncomingViewingKey(pa, ivk) &&
|
||||
GetSaplingFullViewingKey(ivk, fvk) &&
|
||||
HaveSaplingSpendingKey(fvk))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto note = notePt.note(nd.ivk).get();
|
||||
saplingEntries.push_back(UnspentSaplingNoteEntry {
|
||||
op, pa, note, notePt.memo(), wtx.GetDepthInMainChain() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Shielded key and address generalizations
|
||||
//
|
||||
|
||||
bool PaymentAddressBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const
|
||||
{
|
||||
return m_wallet->HaveSproutSpendingKey(zaddr) || m_wallet->HaveSproutViewingKey(zaddr);
|
||||
}
|
||||
|
||||
bool PaymentAddressBelongsToWallet::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
|
||||
{
|
||||
libzcash::SaplingIncomingViewingKey ivk;
|
||||
|
||||
// If we have a SaplingSpendingKey or SaplingExpandedSpendingKey in the
|
||||
// wallet, then we will also have the corresponding SaplingFullViewingKey.
|
||||
return m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
|
||||
m_wallet->HaveSaplingFullViewingKey(ivk);
|
||||
}
|
||||
|
||||
bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding& no) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -307,6 +307,24 @@ struct CUnspentSproutNotePlaintextEntry {
|
|||
int nHeight;
|
||||
};
|
||||
|
||||
/** Sapling note and its location in a transaction. */
|
||||
struct SaplingNoteEntry
|
||||
{
|
||||
SaplingOutPoint op;
|
||||
libzcash::SaplingPaymentAddress address;
|
||||
libzcash::SaplingNote note;
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo;
|
||||
};
|
||||
|
||||
/** Sapling note, location in a transaction, and confirmation height. */
|
||||
struct UnspentSaplingNoteEntry {
|
||||
SaplingOutPoint op;
|
||||
libzcash::SaplingPaymentAddress address;
|
||||
libzcash::SaplingNote note;
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo;
|
||||
int nHeight;
|
||||
};
|
||||
|
||||
/** A transaction with a merkle branch linking it to the block chain. */
|
||||
class CMerkleTx : public CTransaction
|
||||
{
|
||||
|
@ -1206,21 +1224,24 @@ public:
|
|||
void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; }
|
||||
|
||||
/* Find notes filtered by payment address, min depth, ability to spend */
|
||||
void GetFilteredNotes(std::vector<CSproutNotePlaintextEntry> & outEntries,
|
||||
void GetFilteredNotes(std::vector<CSproutNotePlaintextEntry>& sproutEntries,
|
||||
std::vector<SaplingNoteEntry>& saplingEntries,
|
||||
std::string address,
|
||||
int minDepth=1,
|
||||
bool ignoreSpent=true,
|
||||
bool ignoreUnspendable=true);
|
||||
|
||||
/* Find notes filtered by payment addresses, min depth, ability to spend */
|
||||
void GetFilteredNotes(std::vector<CSproutNotePlaintextEntry>& outEntries,
|
||||
void GetFilteredNotes(std::vector<CSproutNotePlaintextEntry>& sproutEntries,
|
||||
std::vector<SaplingNoteEntry>& saplingEntries,
|
||||
std::set<libzcash::PaymentAddress>& filterAddresses,
|
||||
int minDepth=1,
|
||||
bool ignoreSpent=true,
|
||||
bool ignoreUnspendable=true);
|
||||
|
||||
/* Find unspent notes filtered by payment address, min depth and max depth */
|
||||
void GetUnspentFilteredNotes(std::vector<CUnspentSproutNotePlaintextEntry>& outEntries,
|
||||
void GetUnspentFilteredNotes(std::vector<CUnspentSproutNotePlaintextEntry>& sproutEntries,
|
||||
std::vector<UnspentSaplingNoteEntry>& saplingEntries,
|
||||
std::set<libzcash::PaymentAddress>& filterAddresses,
|
||||
int minDepth=1,
|
||||
int maxDepth=INT_MAX,
|
||||
|
@ -1282,4 +1303,20 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Shielded key and address generalizations
|
||||
//
|
||||
|
||||
class PaymentAddressBelongsToWallet : public boost::static_visitor<bool>
|
||||
{
|
||||
private:
|
||||
CWallet *m_wallet;
|
||||
public:
|
||||
PaymentAddressBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {}
|
||||
|
||||
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
|
||||
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
|
||||
bool operator()(const libzcash::InvalidEncoding& no) const;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_WALLET_WALLET_H
|
||||
|
|
Loading…
Reference in New Issue