Browse Source

Parallel Sapling note trial decryption

Can be toggled by -asyncnotedecryption option, enabled by default.

From miodrag: 5803ce2e56
asyncnotedecryption
fekt 3 months ago
parent
commit
c0f9a3359f
  1. 4
      src/init.cpp
  2. 1
      src/main.cpp
  3. 3
      src/main.h
  4. 100
      src/wallet/wallet.cpp
  5. 2
      src/wallet/wallet.h

4
src/init.cpp

@ -396,6 +396,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
strUsage += HelpMessageOpt("-asyncnotedecryption", strprintf(_("Option to toggle parallel Sapling note trial decryption (default: %u)"), DEFAULT_ASYNC_NOTE_DECRYPTION));
strUsage += HelpMessageOpt("-reindex", _("Rebuild block chain index from current blk000??.dat files on startup"));
#if !defined(WIN32)
strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"));
@ -1337,6 +1338,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
fPruneMode = true;
}
fAsyncNoteDecryption = GetBoolArg("-asyncnotedecryption", DEFAULT_ASYNC_NOTE_DECRYPTION);
LogPrintf("Asynchronous Sapling note trial decryption is %s\n", fAsyncNoteDecryption ? "enabled" : "disabled");
RegisterAllCoreRPCCommands(tableRPC);
#ifdef ENABLE_WALLET
bool fDisableWallet = GetBoolArg("-disablewallet", false);

1
src/main.cpp

@ -102,6 +102,7 @@ bool fIsBareMultisigStd = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = true;
bool fCoinbaseEnforcedProtectionEnabled = true;
bool fAsyncNoteDecryption = DEFAULT_ASYNC_NOTE_DECRYPTION;
size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
// If the tip is older than this (in seconds), the node is considered to be in initial block download.

3
src/main.h

@ -114,6 +114,8 @@ static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum length of reject messages. */
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
/** Default for -asyncnotedecryption */
static const bool DEFAULT_ASYNC_NOTE_DECRYPTION = true;
// NSPV enabled?
static const bool DEFAULT_NSPV_PROCESSING = false;
@ -160,6 +162,7 @@ extern bool fZindex;
extern bool fIsBareMultisigStd;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
extern bool fAsyncNoteDecryption;
// TODO: remove this flag by structuring our code such that
// it is unneeded for testing
extern bool fCoinbaseEnforcedProtectionEnabled;

100
src/wallet/wallet.cpp

@ -1619,7 +1619,8 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
return false;
bool fExisted = mapWallet.count(tx.GetHash()) != 0;
if (fExisted && !fUpdate) return false;
auto saplingNoteDataAndAddressesToAdd = FindMySaplingNotes(tx);
std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> saplingNoteDataAndAddressesToAdd;
saplingNoteDataAndAddressesToAdd = fAsyncNoteDecryption ? FindMySaplingNotesAsync(tx, nHeight) : FindMySaplingNotes(tx, nHeight);
auto saplingNoteData = saplingNoteDataAndAddressesToAdd.first;
auto addressesToAdd = saplingNoteDataAndAddressesToAdd.second;
for (const auto &addressToAdd : addressesToAdd) {
@ -1814,6 +1815,103 @@ std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> CWallet::FindMySap
return std::make_pair(noteData, viewingKeysToAdd);
}
static std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> DecryptSaplingNoteWorker(const Consensus::Params &consensus_params, const SaplingIncomingViewingKey &ivk, const OutputDescription &outdesc, const int &height, const uint256 &hash, const uint32_t &i)
{
mapSaplingNoteData_t noteData;
SaplingIncomingViewingKeyMap viewingKeysToAdd;
auto result = SaplingNotePlaintext::decrypt(consensus_params, height, outdesc.encCiphertext, ivk, outdesc.ephemeralKey, outdesc.cmu);
if (result)
{
// We don't cache the nullifier here as computing it requires knowledge of the note position
// in the commitment tree, which can only be determined when the transaction has been mined.
SaplingOutPoint op {hash, i};
SaplingNoteData nd;
nd.ivk = ivk;
noteData.insert(std::make_pair(op, nd));
auto address = ivk.address(result.value().d);
if (address)
{
viewingKeysToAdd.insert(make_pair(address.value(), ivk));
}
}
return std::make_pair(noteData, viewingKeysToAdd);
}
/**
* Finds all output notes in the given transaction that have been sent to
* SaplingPaymentAddresses in this wallet.
*
* It should never be necessary to call this method with a CWalletTx, because
* the result of FindMySaplingNotes (for the addresses available at the time) will
* already have been cached in CWalletTx.mapSaplingNoteData.
*/
std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> CWallet::FindMySaplingNotesAsync(const CTransaction &tx, int height) const
{
mapSaplingNoteData_t noteData;
SaplingIncomingViewingKeyMap viewingKeysToAdd;
if (tx.vShieldedOutput.empty())
{
return std::make_pair(noteData, viewingKeysToAdd);
}
LOCK(cs_KeyStore);
std::set<SaplingIncomingViewingKey> setSaplingIvkToTest;
for (const auto& [ivk, fvk] : mapSaplingFullViewingKeys)
{
setSaplingIvkToTest.insert(ivk);
}
for (const auto& [address, ivk] : mapSaplingIncomingViewingKeys)
{
setSaplingIvkToTest.insert(ivk);
}
if (setSaplingIvkToTest.empty())
{
return std::make_pair(noteData, viewingKeysToAdd);
}
std::vector<std::future<std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap>>> vDecryptFutures;
const Consensus::Params consensus_params = Params().GetConsensus();
uint256 hash = tx.GetHash();
// Protocol Spec: 4.19 Block Chain Scanning (Sapling)
for (uint32_t i = 0; i < tx.vShieldedOutput.size(); ++i)
{
const OutputDescription output = tx.vShieldedOutput[i];
for (SaplingIncomingViewingKey ivk : setSaplingIvkToTest)
{
vDecryptFutures.emplace_back(std::async(std::launch::async, DecryptSaplingNoteWorker, consensus_params, ivk, output, height, hash, i));
}
}
for (auto &fut : vDecryptFutures)
{
auto result_pair = fut.get();
if (!result_pair.first.empty())
{
noteData.insert(result_pair.first.begin(), result_pair.first.end());
for (auto [address, ivk] : result_pair.second)
{
if (mapSaplingIncomingViewingKeys.count(address) == 0)
{
viewingKeysToAdd[address] = ivk;
}
}
}
}
return std::make_pair(noteData, viewingKeysToAdd);
}
bool CWallet::IsSaplingNullifierFromMe(const uint256& nullifier) const
{
{

2
src/wallet/wallet.h

@ -1191,6 +1191,8 @@ public:
std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const;
std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> FindMySaplingNotes(const CTransaction& tx) const;
std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> FindMySaplingNotesAsync(const CTransaction& tx, int height) const;
bool IsSaplingNullifierFromMe(const uint256& nullifier) const;
void GetSaplingNoteWitnesses(

Loading…
Cancel
Save