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:
@@ -888,13 +888,14 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
|
|||||||
|
|
||||||
|
|
||||||
bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
||||||
std::vector<CSproutNotePlaintextEntry> entries;
|
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||||
|
std::vector<SaplingNoteEntry> saplingEntries;
|
||||||
{
|
{
|
||||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
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())));
|
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());
|
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",
|
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)
|
HexStr(data).substr(0, 10)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// TODO: Do something with Sapling notes
|
||||||
|
|
||||||
if (z_inputs_.size() == 0) {
|
if (z_inputs_.size() == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -186,13 +186,16 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||||
|
|
||||||
// We currently have an unspent and unconfirmed note in the wallet (depth of -1)
|
// We currently have an unspent and unconfirmed note in the wallet (depth of -1)
|
||||||
std::vector<CSproutNotePlaintextEntry> entries;
|
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||||
wallet.GetFilteredNotes(entries, "", 0);
|
std::vector<SaplingNoteEntry> saplingEntries;
|
||||||
EXPECT_EQ(0, entries.size());
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
|
||||||
entries.clear();
|
EXPECT_EQ(0, sproutEntries.size());
|
||||||
wallet.GetFilteredNotes(entries, "", -1);
|
sproutEntries.clear();
|
||||||
EXPECT_EQ(1, entries.size());
|
saplingEntries.clear();
|
||||||
entries.clear();
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", -1);
|
||||||
|
EXPECT_EQ(1, sproutEntries.size());
|
||||||
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
|
|
||||||
// Fake-mine the transaction
|
// Fake-mine the transaction
|
||||||
EXPECT_EQ(-1, chainActive.Height());
|
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)
|
// We now have an unspent and confirmed note in the wallet (depth of 1)
|
||||||
wallet.GetFilteredNotes(entries, "", 0);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
|
||||||
EXPECT_EQ(1, entries.size());
|
EXPECT_EQ(1, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
wallet.GetFilteredNotes(entries, "", 1);
|
saplingEntries.clear();
|
||||||
EXPECT_EQ(1, entries.size());
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1);
|
||||||
entries.clear();
|
EXPECT_EQ(1, sproutEntries.size());
|
||||||
wallet.GetFilteredNotes(entries, "", 2);
|
sproutEntries.clear();
|
||||||
EXPECT_EQ(0, entries.size());
|
saplingEntries.clear();
|
||||||
entries.clear();
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2);
|
||||||
|
EXPECT_EQ(0, sproutEntries.size());
|
||||||
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
|
|
||||||
|
|
||||||
// Let's spend the note.
|
// Let's spend the note.
|
||||||
@@ -247,21 +253,25 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||||||
EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
|
EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
|
||||||
|
|
||||||
// The note has been spent. By default, GetFilteredNotes() ignores spent notes.
|
// The note has been spent. By default, GetFilteredNotes() ignores spent notes.
|
||||||
wallet.GetFilteredNotes(entries, "", 0);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0);
|
||||||
EXPECT_EQ(0, entries.size());
|
EXPECT_EQ(0, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
// Let's include spent notes to retrieve it.
|
// Let's include spent notes to retrieve it.
|
||||||
wallet.GetFilteredNotes(entries, "", 0, false);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 0, false);
|
||||||
EXPECT_EQ(1, entries.size());
|
EXPECT_EQ(1, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
// The spent note has two confirmations.
|
// The spent note has two confirmations.
|
||||||
wallet.GetFilteredNotes(entries, "", 2, false);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false);
|
||||||
EXPECT_EQ(1, entries.size());
|
EXPECT_EQ(1, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
// It does not have 3 confirmations.
|
// It does not have 3 confirmations.
|
||||||
wallet.GetFilteredNotes(entries, "", 3, false);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 3, false);
|
||||||
EXPECT_EQ(0, entries.size());
|
EXPECT_EQ(0, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
|
|
||||||
|
|
||||||
// Let's receive a new note
|
// Let's receive a new note
|
||||||
@@ -301,21 +311,25 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||||||
wallet.AddToWallet(wtx3, true, NULL);
|
wallet.AddToWallet(wtx3, true, NULL);
|
||||||
|
|
||||||
// We now have an unspent note which has one confirmation, in addition to our spent note.
|
// We now have an unspent note which has one confirmation, in addition to our spent note.
|
||||||
wallet.GetFilteredNotes(entries, "", 1);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1);
|
||||||
EXPECT_EQ(1, entries.size());
|
EXPECT_EQ(1, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
// Let's return the spent note too.
|
// Let's return the spent note too.
|
||||||
wallet.GetFilteredNotes(entries, "", 1, false);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 1, false);
|
||||||
EXPECT_EQ(2, entries.size());
|
EXPECT_EQ(2, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
// Increasing number of confirmations will exclude our new unspent note.
|
// Increasing number of confirmations will exclude our new unspent note.
|
||||||
wallet.GetFilteredNotes(entries, "", 2, false);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, false);
|
||||||
EXPECT_EQ(1, entries.size());
|
EXPECT_EQ(1, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
// If we also ignore spent notes at this depth, we won't find any notes.
|
// If we also ignore spent notes at this depth, we won't find any notes.
|
||||||
wallet.GetFilteredNotes(entries, "", 2, true);
|
wallet.GetFilteredNotes(sproutEntries, saplingEntries, "", 2, true);
|
||||||
EXPECT_EQ(0, entries.size());
|
EXPECT_EQ(0, sproutEntries.size());
|
||||||
entries.clear();
|
sproutEntries.clear();
|
||||||
|
saplingEntries.clear();
|
||||||
|
|
||||||
// Tear down
|
// Tear down
|
||||||
chainActive.SetTip(NULL);
|
chainActive.SetTip(NULL);
|
||||||
|
|||||||
@@ -2556,10 +2556,11 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
|
|||||||
UniValue results(UniValue::VARR);
|
UniValue results(UniValue::VARR);
|
||||||
|
|
||||||
if (zaddrs.size() > 0) {
|
if (zaddrs.size() > 0) {
|
||||||
std::vector<CUnspentSproutNotePlaintextEntry> entries;
|
std::vector<CUnspentSproutNotePlaintextEntry> sproutEntries;
|
||||||
pwalletMain->GetUnspentFilteredNotes(entries, zaddrs, nMinDepth, nMaxDepth, !fIncludeWatchonly);
|
std::vector<UnspentSaplingNoteEntry> saplingEntries;
|
||||||
|
pwalletMain->GetUnspentFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, !fIncludeWatchonly);
|
||||||
std::set<std::pair<PaymentAddress, uint256>> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs);
|
std::set<std::pair<PaymentAddress, uint256>> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs);
|
||||||
for (CUnspentSproutNotePlaintextEntry & entry : entries) {
|
for (CUnspentSproutNotePlaintextEntry & entry : sproutEntries) {
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
|
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
|
||||||
obj.push_back(Pair("jsindex", (int)entry.jsop.js ));
|
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);
|
results.push_back(obj);
|
||||||
}
|
}
|
||||||
|
// TODO: Sapling
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
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 getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) {
|
||||||
CAmount balance = 0;
|
CAmount balance = 0;
|
||||||
std::vector<CSproutNotePlaintextEntry> entries;
|
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||||
|
std::vector<SaplingNoteEntry> saplingEntries;
|
||||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||||
pwalletMain->GetFilteredNotes(entries, address, minDepth, true, ignoreUnspendable);
|
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, address, minDepth, true, ignoreUnspendable);
|
||||||
for (auto & entry : entries) {
|
for (auto & entry : sproutEntries) {
|
||||||
balance += CAmount(entry.plaintext.value());
|
balance += CAmount(entry.plaintext.value());
|
||||||
}
|
}
|
||||||
|
for (auto & entry : saplingEntries) {
|
||||||
|
balance += CAmount(entry.note.value());
|
||||||
|
}
|
||||||
return balance;
|
return balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3309,10 +3315,11 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
UniValue result(UniValue::VARR);
|
UniValue result(UniValue::VARR);
|
||||||
std::vector<CSproutNotePlaintextEntry> entries;
|
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||||
pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false, false);
|
std::vector<SaplingNoteEntry> saplingEntries;
|
||||||
|
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false);
|
||||||
auto nullifierSet = hasSproutSpendingKey ? pwalletMain->GetNullifiersForAddresses({zaddr}) : std::set<std::pair<PaymentAddress, uint256>>();
|
auto nullifierSet = hasSproutSpendingKey ? pwalletMain->GetNullifiersForAddresses({zaddr}) : std::set<std::pair<PaymentAddress, uint256>>();
|
||||||
for (CSproutNotePlaintextEntry & entry : entries) {
|
for (CSproutNotePlaintextEntry & entry : sproutEntries) {
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
|
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
|
||||||
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value()))));
|
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);
|
result.push_back(obj);
|
||||||
}
|
}
|
||||||
|
// TODO: Sapling
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3338,8 +3346,8 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
|||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"z_getbalance \"address\" ( minconf )\n"
|
"z_getbalance \"address\" ( minconf )\n"
|
||||||
"\nReturns the balance of a taddr or zaddr belonging to the node’s wallet.\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,"
|
"\nCAUTION: If the wallet has only an incoming viewing key for this address, then spends cannot be"
|
||||||
"\nbecause spends cannot be detected with incoming viewing keys.\n"
|
"\ndetected, and so the returned balance may be larger than the actual balance.\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"address\" (string) The selected address. It may be a transparent or private address.\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"
|
"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)) {
|
if (!IsValidPaymentAddress(res)) {
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr.");
|
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.
|
if (!boost::apply_visitor(PaymentAddressBelongsToWallet(pwalletMain), res)) {
|
||||||
assert(boost::get<libzcash::SproutPaymentAddress>(&res) != nullptr);
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, spending key or viewing key not found.");
|
||||||
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.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3402,15 +3407,16 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
|
|||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"z_gettotalbalance ( minconf includeWatchonly )\n"
|
"z_gettotalbalance ( minconf includeWatchonly )\n"
|
||||||
"\nReturn the total value of funds stored in the node’s wallet.\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,"
|
"\nCAUTION: If the wallet contains any addresses for which it only has incoming viewing keys,"
|
||||||
"\nbecause spends cannot be detected with incoming viewing keys.\n"
|
"\nthe returned private balance may be larger than the actual balance, because spends cannot"
|
||||||
|
"\nbe detected with incoming viewing keys.\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\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"
|
"2. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress' and 'z_importviewingkey')\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" \"transparent\": xxxxx, (numeric) the total balance of transparent funds\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"
|
" \"total\": xxxxx, (numeric) the total balance of both transparent and private funds\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
@@ -4247,11 +4253,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
|||||||
|
|
||||||
if (useAny || useAnyNote || zaddrs.size() > 0) {
|
if (useAny || useAnyNote || zaddrs.size() > 0) {
|
||||||
// Get available notes
|
// Get available notes
|
||||||
std::vector<CSproutNotePlaintextEntry> entries;
|
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||||
pwalletMain->GetFilteredNotes(entries, zaddrs);
|
std::vector<SaplingNoteEntry> saplingEntries;
|
||||||
|
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs);
|
||||||
|
|
||||||
// Find unspent notes and update estimated size
|
// Find unspent notes and update estimated size
|
||||||
for (CSproutNotePlaintextEntry& entry : entries) {
|
for (CSproutNotePlaintextEntry& entry : sproutEntries) {
|
||||||
noteCounter++;
|
noteCounter++;
|
||||||
CAmount nValue = entry.plaintext.value();
|
CAmount nValue = entry.plaintext.value();
|
||||||
|
|
||||||
@@ -4265,8 +4272,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
|||||||
maxedOutNotesFlag = true;
|
maxedOutNotesFlag = true;
|
||||||
} else {
|
} else {
|
||||||
estimatedTxSize += increase;
|
estimatedTxSize += increase;
|
||||||
// TODO: Add Sapling support
|
auto zaddr = entry.address;
|
||||||
auto zaddr = boost::get<SproutPaymentAddress>(entry.address);
|
|
||||||
SproutSpendingKey zkey;
|
SproutSpendingKey zkey;
|
||||||
pwalletMain->GetSproutSpendingKey(zaddr, zkey);
|
pwalletMain->GetSproutSpendingKey(zaddr, zkey);
|
||||||
noteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, 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;
|
remainingNoteValue += nValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: Add Sapling support
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t numUtxos = utxoInputs.size();
|
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.
|
* 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.
|
* 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;
|
std::set<PaymentAddress> filterAddresses;
|
||||||
|
|
||||||
@@ -4099,7 +4105,7 @@ void CWallet::GetFilteredNotes(std::vector<CSproutNotePlaintextEntry> & outEntri
|
|||||||
filterAddresses.insert(DecodePaymentAddress(address));
|
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.
|
* These notes are decrypted and added to the output parameter vector, outEntries.
|
||||||
*/
|
*/
|
||||||
void CWallet::GetFilteredNotes(
|
void CWallet::GetFilteredNotes(
|
||||||
std::vector<CSproutNotePlaintextEntry>& outEntries,
|
std::vector<CSproutNotePlaintextEntry>& sproutEntries,
|
||||||
|
std::vector<SaplingNoteEntry>& saplingEntries,
|
||||||
std::set<PaymentAddress>& filterAddresses,
|
std::set<PaymentAddress>& filterAddresses,
|
||||||
int minDepth,
|
int minDepth,
|
||||||
bool ignoreSpent,
|
bool ignoreSpent,
|
||||||
@@ -4123,10 +4130,6 @@ void CWallet::GetFilteredNotes(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wtx.mapSproutNoteData.size() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto & pair : wtx.mapSproutNoteData) {
|
for (auto & pair : wtx.mapSproutNoteData) {
|
||||||
JSOutPoint jsop = pair.first;
|
JSOutPoint jsop = pair.first;
|
||||||
SproutNoteData nd = pair.second;
|
SproutNoteData nd = pair.second;
|
||||||
@@ -4172,7 +4175,7 @@ void CWallet::GetFilteredNotes(
|
|||||||
hSig,
|
hSig,
|
||||||
(unsigned char) j);
|
(unsigned char) j);
|
||||||
|
|
||||||
outEntries.push_back(CSproutNotePlaintextEntry{jsop, pa, plaintext});
|
sproutEntries.push_back(CSproutNotePlaintextEntry{jsop, pa, plaintext});
|
||||||
|
|
||||||
} catch (const note_decryption_failed &err) {
|
} catch (const note_decryption_failed &err) {
|
||||||
// Couldn't decrypt with this spending key
|
// 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()));
|
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 */
|
/* Find unspent notes filtered by payment address, min depth and max depth */
|
||||||
void CWallet::GetUnspentFilteredNotes(
|
void CWallet::GetUnspentFilteredNotes(
|
||||||
std::vector<CUnspentSproutNotePlaintextEntry>& outEntries,
|
std::vector<CUnspentSproutNotePlaintextEntry>& sproutEntries,
|
||||||
|
std::vector<UnspentSaplingNoteEntry>& saplingEntries,
|
||||||
std::set<PaymentAddress>& filterAddresses,
|
std::set<PaymentAddress>& filterAddresses,
|
||||||
int minDepth,
|
int minDepth,
|
||||||
int maxDepth,
|
int maxDepth,
|
||||||
@@ -4204,10 +4255,6 @@ void CWallet::GetUnspentFilteredNotes(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wtx.mapSproutNoteData.size() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto & pair : wtx.mapSproutNoteData) {
|
for (auto & pair : wtx.mapSproutNoteData) {
|
||||||
JSOutPoint jsop = pair.first;
|
JSOutPoint jsop = pair.first;
|
||||||
SproutNoteData nd = pair.second;
|
SproutNoteData nd = pair.second;
|
||||||
@@ -4248,7 +4295,7 @@ void CWallet::GetUnspentFilteredNotes(
|
|||||||
hSig,
|
hSig,
|
||||||
(unsigned char) j);
|
(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) {
|
} catch (const note_decryption_failed &err) {
|
||||||
// Couldn't decrypt with this spending key
|
// 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()));
|
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;
|
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. */
|
/** A transaction with a merkle branch linking it to the block chain. */
|
||||||
class CMerkleTx : public CTransaction
|
class CMerkleTx : public CTransaction
|
||||||
{
|
{
|
||||||
@@ -1206,21 +1224,24 @@ public:
|
|||||||
void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; }
|
void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; }
|
||||||
|
|
||||||
/* Find notes filtered by payment address, min depth, ability to spend */
|
/* 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,
|
std::string address,
|
||||||
int minDepth=1,
|
int minDepth=1,
|
||||||
bool ignoreSpent=true,
|
bool ignoreSpent=true,
|
||||||
bool ignoreUnspendable=true);
|
bool ignoreUnspendable=true);
|
||||||
|
|
||||||
/* Find notes filtered by payment addresses, min depth, ability to spend */
|
/* 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,
|
std::set<libzcash::PaymentAddress>& filterAddresses,
|
||||||
int minDepth=1,
|
int minDepth=1,
|
||||||
bool ignoreSpent=true,
|
bool ignoreSpent=true,
|
||||||
bool ignoreUnspendable=true);
|
bool ignoreUnspendable=true);
|
||||||
|
|
||||||
/* Find unspent notes filtered by payment address, min depth and max depth */
|
/* 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,
|
std::set<libzcash::PaymentAddress>& filterAddresses,
|
||||||
int minDepth=1,
|
int minDepth=1,
|
||||||
int maxDepth=INT_MAX,
|
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
|
#endif // BITCOIN_WALLET_WALLET_H
|
||||||
|
|||||||
Reference in New Issue
Block a user