Compare commits

...

5 Commits

  1. 45
      src/wallet/rpcwallet.cpp
  2. 40
      src/wallet/wallet.cpp
  3. 30
      src/wallet/wallet.h

45
src/wallet/rpcwallet.cpp

@ -3636,6 +3636,50 @@ UniValue z_listsentbyaddress(const UniValue& params, bool fHelp,const CPubKey&)
return ret;
}
UniValue z_getwitnesscachestats(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() > 0 )
throw runtime_error(
"z_getwitnesscachestats\n"
"\nReturns statistics about the witness cache for zaddrs\n"
"\nResult:\n"
"\njson\n"
"\nExamples:\n"
+ HelpExampleCli("z_getwitnesscachestats","")
+ HelpExampleRpc("z_getwitnesscachestats","")
);
LOCK2(cs_main, pwalletMain->cs_wallet);
uint32_t nMinDepth = 1;
uint64_t total_witnesses = 0;
uint64_t total_zutxos = 0;
std::vector<uint256> nullifiers;
for (auto& wtx : pwalletMain->mapWallet) {
// add all nullifiers in this tx to our list
for (uint32_t i = 0; i < wtx.second.vShieldedSpend.size(); i++) {
nullifiers.emplace_back(wtx.second.vShieldedSpend[i].nullifier);
}
if (wtx.second.GetDepthInMainChain() >= nMinDepth) {
total_zutxos += wtx.second.mapSaplingNoteData.size();
for (mapSaplingNoteData_t::value_type& item : wtx.second.mapSaplingNoteData) {
auto* nd = &(item.second);
total_witnesses += nd->witnesses.size();
}
}
}
UniValue ret(UniValue::VOBJ);
ret.pushKV("total_witnesses", total_witnesses);
ret.pushKV("total_nullifiers", nullifiers.size());
ret.pushKV("total_zutxos", total_zutxos);
ret.pushKV("witnesses_cache_size", pwalletMain->nWitnessCacheSize);
ret.pushKV("max_witnesses_cache_size", (int) WITNESS_CACHE_SIZE);
return ret;
}
UniValue z_getstats(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (!EnsureWalletIsAvailable(fHelp))
@ -8710,6 +8754,7 @@ static const CRPCCommand commands[] =
{ "wallet", "z_getbalance", &z_getbalance, false },
{ "wallet", "z_getbalances", &z_getbalances, false },
{ "wallet", "z_getstats", &z_getstats, true },
{ "wallet", "z_getwitnesscachestats", &z_getwitnesscachestats, true },
{ "wallet", "z_anonsettxdelta", &z_anonsettxdelta, true },
{ "wallet", "z_anonsetblockdelta", &z_anonsetblockdelta, true },
{ "wallet", "z_gettotalbalance", &z_gettotalbalance, false },

40
src/wallet/wallet.cpp

@ -1103,7 +1103,7 @@ int CWallet::VerifyAndSetInitialWitness(const CBlockIndex* pindex, bool witnessO
nd->witnesses.front().append(note_commitment);
}
//Only needed for intial witness
//Only needed for initial witness
if (nd->witnesses.empty()) {
saplingTree.append(note_commitment);
@ -1140,6 +1140,7 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly)
return;
}
std::vector<uint256> nullifiers;
uint256 saplingRoot;
CBlockIndex* pblockindex = chainActive[startHeight];
int height = chainActive.Height();
@ -1172,16 +1173,42 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly)
strprintf("Cannot read block height %d (%s) from disk", pindex->GetHeight(), pindex->GetBlockHash().GetHex()));
}
// block height of the current block being processed
int indexHeight = pblockindex->GetHeight();
for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
if (wtxItem.second.mapSaplingNoteData.empty())
continue;
// ignore any tx that is not yet in a block (i.e. orphaned or in the mempool)
if (wtxItem.second.GetDepthInMainChain() > 0) {
//Sapling
for (mapSaplingNoteData_t::value_type& item : wtxItem.second.mapSaplingNoteData) {
auto* nd = &(item.second);
// update spentness info
for (const auto& nullifier : nullifiers) {
// This is UpdateSpentHeightAndMaybePruneWitnesses ported to our codebase
// If the note has no witnesses, then either the note has not been mined
// (and thus cannot be spent at this height), or has been spent for long
// enough that we will never unspend it. Either way, we can skip the
// spentness check and pruning.
if (nd->witnesses.empty()) continue;
// Update spentness for sapling note data. If a note is in a block and has a nullifier
// it is a spend
if (nd->nullifier.has_value() && nd->nullifier.value() == nullifier) {
LogPrintf("%s: updating spentness for %s spentHeight=%d\n", __func__, nullifier.GetHex().c_str(), nd->spentHeight);
nd->spentHeight = indexHeight;
}
// Prune witnesses for notes spent more than WITNESS_CACHE_SIZE blocks ago,
// so we stop updating their witnesses. This is safe to do because we know
// we won't roll back more than WITNESS_CACHE_SIZE blocks, since it is a
// number larger than the max reorg length
if (nd->spentHeight > 0 && nd->spentHeight + WITNESS_CACHE_SIZE < indexHeight) {
LogPrintf("%s: pruning witnesses for %s at indexHeight=%d with spentHeight=%d\n", __func__, nullifier.GetHex().c_str(), indexHeight, nd->spentHeight);
nd->witnesses.clear();
nd->witnessHeight = -1;
}
}
if (nd->nullifier && nd->witnessHeight == pblockindex->GetHeight() - 1
&& GetSaplingSpendDepth(*item.second.nullifier) <= WITNESS_CACHE_SIZE) {
@ -1195,6 +1222,11 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly)
const uint256& note_commitment = tx.vShieldedOutput[i].cm;
nd->witnesses.front().append(note_commitment);
}
// add all nullifiers in this tx to our list
for (uint32_t i = 0; i < tx.vShieldedSpend.size(); i++) {
//TODO: do we need to make sure it's not already there before adding it?
nullifiers.emplace_back(tx.vShieldedSpend[i].nullifier);
}
}
nd->witnessHeight = pblockindex->GetHeight();
}
@ -1207,9 +1239,7 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly)
break;
pblockindex = chainActive.Next(pblockindex);
}
}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)

