|
|
@ -4503,120 +4503,6 @@ void CWallet::GetFilteredNotes( |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Find unspent notes filtered by payment address, min depth and max depth */ |
|
|
|
void CWallet::GetUnspentFilteredNotes( |
|
|
|
std::vector<CSproutNotePlaintextEntry>& sproutEntries, |
|
|
|
std::vector<SaplingNoteEntry>& saplingEntries, |
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
for (auto & pair : wtx.mapSproutNoteData) { |
|
|
|
JSOutPoint jsop = pair.first; |
|
|
|
SproutNoteData nd = pair.second; |
|
|
|
SproutPaymentAddress 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 && IsSproutSpent(*nd.nullifier)) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// skip notes where the spending key is not available
|
|
|
|
if (requireSpendingKey && !HaveSproutSpendingKey(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", EncodePaymentAddress(pa))); |
|
|
|
} |
|
|
|
|
|
|
|
// determine amount of funds in the note
|
|
|
|
auto hSig = wtx.vjoinsplit[i].h_sig(*pzcashParams, wtx.joinSplitPubKey); |
|
|
|
try { |
|
|
|
SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( |
|
|
|
decryptor, |
|
|
|
wtx.vjoinsplit[i].ciphertexts[j], |
|
|
|
wtx.vjoinsplit[i].ephemeralKey, |
|
|
|
hSig, |
|
|
|
(unsigned char) j); |
|
|
|
|
|
|
|
sproutEntries.push_back(CSproutNotePlaintextEntry{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", EncodePaymentAddress(pa))); |
|
|
|
} catch (const std::exception &exc) { |
|
|
|
// Unexpected failure
|
|
|
|
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(SaplingNoteEntry { |
|
|
|
op, pa, note, notePt.memo(), wtx.GetDepthInMainChain() }); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//
|
|
|
|
// Shielded key and address generalizations
|
|
|
|
//
|
|
|
|