30
src/wallet/wallet.h

@ -266,12 +266,6 @@ class SproutNoteData
/**
* Block height corresponding to the most current witness.
*
* When we first create a SaplingNoteData in CWallet::FindMySaplingNotes, this is set to
* -1 as a placeholder. The next time CWallet::ChainTip is called, we can
* determine what height the witness cache for this note is valid for (even
* if no witnesses were cached), and so can set the correct value in
* CWallet::BuildWitnessCache and CWallet::DecrementNoteWitnesses.
*/
int witnessHeight;
@ -301,17 +295,39 @@ public:
* CWallet::BuildWitnessCache and CWallet::DecrementNoteWitnesses.
*/
SaplingNoteData() : witnessHeight {-1}, nullifier() { }
SaplingNoteData() : witnessHeight {-1}, nullifier(), spentHeight(-1) { }
SaplingNoteData(libzcash::SaplingIncomingViewingKey ivk) : ivk {ivk}, witnessHeight {-1}, nullifier() { }
SaplingNoteData(libzcash::SaplingIncomingViewingKey ivk, uint256 n) : ivk {ivk}, witnessHeight {-1}, nullifier(n) { }
/**
* Cached incremental witnesses for spendable Notes.
* Beginning of the list is the most recent witness.
*/
std::list<SaplingWitness> witnesses;
/**
* The height of the most recently-witnessed block for this note.
*
* Set to -1 if the note is unmined, or if the note was spent long enough
* ago that we will never unspend it.
*/
int witnessHeight;
libzcash::SaplingIncomingViewingKey ivk;
boost::optional<uint256> nullifier;
//In Memory Only
bool witnessRootValidated;
/**
* (memory only) Block height at which this note was observed to be spent.
*
* This is used to prune the list of witnesses once we are guaranteed to
* never be unspending the note. If the node is restarted in the window
* between detecting the spend and pruning the witnesses (or before the
* pruning is serialized to disk), then the spentness will likely not be
* re-detected until a rescan is performed (meaning that this note's
* witnesses will continue to be updated, which is only a performance
* rather than a correctness issue).
*/
int spentHeight;
ADD_SERIALIZE_METHODS;

Loading…
Cancel
Save