diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 16c2170cbc6d..5a12586d56c2 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -301,8 +301,6 @@ Threads - DumpAddresses : Dumps IP addresses of nodes to peers.dat. -- ThreadFlushWalletDB : Close the wallet.dat file if it hasn't been used in 500ms. - - ThreadRPCServer : Remote procedure call handler, listens on port 8332 for connections and services them. - BitcoinMiner : Generates PIVs (if wallet is enabled). diff --git a/doc/release-notes.md b/doc/release-notes.md index 026cb3cd37a8..bac5fd8589db 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -49,7 +49,7 @@ Cold-Staking Re-Activation PIVX Core v6.0.0 includes a fix for the vulnerability identified within the cold-staking protocol (see PR [#2258](https://github.com/PIVX-Project/PIVX/pull/2258)). Therefore the feature will be re-enabled on the network, via `SPORK_19`, shortly after the upgrade enforcement. -#### Protocol changes +### Protocol changes A new opcode (`0xd2`) is introduced (see PR [#2275](https://github.com/PIVX-Project/PIVX/pull/2275)). It enforces the same rules as the legacy cold-staking opcode, but without allowing a "free" script for the last output of the transaction. This is in accord with the consensus change introduced with the "Deterministic Masternodes" update, as masternode/budget payments are now outputs of the *coinbase* transaction (rather than the *coinstake*), therefore a "free" output for the coinstake is no longer needed. @@ -57,6 +57,18 @@ The new opcode takes the name of `OP_CHECKCOLDSTAKEVERIFY`, and the legacy opcod Scripts with the old opcode are still accepted on the network (the restriction on the last-output is enforced after the script validation in this case), but the client creates new delegations with the new opcode, by default, after the upgrade enforcement. +Multi-wallet support +-------------------- + +PIVX Core now supports loading multiple, separate wallets (See [PR 2337](https://github.com/PIVX-Project/PIVX/pull/2337)). The wallets are completely separated, with individual balances, keys and received transactions. + +Multi-wallet is enabled by using more than one `-wallet` argument when starting PIVX client, either on the command line or in the pivx.conf config file. + +**In pivx-qt, only the first wallet will be displayed and accessible for creating and signing transactions.** GUI selectable multiple wallets will be supported in a future version. However, even in 6.0 other loaded wallets will remain synchronized to the node's current tip in the background. + +!TODO: update after endpoint support and multi-wallet RPC support + + GUI changes ----------- @@ -101,6 +113,7 @@ Low-level RPC changes - `maximumCount` - a number specifying the minimum number of UTXOs - `minimumSumAmount` - a number specifying the minimum sum value of all UTXOs + #### Show wallet's auto-combine settings in getwalletinfo `getwalletinfo` now has two additional return fields. `autocombine_enabled` (boolean) and `autocombine_threshold` (numeric) that will show the auto-combine threshold and whether or not it is currently enabled. diff --git a/src/budget/budgetmanager.cpp b/src/budget/budgetmanager.cpp index 8520533f0d8a..e7e26d2aff23 100644 --- a/src/budget/budgetmanager.cpp +++ b/src/budget/budgetmanager.cpp @@ -107,13 +107,17 @@ uint256 CBudgetManager::SubmitFinalBudget() // create the collateral tx, send it to the network and return CTransactionRef wtx; // Get our change address - CReserveKey keyChange(pwalletMain); - if (!pwalletMain->CreateBudgetFeeTX(wtx, budgetHash, keyChange, true)) { + if (vpwallets.empty() || !vpwallets[0]) { + LogPrint(BCLog::MNBUDGET,"%s: Wallet not found\n", __func__); + return UINT256_ZERO; + } + CReserveKey keyChange(vpwallets[0]); + if (!vpwallets[0]->CreateBudgetFeeTX(wtx, budgetHash, keyChange, true)) { LogPrint(BCLog::MNBUDGET,"%s: Can't make collateral transaction\n", __func__); return UINT256_ZERO; } // Send the tx to the network - const CWallet::CommitResult& res = pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get()); + const CWallet::CommitResult& res = vpwallets[0]->CommitTransaction(wtx, keyChange, g_connman.get()); if (res.status == CWallet::CommitStatus::OK) { const uint256& collateraltxid = wtx->GetHash(); mapUnconfirmedFeeTx.emplace(budgetHash, collateraltxid); diff --git a/src/init.cpp b/src/init.cpp index 002ff1f954b3..6c748bd99973 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,10 +1,10 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin developers +// Copyright (c) 2009-2021 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2011-2013 The PPCoin developers // Copyright (c) 2013-2014 The NovaCoin Developers // Copyright (c) 2014-2018 The BlackCoin Developers -// Copyright (c) 2015-2020 The PIVX developers +// Copyright (c) 2015-2021 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -58,7 +58,6 @@ #include "zpivchain.h" #ifdef ENABLE_WALLET -#include "wallet/db.h" #include "wallet/wallet.h" #include "wallet/rpcwallet.h" @@ -222,8 +221,9 @@ void PrepareShutdown() StopRPC(); StopHTTPServer(); #ifdef ENABLE_WALLET - if (pwalletMain) - bitdb.Flush(false); + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(false); + } GenerateBitcoins(false, NULL, 0); #endif StopMapPort(); @@ -302,8 +302,9 @@ void PrepareShutdown() evoDb.reset(); } #ifdef ENABLE_WALLET - if (pwalletMain) - bitdb.Flush(true); + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(true); + } #endif if (pEvoNotificationInterface) { @@ -355,8 +356,10 @@ void Shutdown() PrepareShutdown(); } #ifdef ENABLE_WALLET - delete pwalletMain; - pwalletMain = NULL; + for (CWalletRef pwallet : vpwallets) { + delete pwallet; + } + vpwallets.clear(); #endif globalVerifyHandle.reset(); ECC_Stop(); @@ -933,24 +936,6 @@ void InitParameterInteraction() if (gArgs.SoftSetBoolArg("-discover", false)) LogPrintf("%s : parameter interaction: -externalip set -> setting -discover=0\n", __func__); } - - if (gArgs.GetBoolArg("-salvagewallet", false)) { - // Rewrite just private keys: rescan to find transactions - if (gArgs.SoftSetBoolArg("-rescan", true)) - LogPrintf("%s : parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); - } - - int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); - // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); - } - - // -zapwallettxes implies a rescan - if (zapwallettxes != 0) { - if (gArgs.SoftSetBoolArg("-rescan", true)) - LogPrintf("%s : parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); - } } bool InitNUParams() @@ -1334,11 +1319,8 @@ bool AppInitMain() } } -// ********************************************************* Step 5: Backup wallet and verify wallet database integrity +// ********************************************************* Step 5: Verify wallet database integrity #ifdef ENABLE_WALLET - if (!InitAutoBackupWallet()) { - return false; - } if (!CWallet::Verify()) { return false; } @@ -1726,7 +1708,7 @@ bool AppInitMain() mempool.ReadFeeEstimates(est_filein); fFeeEstimatesInitialized = true; -// ********************************************************* Step 8: load wallet +// ********************************************************* Step 8: Backup and Load wallet #ifdef ENABLE_WALLET if (!CWallet::InitLoadWallet()) return false; @@ -1878,14 +1860,15 @@ bool AppInitMain() strBudgetMode = gArgs.GetArg("-budgetvotemode", "auto"); #ifdef ENABLE_WALLET - if (gArgs.GetBoolArg("-mnconflock", DEFAULT_MNCONFLOCK) && pwalletMain) { - LOCK(pwalletMain->cs_wallet); + // use only the first wallet here. This section can be removed after transition to DMN + if (gArgs.GetBoolArg("-mnconflock", DEFAULT_MNCONFLOCK) && !vpwallets.empty() && vpwallets[0]) { + LOCK(vpwallets[0]->cs_wallet); LogPrintf("Locking Masternodes collateral utxo:\n"); uint256 mnTxHash; for (const auto& mne : masternodeConfig.getEntries()) { mnTxHash.SetHex(mne.getTxHash()); COutPoint outpoint = COutPoint(mnTxHash, (unsigned int) std::stoul(mne.getOutputIndex())); - pwalletMain->LockCoin(outpoint); + vpwallets[0]->LockCoin(outpoint); LogPrintf("Locked collateral, MN: %s, tx hash: %s, output index: %s\n", mne.getAlias(), mne.getTxHash(), mne.getOutputIndex()); } @@ -1919,11 +1902,13 @@ bool AppInitMain() #ifdef ENABLE_WALLET { - if (pwalletMain) { - LOCK(pwalletMain->cs_wallet); - LogPrintf("setKeyPool.size() = %u\n", pwalletMain ? pwalletMain->GetKeyPoolSize() : 0); - LogPrintf("mapWallet.size() = %u\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); - LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->GetAddressBookSize() : 0); + int idx = 0; + for (CWalletRef pwallet : vpwallets) { + LogPrintf("Wallet %d\n", idx++); + LOCK(pwallet->cs_wallet); + LogPrintf("setKeyPool.size() = %u\n", pwallet->GetKeyPoolSize()); + LogPrintf("mapWallet.size() = %u\n", pwallet->mapWallet.size()); + LogPrintf("mapAddressBook.size() = %u\n", pwallet->GetAddressBookSize()); } } #endif @@ -1954,22 +1939,21 @@ bool AppInitMain() return UIError(strNodeError); #ifdef ENABLE_WALLET - // Generate coins in the background - if (pwalletMain) - GenerateBitcoins(gArgs.GetBoolArg("-gen", DEFAULT_GENERATE), pwalletMain, gArgs.GetArg("-genproclimit", DEFAULT_GENERATE_PROCLIMIT)); + // Generate coins in the background (disabled on mainnet. use only wallet 0) + if (!vpwallets.empty()) + GenerateBitcoins(gArgs.GetBoolArg("-gen", DEFAULT_GENERATE), vpwallets[0], gArgs.GetArg("-genproclimit", DEFAULT_GENERATE_PROCLIMIT)); #endif // ********************************************************* Step 12: finished #ifdef ENABLE_WALLET - if (pwalletMain) { - uiInterface.InitMessage(_("Reaccepting wallet transactions...")); - pwalletMain->postInitProcess(scheduler); - - // StakeMiner thread disabled by default on regtest - if (gArgs.GetBoolArg("-staking", !Params().IsRegTestNet() && DEFAULT_STAKING)) { - threadGroup.create_thread(std::bind(&ThreadStakeMinter)); - } + uiInterface.InitMessage(_("Reaccepting wallet transactions...")); + for (CWalletRef pwallet : vpwallets) { + pwallet->postInitProcess(scheduler); + } + // StakeMiner thread disabled by default on regtest + if (!vpwallets.empty() && gArgs.GetBoolArg("-staking", !Params().IsRegTestNet() && DEFAULT_STAKING)) { + threadGroup.create_thread(std::bind(&ThreadStakeMinter)); } #endif diff --git a/src/masternode.cpp b/src/masternode.cpp index a4780d0635c1..a668f1008fb8 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -251,7 +251,8 @@ bool CMasternodeBroadcast::Create(const std::string& strService, } std::string strError; - if (!pwalletMain->GetMasternodeVinAndKeys(txin, pubKeyCollateralAddressNew, keyCollateralAddressNew, strTxHash, strOutputIndex, strError)) { + // Use wallet-0 here. Legacy mnb creation can be removed after transition to DMN + if (vpwallets.empty() || !vpwallets[0]->GetMasternodeVinAndKeys(txin, pubKeyCollateralAddressNew, keyCollateralAddressNew, strTxHash, strOutputIndex, strError)) { strErrorRet = strError; // GetMasternodeVinAndKeys logs this error. Only returned for GUI error notification. LogPrint(BCLog::MASTERNODE,"CMasternodeBroadcast::Create -- %s\n", strprintf("Could not allocate txin %s:%s for masternode %s", strTxHash, strOutputIndex, strService)); return false; diff --git a/src/miner.cpp b/src/miner.cpp index afeb089e7be9..6695eef856d4 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -306,8 +306,8 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) void ThreadStakeMinter() { boost::this_thread::interruption_point(); - LogPrintf("ThreadStakeMinter started\n"); - CWallet* pwallet = pwalletMain; + LogPrintf("ThreadStakeMinter started. Using wallet-0\n"); + CWallet* pwallet = vpwallets[0]; try { BitcoinMiner(pwallet, true); boost::this_thread::interruption_point(); diff --git a/src/qt/pivx.cpp b/src/qt/pivx.cpp index b96eddba4208..9608bf12cd68 100644 --- a/src/qt/pivx.cpp +++ b/src/qt/pivx.cpp @@ -487,8 +487,9 @@ void BitcoinApplication::initializeResult(int retval) window->setClientModel(clientModel); #ifdef ENABLE_WALLET - if (pwalletMain) { - walletModel = new WalletModel(pwalletMain, optionsModel); + // TODO: Expose secondary wallets + if (!vpwallets.empty()) { + walletModel = new WalletModel(vpwallets[0], optionsModel); walletModel->setClientModel(clientModel); window->addWallet(PIVXGUI::DEFAULT_WALLET, walletModel); diff --git a/src/rest.cpp b/src/rest.cpp index bfe3f0a021e1..3c15c7cd5704 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -16,6 +16,7 @@ #include "utilstrencodings.h" #include "validation.h" #include "version.h" +#include "wallet/wallet.h" #include @@ -60,7 +61,7 @@ struct CCoin { } }; -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); +extern void TxToJSON(CWallet* const pwallet, const CTransaction& tx, const uint256 hashBlock, UniValue& entry); extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); extern UniValue mempoolInfoToJSON(); extern UniValue mempoolToJSON(bool fVerbose = false); @@ -373,7 +374,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) case RF_JSON: { UniValue objTx(UniValue::VOBJ); - TxToJSON(*tx, hashBlock, objTx); + TxToJSON(nullptr, *tx, hashBlock, objTx); std::string strJSON = objTx.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a3d8d975e68a..bdc5461fed9d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -42,7 +42,7 @@ static std::mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; -extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); +extern void TxToJSON(CWallet* const pwallet, const CTransaction& tx, const uint256 hashBlock, UniValue& entry); UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request) { @@ -147,7 +147,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx const CTransaction& tx = *txIn; if (txDetails) { UniValue objTx(UniValue::VOBJ); - TxToJSON(tx, UINT256_ZERO, objTx); + TxToJSON(nullptr, tx, UINT256_ZERO, objTx); txs.push_back(objTx); } else txs.push_back(tx.GetHash().GetHex()); diff --git a/src/rpc/budget.cpp b/src/rpc/budget.cpp index 071a894a6b3b..7fbdf7d4d1f9 100644 --- a/src/rpc/budget.cpp +++ b/src/rpc/budget.cpp @@ -15,6 +15,9 @@ #include "messagesigner.h" #include "rpc/server.h" #include "utilmoneystr.h" +#ifdef ENABLE_WALLET +#include "wallet/rpcwallet.h" +#endif #include @@ -97,6 +100,11 @@ void checkBudgetInputs(const UniValue& params, std::string &strProposalName, std UniValue preparebudget(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 6) throw std::runtime_error( "preparebudget \"proposal-name\" \"url\" payment-count block-start \"pivx-address\" monthy-payment\n" @@ -117,13 +125,9 @@ UniValue preparebudget(const JSONRPCRequest& request) HelpExampleCli("preparebudget", "\"test-proposal\" \"https://forum.pivx.org/t/test-proposal\" 2 820800 \"D9oc6C3dttUbv8zd7zGNq1qKBGf4ZQ1XEE\" 500") + HelpExampleRpc("preparebudget", "\"test-proposal\" \"https://forum.pivx.org/t/test-proposal\" 2 820800 \"D9oc6C3dttUbv8zd7zGNq1qKBGf4ZQ1XEE\" 500")); - if (!pwalletMain) { - throw JSONRPCError(RPC_IN_WARMUP, "Try again after active chain is loaded"); - } - - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); std::string strProposalName; std::string strURL; @@ -145,19 +149,19 @@ UniValue preparebudget(const JSONRPCRequest& request) CTransactionRef wtx; // make our change address - CReserveKey keyChange(pwalletMain); - if (!pwalletMain->CreateBudgetFeeTX(wtx, nHash, keyChange, false)) { // 50 PIV collateral for proposal + CReserveKey keyChange(pwallet); + if (!pwallet->CreateBudgetFeeTX(wtx, nHash, keyChange, false)) { // 50 PIV collateral for proposal throw std::runtime_error("Error making collateral transaction for proposal. Please check your wallet balance."); } //send the tx to the network - const CWallet::CommitResult& res = pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get()); + const CWallet::CommitResult& res = pwallet->CommitTransaction(wtx, keyChange, g_connman.get()); if (res.status != CWallet::CommitStatus::OK) throw JSONRPCError(RPC_WALLET_ERROR, res.ToString()); // Store proposal name as a comment - assert(pwalletMain->mapWallet.count(wtx->GetHash())); - pwalletMain->mapWallet.at(wtx->GetHash()).SetComment("Proposal: " + strProposalName); + assert(pwallet->mapWallet.count(wtx->GetHash())); + pwallet->mapWallet.at(wtx->GetHash()).SetComment("Proposal: " + strProposalName); return wtx->GetHash().ToString(); } diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 4bdcd83d70de..a91bf2812b30 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -14,6 +14,9 @@ #include "netbase.h" #include "rpc/server.h" #include "utilmoneystr.h" +#ifdef ENABLE_WALLET +#include "wallet/rpcwallet.h" +#endif #include @@ -355,6 +358,11 @@ UniValue startmasternode(const JSONRPCRequest& request) throw JSONRPCError(RPC_MISC_ERROR, "startmasternode is not supported when deterministic masternode list is active (DIP3)"); } + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + std::string strCommand; if (request.params.size() >= 1) { strCommand = request.params[0].get_str(); @@ -402,7 +410,7 @@ UniValue startmasternode(const JSONRPCRequest& request) bool fLock = (request.params[1].get_str() == "true" ? true : false); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); if (strCommand == "local") { if (!fMasterNode) throw std::runtime_error("you must set masternode=1 in the configuration\n"); @@ -410,7 +418,7 @@ UniValue startmasternode(const JSONRPCRequest& request) if (activeMasternode.GetStatus() != ACTIVE_MASTERNODE_STARTED) { activeMasternode.ResetStatus(); if (fLock) - pwalletMain->Lock(); + pwallet->Lock(); } return activeMasternode.GetStatusMessage(); @@ -439,7 +447,7 @@ UniValue startmasternode(const JSONRPCRequest& request) RelayMNB(mnb, fSuccess, successful, failed); } if (fLock) - pwalletMain->Lock(); + pwallet->Lock(); UniValue returnObj(UniValue::VOBJ); returnObj.pushKV("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", successful, failed, successful + failed)); @@ -479,7 +487,7 @@ UniValue startmasternode(const JSONRPCRequest& request) } if (fLock) - pwalletMain->Lock(); + pwallet->Lock(); if(!found) { statusObj.pushKV("success", false); @@ -512,6 +520,11 @@ UniValue createmasternodekey (const JSONRPCRequest& request) UniValue getmasternodeoutputs (const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || (request.params.size() != 0)) throw std::runtime_error( "getmasternodeoutputs\n" @@ -534,7 +547,7 @@ UniValue getmasternodeoutputs (const JSONRPCRequest& request) coinsFilter.fIncludeDelegated = false; coinsFilter.nCoinType = ONLY_10000; std::vector possibleCoins; - pwalletMain->AvailableCoins(&possibleCoins, nullptr, coinsFilter); + pwallet->AvailableCoins(&possibleCoins, nullptr, coinsFilter); UniValue ret(UniValue::VARR); for (COutput& out : possibleCoins) { @@ -807,6 +820,11 @@ bool DecodeHexMnb(CMasternodeBroadcast& mnb, std::string strHexMnb) { } UniValue createmasternodebroadcast(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + std::string strCommand; if (request.params.size() >= 1) strCommand = request.params[0].get_str(); @@ -814,7 +832,7 @@ UniValue createmasternodebroadcast(const JSONRPCRequest& request) throw std::runtime_error( "createmasternodebroadcast \"command\" ( \"alias\")\n" "\nCreates a masternode broadcast message for one or all masternodes configured in masternode.conf\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"command\" (string, required) \"alias\" for single masternode, \"all\" for all masternodes\n" @@ -845,7 +863,7 @@ UniValue createmasternodebroadcast(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("createmasternodebroadcast", "alias mymn1") + HelpExampleRpc("createmasternodebroadcast", "alias mymn1")); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); if (strCommand == "alias") { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2fe468f85006..5944b52547ed 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -17,6 +17,7 @@ #include "rpc/server.h" #include "validationinterface.h" #ifdef ENABLE_WALLET +#include "wallet/rpcwallet.h" #include "wallet/db.h" #include "wallet/wallet.h" #endif @@ -27,7 +28,7 @@ #ifdef ENABLE_WALLET UniValue generateBlocks(const Consensus::Params& consensus, - CWallet* wallet, + CWallet* const pwallet, bool fPoS, const int nGenerate, int nHeight, @@ -40,13 +41,13 @@ UniValue generateBlocks(const Consensus::Params& consensus, // Get available coins std::vector availableCoins; - if (fPoS && !wallet->StakeableCoins(&availableCoins)) { + if (fPoS && !pwallet->StakeableCoins(&availableCoins)) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "No available coins to stake"); } std::unique_ptr pblocktemplate(fPoS ? - BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), wallet, true, &availableCoins) : - CreateNewBlockWithScript(*coinbaseScript, wallet)); + BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), pwallet, true, &availableCoins) : + CreateNewBlockWithScript(*coinbaseScript, pwallet)); if (!pblocktemplate.get()) break; std::shared_ptr pblock = std::make_shared(pblocktemplate->block); @@ -76,6 +77,11 @@ UniValue generateBlocks(const Consensus::Params& consensus, UniValue generate(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) throw std::runtime_error( "generate numblocks\n" @@ -113,10 +119,10 @@ UniValue generate(const JSONRPCRequest& request) if (fPoS) { // If we are in PoS, wallet must be unlocked. - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); } else { // Coinbase key - reservekey = MakeUnique(pwalletMain); + reservekey = MakeUnique(pwallet); CPubKey pubkey; if (!reservekey->GetReservedKey(pubkey)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Error: Cannot get key from keypool"); coinbaseScript = GetScriptForDestination(pubkey.GetID()); @@ -124,7 +130,7 @@ UniValue generate(const JSONRPCRequest& request) // Create the blocks UniValue blockHashes = generateBlocks(consensus, - pwalletMain, + pwallet, fPoS, nGenerate, nHeight, @@ -142,6 +148,11 @@ UniValue generate(const JSONRPCRequest& request) UniValue generatetoaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 2) throw std::runtime_error( "generatetoaddress numblocks \"address\"\n" @@ -175,7 +186,7 @@ UniValue generatetoaddress(const JSONRPCRequest& request) bool fPoS = consensus.NetworkUpgradeActive(nHeight + 1, Consensus::UPGRADE_POS); return generateBlocks(consensus, - pwalletMain, + pwallet, fPoS, nGenerate, nHeight, @@ -273,6 +284,11 @@ UniValue getgenerate(const JSONRPCRequest& request) UniValue setgenerate(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "setgenerate generate ( genproclimit )\n" @@ -291,8 +307,6 @@ UniValue setgenerate(const JSONRPCRequest& request) "\nTurn off generation\n" + HelpExampleCli("setgenerate", "false") + "\nUsing json rpc\n" + HelpExampleRpc("setgenerate", "true, 1")); - EnsureWallet(); - if (Params().IsRegTestNet()) throw JSONRPCError(RPC_INVALID_REQUEST, "Use the generate method instead of setgenerate on regtest"); @@ -313,7 +327,7 @@ UniValue setgenerate(const JSONRPCRequest& request) gArgs.GetArg("-gen", "") = (fGenerate ? "1" : "0"); gArgs.GetArg("-genproclimit", "") = itostr(nGenProcLimit); - GenerateBitcoins(fGenerate, pwalletMain, nGenProcLimit); + GenerateBitcoins(fGenerate, pwallet, nGenProcLimit); return NullUniValue; } @@ -438,6 +452,8 @@ static UniValue BIP22ValidationResult(const CValidationState& state) #ifdef ENABLE_MINING_RPC UniValue getblocktemplate(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "getblocktemplate ( \"jsonrequestobject\" )\n" @@ -623,7 +639,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) pblocktemplate = NULL; } CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptDummy, pwalletMain, false); + pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptDummy, pwallet, false); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 7ae80a8d0235..bd7ec3e0e73b 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -18,6 +18,7 @@ #include "timedata.h" #include "util/system.h" #ifdef ENABLE_WALLET +#include "wallet/rpcwallet.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" #endif @@ -47,6 +48,8 @@ UniValue getsupplyinfo(const JSONRPCRequest& request); **/ UniValue getinfo(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (request.fHelp || request.params.size() != 0) throw std::runtime_error( "getinfo\n" @@ -83,7 +86,7 @@ UniValue getinfo(const JSONRPCRequest& request) HelpExampleCli("getinfo", "") + HelpExampleRpc("getinfo", "")); #ifdef ENABLE_WALLET - LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); + LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : NULL); #else LOCK(cs_main); #endif @@ -114,10 +117,10 @@ UniValue getinfo(const JSONRPCRequest& request) obj.pushKV("protocolversion", PROTOCOL_VERSION); obj.pushKV("services", services); #ifdef ENABLE_WALLET - if (pwalletMain) { - obj.pushKV("walletversion", pwalletMain->GetVersion()); - obj.pushKV("balance", ValueFromAmount(pwalletMain->GetAvailableBalance())); - obj.pushKV("staking status", (pwalletMain->pStakerStatus->IsActive() ? "Staking Active" : "Staking Not Active")); + if (pwallet) { + obj.pushKV("walletversion", pwallet->GetVersion()); + obj.pushKV("balance", ValueFromAmount(pwallet->GetAvailableBalance())); + obj.pushKV("staking status", (pwallet->pStakerStatus->IsActive() ? "Staking Active" : "Staking Not Active")); } #endif obj.pushKV("blocks", (int)chainActive.Height()); @@ -135,13 +138,13 @@ UniValue getinfo(const JSONRPCRequest& request) obj.pushKV("shieldsupply", supply_info["shieldsupply"]); #ifdef ENABLE_WALLET - if (pwalletMain) { - obj.pushKV("keypoololdest", pwalletMain->GetOldestKeyPoolTime()); - size_t kpExternalSize = pwalletMain->KeypoolCountExternalKeys(); + if (pwallet) { + obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime()); + size_t kpExternalSize = pwallet->KeypoolCountExternalKeys(); obj.pushKV("keypoolsize", (int64_t)kpExternalSize); } - if (pwalletMain && pwalletMain->IsCrypted()) - obj.pushKV("unlocked_until", nWalletUnlockTime); + if (pwallet && pwallet->IsCrypted()) + obj.pushKV("unlocked_until", pwallet->nRelockTime); obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); #endif obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); @@ -227,7 +230,9 @@ class DescribeAddressVisitor : public boost::static_visitor isminetype mine; public: - DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {} + CWallet * const pwallet; + + DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {} UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } @@ -235,8 +240,7 @@ class DescribeAddressVisitor : public boost::static_visitor UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; obj.pushKV("isscript", false); - if (bool(mine & ISMINE_ALL)) { - pwalletMain->GetPubKey(keyID, vchPubKey); + if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { obj.pushKV("pubkey", HexStr(vchPubKey)); obj.pushKV("iscompressed", vchPubKey.IsCompressed()); } @@ -247,19 +251,20 @@ class DescribeAddressVisitor : public boost::static_visitor UniValue obj(UniValue::VOBJ); obj.pushKV("isscript", true); CScript subscript; - pwalletMain->GetCScript(scriptID, subscript); - std::vector addresses; - txnouttype whichType; - int nRequired; - ExtractDestinations(subscript, whichType, addresses, nRequired); - obj.pushKV("script", GetTxnOutputType(whichType)); - obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); - UniValue a(UniValue::VARR); - for (const CTxDestination& addr : addresses) - a.push_back(EncodeDestination(addr)); - obj.pushKV("addresses", a); - if (whichType == TX_MULTISIG) - obj.pushKV("sigsrequired", nRequired); + if (pwallet && pwallet->GetCScript(scriptID, subscript)) { + std::vector addresses; + txnouttype whichType; + int nRequired; + ExtractDestinations(subscript, whichType, addresses, nRequired); + obj.pushKV("script", GetTxnOutputType(whichType)); + obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); + UniValue a(UniValue::VARR); + for (const CTxDestination& addr : addresses) + a.push_back(EncodeDestination(addr)); + obj.pushKV("addresses", a); + if (whichType == TX_MULTISIG) + obj.pushKV("sigsrequired", nRequired); + } return obj; } }; @@ -334,7 +339,7 @@ typedef boost::variant { public: - explicit DescribePaymentAddressVisitor(bool _isStaking) : isStaking(_isStaking) {} + explicit DescribePaymentAddressVisitor(CWallet *_pwallet, bool _isStaking) : pwallet(_pwallet), isStaking(_isStaking) {} UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); } UniValue operator()(const libzcash::SaplingPaymentAddress &zaddr) const { @@ -342,8 +347,8 @@ class DescribePaymentAddressVisitor : public boost::static_visitor obj.pushKV("diversifier", HexStr(zaddr.d)); obj.pushKV("diversifiedtransmissionkey", zaddr.pk_d.GetHex()); #ifdef ENABLE_WALLET - if (pwalletMain) { - obj.pushKV("ismine", pwalletMain->HaveSpendingKeyForPaymentAddress(zaddr)); + if (pwallet) { + obj.pushKV("ismine", pwallet->HaveSpendingKeyForPaymentAddress(zaddr)); } #endif return obj; @@ -355,24 +360,27 @@ class DescribePaymentAddressVisitor : public boost::static_visitor ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); #ifdef ENABLE_WALLET - isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; + isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO; ret.pushKV("ismine", bool(mine & (ISMINE_SPENDABLE_ALL | ISMINE_COLD))); ret.pushKV("isstaking", isStaking); ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); - UniValue detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); + UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest); ret.pushKVs(detail); - if (pwalletMain && pwalletMain->HasAddressBook(dest)) - ret.pushKV("label", pwalletMain->GetNameForAddressBookEntry(dest)); + if (pwallet && pwallet->HasAddressBook(dest)) + ret.pushKV("label", pwallet->GetNameForAddressBookEntry(dest)); #endif return ret; } private: + CWallet * const pwallet; bool isStaking{false}; }; UniValue validateaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "validateaddress \"pivxaddress\"\n" @@ -405,7 +413,7 @@ UniValue validateaddress(const JSONRPCRequest& request) HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")); #ifdef ENABLE_WALLET - LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : nullptr); + LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); #else LOCK(cs_main); #endif @@ -429,7 +437,7 @@ UniValue validateaddress(const JSONRPCRequest& request) ret.pushKV("isvalid", isValid); if (isValid) { ret.pushKV("address", strAddress); - UniValue detail = boost::apply_visitor(DescribePaymentAddressVisitor(isStakingAddress), finalAddress); + UniValue detail = boost::apply_visitor(DescribePaymentAddressVisitor(pwallet, isStakingAddress), finalAddress); ret.pushKVs(detail); } @@ -439,7 +447,7 @@ UniValue validateaddress(const JSONRPCRequest& request) /** * Used by addmultisigaddress / createmultisig: */ -CScript _createmultisig_redeemScript(const UniValue& params) +CScript _createmultisig_redeemScript(CWallet * const pwallet, const UniValue& params) { int nRequired = params[0].get_int(); const UniValue& keys = params[1].get_array(); @@ -461,14 +469,14 @@ CScript _createmultisig_redeemScript(const UniValue& params) #ifdef ENABLE_WALLET // Case 1: PIVX address and we have full public key: CTxDestination dest = DecodeDestination(ks); - if (pwalletMain && IsValidDestination(dest)) { + if (pwallet && IsValidDestination(dest)) { const CKeyID* keyID = boost::get(&dest); if (!keyID) { throw std::runtime_error( strprintf("%s does not refer to a key", ks)); } CPubKey vchPubKey; - if (!pwalletMain->GetPubKey(*keyID, vchPubKey)) + if (!pwallet->GetPubKey(*keyID, vchPubKey)) throw std::runtime_error( strprintf("no full public key for address %s", ks)); if (!vchPubKey.IsFullyValid()) @@ -499,6 +507,8 @@ CScript _createmultisig_redeemScript(const UniValue& params) UniValue createmultisig(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) throw std::runtime_error( "createmultisig nrequired [\"key\",...]\n" @@ -526,7 +536,7 @@ UniValue createmultisig(const JSONRPCRequest& request) HelpExampleRpc("createmultisig", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")); // Construct using pay-to-script-hash: - CScript inner = _createmultisig_redeemScript(request.params); + CScript inner = _createmultisig_redeemScript(pwallet, request.params); CScriptID innerID(inner); UniValue result(UniValue::VOBJ); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index a5b526ff89fc..d1d8f6212591 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -25,6 +25,7 @@ #ifdef ENABLE_WALLET #include "sapling/address.h" #include "sapling/key_io_sapling.h" +#include "wallet/rpcwallet.h" #include "wallet/wallet.h" #endif @@ -54,7 +55,8 @@ static void PayloadToJSON(const CTransaction& tx, UniValue& entry) } } -void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) +// pwallet can be nullptr. If not null, the json could include information available only to the wallet. +void TxToJSON(CWallet* const pwallet, const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { // Call into TxToUniv() in bitcoin-common to decode the transaction hex. // @@ -64,11 +66,11 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) TxToUniv(tx, uint256(), entry); // Sapling - if (pwalletMain && tx.IsShieldedTx()) { + if (pwallet && tx.IsShieldedTx()) { // Add information that only this wallet knows about the transaction if is possible - if (pwalletMain->HasSaplingSPKM()) { + if (pwallet->HasSaplingSPKM()) { std::vector addresses = - pwalletMain->GetSaplingScriptPubKeyMan()->FindMySaplingAddresses(tx); + pwallet->GetSaplingScriptPubKeyMan()->FindMySaplingAddresses(tx); UniValue addrs(UniValue::VARR); for (const auto& addr : addresses) { addrs.push_back(KeyIO::EncodePaymentAddress(addr)); @@ -252,7 +254,8 @@ UniValue getrawtransaction(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); if (blockindex) result.pushKV("in_active_chain", in_active_chain); - TxToJSON(*tx, hash_block, result); + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + TxToJSON(pwallet, *tx, hash_block, result); return result; } @@ -420,7 +423,8 @@ UniValue decoderawtransaction(const JSONRPCRequest& request) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); UniValue result(UniValue::VOBJ); - TxToJSON(CTransaction(std::move(mtx)), UINT256_ZERO, result); + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + TxToJSON(pwallet, CTransaction(std::move(mtx)), UINT256_ZERO, result); return result; } @@ -482,6 +486,11 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std:: UniValue fundrawtransaction(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "fundrawtransaction \"hexstring\" ( options )\n" @@ -523,12 +532,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") ); - if (!pwalletMain) - throw std::runtime_error("wallet not initialized"); - // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); RPCTypeCheck(request.params, {UniValue::VSTR}); @@ -588,7 +594,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) CMutableTransaction tx(origTx); CAmount nFeeOut; std::string strFailReason; - if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, changeAddress)) + if(!pwallet->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, changeAddress)) throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); UniValue result(UniValue::VOBJ); @@ -601,6 +607,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) UniValue signrawtransaction(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" @@ -610,7 +618,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) "The third optional argument (may be null) is an array of base58-encoded private\n" "keys that, if given, will be the only keys used to sign the transaction.\n" #ifdef ENABLE_WALLET - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" #endif "\nArguments:\n" @@ -659,7 +667,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) HelpExampleCli("signrawtransaction", "\"myhex\"") + HelpExampleRpc("signrawtransaction", "\"myhex\"")); #ifdef ENABLE_WALLET - LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); + LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : NULL); #else LOCK(cs_main); #endif @@ -727,8 +735,8 @@ UniValue signrawtransaction(const JSONRPCRequest& request) } } #ifdef ENABLE_WALLET - else if (pwalletMain) - EnsureWalletIsUnlocked(); + else if (pwallet) + EnsureWalletIsUnlocked(pwallet); #endif // Add previous txouts given in the RPC call: @@ -798,7 +806,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) } #ifdef ENABLE_WALLET - const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); + const CKeyStore& keystore = ((fGivenKeys || !pwallet) ? tempKeystore : *pwallet); #else const CKeyStore& keystore = tempKeystore; #endif diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 4e83f4deccad..a593bcae1a4d 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -272,10 +272,9 @@ static std::string TxInErrorToString(int i, const CTxIn& txin, const std::string return strprintf("Input %d (%s): %s", i, txin.prevout.ToStringShort(), strError); } -static OperationResult SignTransaction(CMutableTransaction& tx) +static OperationResult SignTransaction(CWallet* const pwallet, CMutableTransaction& tx) { - EnsureWalletIsUnlocked(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); const CTransaction txConst(tx); for (unsigned int i = 0; i < tx.vin.size(); i++) { CTxIn& txin = tx.vin[i]; @@ -286,7 +285,7 @@ static OperationResult SignTransaction(CMutableTransaction& tx) SigVersion sv = tx.GetRequiredSigVersion(); txin.scriptSig.clear(); SignatureData sigdata; - if (!ProduceSignature(MutableTransactionSignatureCreator(pwalletMain, &tx, i, coin.out.nValue, SIGHASH_ALL), + if (!ProduceSignature(MutableTransactionSignatureCreator(pwallet, &tx, i, coin.out.nValue, SIGHASH_ALL), coin.out.scriptPubKey, sigdata, sv, false)) { return errorOut(TxInErrorToString(i, txin, "signature failed")); } @@ -295,11 +294,8 @@ static OperationResult SignTransaction(CMutableTransaction& tx) return OperationResult(true); } -static std::string SignAndSendSpecialTx(CMutableTransaction& tx, const ProRegPL& pl) +static std::string SignAndSendSpecialTx(CWallet* const pwallet, CMutableTransaction& tx, const ProRegPL& pl) { - EnsureWallet(); - EnsureWalletIsUnlocked(); - SetTxPayload(tx, pl); CValidationState state; @@ -307,7 +303,7 @@ static std::string SignAndSendSpecialTx(CMutableTransaction& tx, const ProRegPL& throw JSONRPCError(RPC_MISC_ERROR, FormatStateMessage(state)); } - const OperationResult& sigRes = SignTransaction(tx); + const OperationResult& sigRes = SignTransaction(pwallet, tx); if (!sigRes) { throw JSONRPCError(RPC_INTERNAL_ERROR, sigRes.getError()); } @@ -321,7 +317,6 @@ static std::string SignAndSendSpecialTx(CMutableTransaction& tx, const ProRegPL& // Parses inputs (starting from index paramIdx) and returns ProReg payload static ProRegPL ParseProRegPLParams(const UniValue& params, unsigned int paramIdx) { - assert(pwalletMain); assert(params.size() > paramIdx + 4); assert(params.size() < paramIdx + 8); ProRegPL pl; @@ -379,6 +374,11 @@ static ProRegPL ParseProRegPLParams(const UniValue& params, unsigned int paramId // handles protx_register, and protx_register_prepare static UniValue ProTxRegister(const JSONRPCRequest& request, bool fSignAndSend) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 7 || request.params.size() > 9) { throw std::runtime_error( (fSignAndSend ? @@ -391,7 +391,7 @@ static UniValue ProTxRegister(const JSONRPCRequest& request, bool fSignAndSend) "key and then passed to \"protx_register_submit\".\n" "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent transaction output.\n" ) - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" + GetHelpString(1, collateralHash) + GetHelpString(2, collateralIndex) @@ -421,11 +421,10 @@ static UniValue ProTxRegister(const JSONRPCRequest& request, bool fSignAndSend) } if (fSignAndSend) CheckEvoUpgradeEnforcement(); - EnsureWallet(); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); const uint256& collateralHash = ParseHashV(request.params[0], "collateralHash"); const int32_t collateralIndex = request.params[1].get_int(); @@ -459,19 +458,19 @@ static UniValue ProTxRegister(const JSONRPCRequest& request, bool fSignAndSend) throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("collateral type not supported: %s-%d", collateralHash.ToString(), collateralIndex)); } CKey keyCollateral; - if (fSignAndSend && !pwalletMain->GetKey(*keyID, keyCollateral)) { + if (fSignAndSend && !pwallet->GetKey(*keyID, keyCollateral)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest))); } // make sure fee calculation works pl.vchSig.resize(CPubKey::COMPACT_SIGNATURE_SIZE); - FundSpecialTx(pwalletMain, tx, pl); + FundSpecialTx(pwallet, tx, pl); if (fSignAndSend) { SignSpecialTxPayloadByString(pl, keyCollateral); // prove we own the collateral // check the payload, add the tx inputs sigs, and send the tx. - return SignAndSendSpecialTx(tx, pl); + return SignAndSendSpecialTx(pwallet, tx, pl); } // external signing with collateral key pl.vchSig.clear(); @@ -495,12 +494,17 @@ UniValue protx_register_prepare(const JSONRPCRequest& request) UniValue protx_register_submit(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "protx_register_submit \"tx\" \"sig\"\n" "\nSubmits the specified ProTx to the network. This command will also sign the inputs of the transaction\n" "which were previously added by \"protx_register_prepare\" to cover transaction fees\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"tx\" (string, required) The serialized transaction previously returned by \"protx_register_prepare\"\n" "2. \"sig\" (string, required) The signature signed with the collateral key. Must be in base64 format.\n" @@ -512,11 +516,10 @@ UniValue protx_register_submit(const JSONRPCRequest& request) } CheckEvoUpgradeEnforcement(); - EnsureWallet(); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); CMutableTransaction tx; if (!DecodeHexTx(tx, request.params[0].get_str())) { @@ -536,17 +539,22 @@ UniValue protx_register_submit(const JSONRPCRequest& request) pl.vchSig = DecodeBase64(request.params[1].get_str().c_str()); // check the payload, add the tx inputs sigs, and send the tx. - return SignAndSendSpecialTx(tx, pl); + return SignAndSendSpecialTx(pwallet, tx, pl); } UniValue protx_register_fund(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 6 || request.params.size() > 8) { throw std::runtime_error( "protx_register_fund \"collateralAddress\" \"ipAndPort\" \"ownerAddress\" \"operatorAddress\" \"votingAddress\" \"payoutAddress\" (operatorReward \"operatorPayoutAddress\")\n" "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 10000 PIV\n" "to the address specified by collateralAddress and will then function as masternode collateral.\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" + GetHelpString(1, collateralAddress) + GetHelpString(2, ipAndPort_register) @@ -564,11 +572,10 @@ UniValue protx_register_fund(const JSONRPCRequest& request) } CheckEvoUpgradeEnforcement(); - EnsureWallet(); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); const CTxDestination& collateralDest(ParsePubKeyIDFromAddress(request.params[0].get_str())); const CScript& collateralScript = GetScriptForDestination(collateralDest); @@ -582,7 +589,7 @@ UniValue protx_register_fund(const JSONRPCRequest& request) tx.nType = CTransaction::TxType::PROREG; tx.vout.emplace_back(collAmt, collateralScript); - FundSpecialTx(pwalletMain, tx, pl); + FundSpecialTx(pwallet, tx, pl); for (uint32_t i = 0; i < tx.vout.size(); i++) { if (tx.vout[i].nValue == collAmt && tx.vout[i].scriptPubKey == collateralScript) { @@ -594,7 +601,7 @@ UniValue protx_register_fund(const JSONRPCRequest& request) // update payload on tx (with final collateral outpoint) pl.vchSig.clear(); // check the payload, add the tx inputs sigs, and send the tx. - return SignAndSendSpecialTx(tx, pl); + return SignAndSendSpecialTx(pwallet, tx, pl); } #endif //ENABLE_WALLET @@ -704,7 +711,7 @@ UniValue protx_list(const JSONRPCRequest& request) CheckEvoUpgradeEnforcement(); #ifdef ENABLE_WALLET - CWallet* const pwallet = pwalletMain; + CWallet* const pwallet = GetWalletForJSONRPCRequest(request); #else CWallet* const pwallet = nullptr; #endif diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 51ac2adfe0da..8b688648ed08 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -188,7 +188,7 @@ bool ParseBool(const UniValue& o, std::string strKey) * Note: This interface may still be subject to change. */ -std::string CRPCTable::help(std::string strCommand) const +std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const { std::string strRet; std::string category; @@ -199,14 +199,17 @@ std::string CRPCTable::help(std::string strCommand) const vCommands.emplace_back(entry.second->category + entry.first, entry.second); std::sort(vCommands.begin(), vCommands.end()); + JSONRPCRequest jreq(helpreq); + jreq.fHelp = true; + jreq.params = UniValue(); + for (const std::pair& command : vCommands) { const CRPCCommand* pcmd = command.second; std::string strMethod = pcmd->name; if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) continue; + jreq.strMethod = strMethod; try { - JSONRPCRequest jreq; - jreq.fHelp = true; rpcfn_type pfn = pcmd->actor; if (setDone.insert(pfn).second) (*pfn)(jreq); @@ -248,7 +251,7 @@ UniValue help(const JSONRPCRequest& jsonRequest) if (jsonRequest.params.size() > 0) strCommand = jsonRequest.params[0].get_str(); - return tableRPC.help(strCommand); + return tableRPC.help(strCommand, jsonRequest); } diff --git a/src/rpc/server.h b/src/rpc/server.h index 37f7ba51aaec..89739374a4ab 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -151,7 +151,7 @@ class CRPCTable public: CRPCTable(); const CRPCCommand* operator[](const std::string& name) const; - std::string help(std::string name) const; + std::string help(const std::string& name, const JSONRPCRequest& helpreq) const; /** * Execute a method. @@ -190,18 +190,12 @@ extern std::vector ParseHexO(const UniValue& o, std::string strKe extern int ParseInt(const UniValue& o, std::string strKey); extern bool ParseBool(const UniValue& o, std::string strKey); -extern int64_t nWalletUnlockTime; extern CAmount AmountFromValue(const UniValue& value); extern UniValue ValueFromAmount(const CAmount& amount); extern double GetDifficulty(const CBlockIndex* blockindex = NULL); -extern std::string HelpRequiringPassphrase(); extern std::string HelpExampleCli(std::string methodname, std::string args); extern std::string HelpExampleRpc(std::string methodname, std::string args); -extern void EnsureWalletIsUnlocked(bool fAllowAnonOnly = false); -// Ensure the wallet's existence. -extern void EnsureWallet(); - bool StartRPC(); void InterruptRPC(); void StopRPC(); diff --git a/src/test/librust/sapling_rpc_wallet_tests.cpp b/src/test/librust/sapling_rpc_wallet_tests.cpp index 3e17d198d231..281695547d56 100644 --- a/src/test/librust/sapling_rpc_wallet_tests.cpp +++ b/src/test/librust/sapling_rpc_wallet_tests.cpp @@ -25,6 +25,7 @@ #include + extern UniValue CallRPC(std::string args); // Implemented in rpc_tests.cpp namespace { @@ -55,6 +56,8 @@ BOOST_FIXTURE_TEST_SUITE(sapling_rpc_wallet_tests, WalletTestingSetup) BOOST_AUTO_TEST_CASE(rpc_wallet_sapling_validateaddress) { SelectParams(CBaseChainParams::MAIN); + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); + UniValue retValue; // Check number of args @@ -81,6 +84,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_sapling_validateaddress) BOOST_CHECK_EQUAL(b, false); BOOST_CHECK_EQUAL(find_value(resultObj, "diversifier").get_str(), "e1fd627f1b9a8e4c7e6657"); BOOST_CHECK_EQUAL(find_value(resultObj, "diversifiedtransmissionkey").get_str(), "d35e0d0897edbd3cf02b3d2327622a14c685534dbd2d3f4f4fa3e0e56cc2f008"); + + vpwallets.erase(vpwallets.begin()); } BOOST_AUTO_TEST_CASE(rpc_wallet_getbalance) @@ -90,6 +95,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_getbalance) pwalletMain->SetMinVersion(FEATURE_SAPLING); pwalletMain->SetupSPKM(false); } + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); BOOST_CHECK_THROW(CallRPC("getshieldbalance too many args"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("getshieldbalance invalidaddress"), std::runtime_error); @@ -109,6 +115,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_getbalance) BOOST_CHECK_THROW(CallRPC("listreceivedbyshieldaddress DMKU6mc52un1MThGCsnNwAtEvncaTdAuaZ 0"), std::runtime_error); // don't have the spending key BOOST_CHECK_THROW(CallRPC("listreceivedbyshieldaddress ps1u87kylcmn28yclnx2uy0psnvuhs2xn608ukm6n2nshrpg2nzyu3n62ls8j77m9cgp40dx40evej 1"), std::runtime_error); + + vpwallets.erase(vpwallets.begin()); } BOOST_AUTO_TEST_CASE(rpc_wallet_sapling_importkey_paymentaddress) @@ -118,6 +126,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_sapling_importkey_paymentaddress) pwalletMain->SetMinVersion(FEATURE_SAPLING); pwalletMain->SetupSPKM(false); } + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); auto testAddress = [](const std::string& key) { UniValue ret; @@ -135,6 +144,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_sapling_importkey_paymentaddress) "8uqmqlx8ccxpsw7ae243quhwr0zyekrrc520gs9z0j8pm954c3cev2yvp29vrc" "0zweu7stxkwhp593p6drheps9uhz9pvkrfgvpxzte8d60uzw0qxadnsc77tcd"); + vpwallets.erase(vpwallets.begin()); } /* @@ -147,6 +157,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_sapling_importexport) pwalletMain->SetMinVersion(FEATURE_SAPLING); pwalletMain->SetupSPKM(false); } + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); + UniValue retValue; int n1 = 1000; // number of times to import/export int n2 = 1000; // number of addresses to create and list @@ -221,15 +233,17 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_sapling_importexport) BOOST_CHECK((int) listaddrs.size() == numAddrs); BOOST_CHECK(myaddrs == listaddrs); + vpwallets.erase(vpwallets.begin()); } // Check if address is of given type and spendable from our wallet. -void CheckHaveAddr(const libzcash::PaymentAddress& addr) { +void CheckHaveAddr(std::unique_ptr& pwallet, const libzcash::PaymentAddress& addr) +{ BOOST_CHECK(IsValidPaymentAddress(addr)); auto addr_of_type = boost::get(&addr); BOOST_ASSERT(addr_of_type != nullptr); - BOOST_CHECK(pwalletMain->HaveSpendingKeyForPaymentAddress(*addr_of_type)); + BOOST_CHECK(pwallet->HaveSpendingKeyForPaymentAddress(*addr_of_type)); } BOOST_AUTO_TEST_CASE(rpc_wallet_getnewshieldaddress) @@ -239,12 +253,15 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_getnewshieldaddress) pwalletMain->SetMinVersion(FEATURE_SAPLING); pwalletMain->SetupSPKM(false); } + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); // No parameter defaults to sapling address UniValue addr = CallRPC("getnewshieldaddress"); - CheckHaveAddr(KeyIO::DecodePaymentAddress(addr.get_str())); + CheckHaveAddr(pwalletMain, KeyIO::DecodePaymentAddress(addr.get_str())); // Too many arguments will throw with the help BOOST_CHECK_THROW(CallRPC("getnewshieldaddress many args"), std::runtime_error); + + vpwallets.erase(vpwallets.begin()); } BOOST_AUTO_TEST_CASE(rpc_shieldsendmany_parameters) @@ -254,6 +271,7 @@ BOOST_AUTO_TEST_CASE(rpc_shieldsendmany_parameters) pwalletMain->SetMinVersion(FEATURE_SAPLING); pwalletMain->SetupSPKM(false); } + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); BOOST_CHECK_THROW(CallRPC("shieldsendmany"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("shieldsendmany toofewargs"), std::runtime_error); @@ -306,16 +324,20 @@ BOOST_AUTO_TEST_CASE(rpc_shieldsendmany_parameters) std::string zaddr1 = KeyIO::EncodePaymentAddress(pa); BOOST_CHECK_THROW(CallRPC(std::string("shieldsendmany DMKU6mc52un1MThGCsnNwAtEvncaTdAuaZ ") + "[{\"address\":\"" + zaddr1 + "\", \"amount\":123.456}]"), std::runtime_error); + + vpwallets.erase(vpwallets.begin()); } +// TODO: test private methods BOOST_AUTO_TEST_CASE(saplingOperationTests) { { LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->SetupSPKM(false); } - auto consensusParams = Params().GetConsensus(); + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); + UniValue retValue; // add keys manually @@ -328,7 +350,7 @@ BOOST_AUTO_TEST_CASE(saplingOperationTests) // there are no utxos to spend { std::vector recipients = { SendManyRecipient(zaddr1, COIN, "DEADBEEF") }; - SaplingOperation operation(consensusParams, 1, pwalletMain); + SaplingOperation operation(consensusParams, 1, pwalletMain.get()); operation.setFromAddress(taddr1); auto res = operation.setRecipients(recipients)->buildAndSend(ret); BOOST_CHECK(!res); @@ -338,7 +360,7 @@ BOOST_AUTO_TEST_CASE(saplingOperationTests) // minconf cannot be zero when sending from zaddr { std::vector recipients = { SendManyRecipient(zaddr1, COIN, "DEADBEEF") }; - SaplingOperation operation(consensusParams, 1, pwalletMain); + SaplingOperation operation(consensusParams, 1, pwalletMain.get()); operation.setFromAddress(zaddr1); auto res = operation.setRecipients(recipients)->setMinDepth(0)->buildAndSend(ret); BOOST_CHECK(!res); @@ -348,7 +370,7 @@ BOOST_AUTO_TEST_CASE(saplingOperationTests) // there are no unspent notes to spend { std::vector recipients = { SendManyRecipient(taddr1, COIN) }; - SaplingOperation operation(consensusParams, 1, pwalletMain); + SaplingOperation operation(consensusParams, 1, pwalletMain.get()); operation.setFromAddress(zaddr1); auto res = operation.setRecipients(recipients)->buildAndSend(ret); BOOST_CHECK(!res); @@ -383,6 +405,8 @@ BOOST_AUTO_TEST_CASE(saplingOperationTests) const std::string& errStr = res.getError(); BOOST_CHECK(errStr.find("too big") != std::string::npos); } + + vpwallets.erase(vpwallets.begin()); } @@ -392,6 +416,7 @@ BOOST_AUTO_TEST_CASE(rpc_shieldsendmany_taddr_to_sapling) LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->SetupSPKM(false); } + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); UniValue retValue; @@ -409,7 +434,7 @@ BOOST_AUTO_TEST_CASE(rpc_shieldsendmany_taddr_to_sapling) CMutableTransaction mtx; mtx.vout.emplace_back(5 * COIN, GetScriptForDestination(taddr)); // Add to wallet and get the updated wtx - CWalletTx wtxIn(pwalletMain, MakeTransactionRef(mtx)); + CWalletTx wtxIn(pwalletMain.get(), MakeTransactionRef(mtx)); pwalletMain->LoadToWallet(wtxIn); CWalletTx& wtx = pwalletMain->mapWallet.at(mtx.GetHash()); @@ -431,7 +456,7 @@ BOOST_AUTO_TEST_CASE(rpc_shieldsendmany_taddr_to_sapling) BOOST_CHECK_MESSAGE(pwalletMain->GetAvailableBalance() > 0, "tx not confirmed"); std::vector recipients = { SendManyRecipient(zaddr1, 1 * COIN, "ABCD") }; - SaplingOperation operation(consensusParams, nextBlockHeight, pwalletMain); + SaplingOperation operation(consensusParams, nextBlockHeight, pwalletMain.get()); operation.setFromAddress(taddr); BOOST_CHECK(operation.setRecipients(recipients) ->setMinDepth(0) @@ -439,7 +464,7 @@ BOOST_AUTO_TEST_CASE(rpc_shieldsendmany_taddr_to_sapling) // try from auto-selected transparent address std::vector recipients2 = { SendManyRecipient(zaddr1, 1 * COIN, "ABCD") }; - SaplingOperation operation2(consensusParams, nextBlockHeight, pwalletMain); + SaplingOperation operation2(consensusParams, nextBlockHeight, pwalletMain.get()); BOOST_CHECK(operation2.setSelectTransparentCoins(true) ->setRecipients(recipients2) ->setMinDepth(0) @@ -470,6 +495,7 @@ BOOST_AUTO_TEST_CASE(rpc_shieldsendmany_taddr_to_sapling) // Tear down chainActive.SetTip(nullptr); mapBlockIndex.erase(blockHash); + vpwallets.erase(vpwallets.begin()); } BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) @@ -482,6 +508,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) pwalletMain->SetMinVersion(FEATURE_SAPLING); pwalletMain->SetupSPKM(false); } + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); // wallet should currently be empty std::set addrs; @@ -530,8 +557,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) arr = retValue.get_array(); BOOST_CHECK((int) arr.size() == n+1); - // We can't simulate over RPC the wallet closing and being reloaded - // but there are tests for this in gtest. + vpwallets.erase(vpwallets.begin()); } BOOST_AUTO_TEST_CASE(rpc_listshieldunspent_parameters) @@ -540,6 +566,7 @@ BOOST_AUTO_TEST_CASE(rpc_listshieldunspent_parameters) LOCK(pwalletMain->cs_wallet); pwalletMain->SetupSPKM(false); } + vpwallets.insert(vpwallets.begin(), pwalletMain.get()); UniValue retValue; @@ -581,6 +608,8 @@ BOOST_AUTO_TEST_CASE(rpc_listshieldunspent_parameters) // duplicate address error BOOST_CHECK_THROW(CallRPC("listshieldunspent 1 999 false [\"" + myzaddr + "\", \"" + myzaddr + "\"]"), std::runtime_error); + + vpwallets.erase(vpwallets.begin()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/librust/sapling_test_fixture.cpp b/src/test/librust/sapling_test_fixture.cpp index 740c70341fee..ea03ff84c863 100644 --- a/src/test/librust/sapling_test_fixture.cpp +++ b/src/test/librust/sapling_test_fixture.cpp @@ -13,3 +13,8 @@ SaplingTestingSetup::SaplingTestingSetup(const std::string& chainName) : Testing SaplingTestingSetup::~SaplingTestingSetup() { } + +SaplingRegTestingSetup::SaplingRegTestingSetup() : SaplingTestingSetup(CBaseChainParams::REGTEST) +{ + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V5_0, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); +} diff --git a/src/test/librust/sapling_test_fixture.h b/src/test/librust/sapling_test_fixture.h index 59ae6927b46d..962d479ce279 100644 --- a/src/test/librust/sapling_test_fixture.h +++ b/src/test/librust/sapling_test_fixture.h @@ -16,5 +16,13 @@ struct SaplingTestingSetup : public TestingSetup ~SaplingTestingSetup(); }; +/** + * Regtest setup with sapling always active + */ +struct SaplingRegTestingSetup : public SaplingTestingSetup +{ + SaplingRegTestingSetup(); +}; + #endif //PIVX_SAPLING_TEST_FIXTURE_H diff --git a/src/test/librust/sapling_wallet_tests.cpp b/src/test/librust/sapling_wallet_tests.cpp index 4fac832cbbdc..36c8681a94bf 100644 --- a/src/test/librust/sapling_wallet_tests.cpp +++ b/src/test/librust/sapling_wallet_tests.cpp @@ -70,10 +70,10 @@ uint256 GetWitnessesAndAnchors(CWallet& wallet, return saplingAnchor; } -BOOST_FIXTURE_TEST_SUITE(sapling_wallet_tests, WalletTestingSetup) +BOOST_FIXTURE_TEST_SUITE(sapling_wallet_tests, WalletRegTestingSetup) BOOST_AUTO_TEST_CASE(SetSaplingNoteAddrsInCWalletTx) { - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; LOCK(wallet.cs_wallet); @@ -124,9 +124,6 @@ BOOST_AUTO_TEST_CASE(SetSaplingNoteAddrsInCWalletTx) { BOOST_CHECK(nullifier == wtx.mapSaplingNoteData[op].nullifier); BOOST_CHECK(nd.witnessHeight == wtx.mapSaplingNoteData[op].witnessHeight); BOOST_CHECK(witness == wtx.mapSaplingNoteData[op].witnesses.front()); - - // Revert to default - RegtestDeactivateSapling(); } // Cannot add note data for an index which does not exist in tx.vShieldedOutput @@ -142,8 +139,9 @@ BOOST_AUTO_TEST_CASE(SetInvalidSaplingNoteDataInCWalletTx) { BOOST_CHECK_THROW(wtx.SetSaplingNoteData(noteData), std::logic_error); } -BOOST_AUTO_TEST_CASE(FindMySaplingNotes) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(FindMySaplingNotes) +{ + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; LOCK(wallet.cs_wallet); @@ -175,14 +173,12 @@ BOOST_AUTO_TEST_CASE(FindMySaplingNotes) { BOOST_CHECK(wallet.HaveSaplingSpendingKey(extfvk)); noteMap = wallet.GetSaplingScriptPubKeyMan()->FindMySaplingNotes(*wtx.tx).first; BOOST_CHECK_EQUAL(2, noteMap.size()); - - // Revert to default - RegtestDeactivateSapling(); } // Generate note A and spend to create note B, from which we spend to create two conflicting transactions -BOOST_AUTO_TEST_CASE(GetConflictedSaplingNotes) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(GetConflictedSaplingNotes) +{ + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; LOCK2(cs_main, wallet.cs_wallet); @@ -296,15 +292,13 @@ BOOST_AUTO_TEST_CASE(GetConflictedSaplingNotes) { BOOST_CHECK(std::set({hash2, hash3}) == c3); // Tear down - chainActive.SetTip(NULL); + chainActive.SetTip(nullptr); mapBlockIndex.erase(blockHash); - - // Revert to default - RegtestDeactivateSapling(); } -BOOST_AUTO_TEST_CASE(SaplingNullifierIsSpent) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(SaplingNullifierIsSpent) +{ + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; LOCK2(cs_main, wallet.cs_wallet); @@ -357,15 +351,13 @@ BOOST_AUTO_TEST_CASE(SaplingNullifierIsSpent) { BOOST_CHECK(wallet.GetSaplingScriptPubKeyMan()->IsSaplingSpent(nullifier)); // Tear down - chainActive.SetTip(NULL); + chainActive.SetTip(nullptr); mapBlockIndex.erase(blockHash); - - // Revert to default - RegtestDeactivateSapling(); } -BOOST_AUTO_TEST_CASE(NavigateFromSaplingNullifierToNote) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(NavigateFromSaplingNullifierToNote) +{ + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; LOCK2(cs_main, wallet.cs_wallet); @@ -454,16 +446,14 @@ BOOST_AUTO_TEST_CASE(NavigateFromSaplingNullifierToNote) { } // Tear down - chainActive.SetTip(NULL); + chainActive.SetTip(nullptr); mapBlockIndex.erase(blockHash); - - // Revert to default - RegtestDeactivateSapling(); } // Create note A, spend A to create note B, spend and verify note B is from me. -BOOST_AUTO_TEST_CASE(SpentSaplingNoteIsFromMe) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(SpentSaplingNoteIsFromMe) +{ + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; LOCK2(cs_main, wallet.cs_wallet); @@ -601,16 +591,14 @@ BOOST_AUTO_TEST_CASE(SpentSaplingNoteIsFromMe) { BOOST_CHECK(wallet.GetSaplingScriptPubKeyMan()->mapSaplingNullifiersToNotes.count(nullifier2)); // Tear down - chainActive.SetTip(NULL); + chainActive.SetTip(nullptr); mapBlockIndex.erase(blockHash); mapBlockIndex.erase(blockHash2); - - // Revert to default - RegtestDeactivateSapling(); } -BOOST_AUTO_TEST_CASE(CachedWitnessesEmptyChain) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(CachedWitnessesEmptyChain) +{ + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; { @@ -646,13 +634,11 @@ BOOST_AUTO_TEST_CASE(CachedWitnessesEmptyChain) { // Until zcash#1302 is implemented, this should triggger an assertion BOOST_CHECK_THROW(wallet.DecrementNoteWitnesses(&index), std::runtime_error); - - // Revert to default - RegtestDeactivateSapling(); } -BOOST_AUTO_TEST_CASE(CachedWitnessesChainTip) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(CachedWitnessesChainTip) +{ + auto consensusParams = Params().GetConsensus(); libzcash::SaplingExtendedSpendingKey sk = GetTestMasterSaplingSpendingKey(); CWallet& wallet = *pwalletMain; @@ -730,9 +716,9 @@ BOOST_AUTO_TEST_CASE(CachedWitnessesChainTip) { } } -BOOST_AUTO_TEST_CASE(CachedWitnessesDecrementFirst) { - auto consensusParams = RegtestActivateSapling(); - +BOOST_AUTO_TEST_CASE(CachedWitnessesDecrementFirst) +{ + auto consensusParams = Params().GetConsensus(); libzcash::SaplingExtendedSpendingKey sk = GetTestMasterSaplingSpendingKey(); CWallet& wallet = *pwalletMain; { @@ -798,8 +784,9 @@ BOOST_AUTO_TEST_CASE(CachedWitnessesDecrementFirst) { } } -BOOST_AUTO_TEST_CASE(CachedWitnessesCleanIndex) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(CachedWitnessesCleanIndex) +{ + auto consensusParams = Params().GetConsensus(); libzcash::SaplingExtendedSpendingKey sk = GetTestMasterSaplingSpendingKey(); CWallet& wallet = *pwalletMain; @@ -874,8 +861,9 @@ BOOST_AUTO_TEST_CASE(CachedWitnessesCleanIndex) { } } -BOOST_AUTO_TEST_CASE(ClearNoteWitnessCache) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(ClearNoteWitnessCache) +{ + auto consensusParams = Params().GetConsensus(); libzcash::SaplingExtendedSpendingKey sk = GetTestMasterSaplingSpendingKey(); CWallet& wallet = *pwalletMain; @@ -922,8 +910,9 @@ BOOST_AUTO_TEST_CASE(ClearNoteWitnessCache) { BOOST_CHECK_EQUAL(0, wallet.GetSaplingScriptPubKeyMan()->nWitnessCacheSize); } -BOOST_AUTO_TEST_CASE(UpdatedSaplingNoteData) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(UpdatedSaplingNoteData) +{ + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; // Need to lock cs_main for now due the lock ordering. future: revamp all of this function to only lock where is needed. @@ -1028,15 +1017,13 @@ BOOST_AUTO_TEST_CASE(UpdatedSaplingNoteData) { BOOST_CHECK(wtx.mapSaplingNoteData[sop1].witnesses.front() == testNote.tree.witness()); // Tear down - chainActive.SetTip(NULL); + chainActive.SetTip(nullptr); mapBlockIndex.erase(blockHash); - - // Revert to default - RegtestDeactivateSapling(); } -BOOST_AUTO_TEST_CASE(MarkAffectedSaplingTransactionsDirty) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(MarkAffectedSaplingTransactionsDirty) +{ + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; LOCK2(cs_main, wallet.cs_wallet); @@ -1140,16 +1127,13 @@ BOOST_AUTO_TEST_CASE(MarkAffectedSaplingTransactionsDirty) { BOOST_CHECK(!wallet.mapWallet.at(hash).IsAmountCached(CWalletTx::AmountType::DEBIT, ISMINE_SPENDABLE)); // Tear down - chainActive.SetTip(NULL); + chainActive.SetTip(nullptr); mapBlockIndex.erase(blockHash); - - // Revert to default - RegtestDeactivateSapling(); } BOOST_AUTO_TEST_CASE(GetNotes) { - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); CWallet& wallet = *pwalletMain; libzcash::SaplingPaymentAddress pk; @@ -1238,9 +1222,6 @@ BOOST_AUTO_TEST_CASE(GetNotes) LOCK(cs_main); chainActive.SetTip(nullptr); mapBlockIndex.erase(blockHash); - - // Revert to default - RegtestDeactivateSapling(); } // TODO: Back port WriteWitnessCache & SetBestChainIgnoresTxsWithoutShieldedData test cases. diff --git a/src/test/librust/transaction_builder_tests.cpp b/src/test/librust/transaction_builder_tests.cpp index 17aed96c8058..55c5180c6066 100644 --- a/src/test/librust/transaction_builder_tests.cpp +++ b/src/test/librust/transaction_builder_tests.cpp @@ -13,11 +13,11 @@ #include #include -BOOST_FIXTURE_TEST_SUITE(sapling_transaction_builder_tests, SaplingTestingSetup) +BOOST_FIXTURE_TEST_SUITE(sapling_transaction_builder_tests, SaplingRegTestingSetup) BOOST_AUTO_TEST_CASE(TransparentToSapling) { - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); CBasicKeyStore keystore; CKey tsk = AddTestCKeyToKeyStore(keystore); @@ -49,13 +49,11 @@ BOOST_AUTO_TEST_CASE(TransparentToSapling) CValidationState state; BOOST_CHECK(SaplingValidation::ContextualCheckTransaction(tx, state, Params(), 2, true, false)); BOOST_CHECK_EQUAL(state.GetRejectReason(), ""); - - // Revert to default - RegtestDeactivateSapling(); } -BOOST_AUTO_TEST_CASE(SaplingToSapling) { - auto consensusParams = RegtestActivateSapling(); +BOOST_AUTO_TEST_CASE(SaplingToSapling) +{ + auto consensusParams = Params().GetConsensus(); auto sk = libzcash::SaplingSpendingKey::random(); auto expsk = sk.expanded_spending_key(); @@ -101,45 +99,33 @@ BOOST_AUTO_TEST_CASE(SaplingToSapling) { BOOST_CHECK_EQUAL(tx2.sapData->valueBalance, 10000000); BOOST_CHECK(SaplingValidation::ContextualCheckTransaction(tx2, state, Params(), 3, true, false)); BOOST_CHECK_EQUAL(state.GetRejectReason(), ""); - - // Revert to default - RegtestDeactivateSapling(); } BOOST_AUTO_TEST_CASE(ThrowsOnTransparentInputWithoutKeyStore) { - SelectParams(CBaseChainParams::REGTEST); - auto consensusParams = Params().GetConsensus(); - - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(Params().GetConsensus(), 1); BOOST_CHECK_THROW(builder.AddTransparentInput(COutPoint(), CScript(), 1), std::runtime_error); } BOOST_AUTO_TEST_CASE(RejectsInvalidTransparentOutput) { - SelectParams(CBaseChainParams::REGTEST); - auto consensusParams = Params().GetConsensus(); - // Default CTxDestination type is an invalid address CTxDestination taddr; - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(Params().GetConsensus(), 1); BOOST_CHECK_THROW(builder.AddTransparentOutput(taddr, 50), std::runtime_error); } BOOST_AUTO_TEST_CASE(RejectsInvalidTransparentChangeAddress) { - SelectParams(CBaseChainParams::REGTEST); - auto consensusParams = Params().GetConsensus(); - // Default CTxDestination type is an invalid address CTxDestination taddr; - auto builder = TransactionBuilder(consensusParams, 1); + auto builder = TransactionBuilder(Params().GetConsensus(), 1); BOOST_CHECK_THROW(builder.SendChangeTo(taddr), std::runtime_error); } BOOST_AUTO_TEST_CASE(FailsWithNegativeChange) { - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); // Generate dummy Sapling address auto sk = libzcash::SaplingSpendingKey::random(); @@ -179,14 +165,11 @@ BOOST_AUTO_TEST_CASE(FailsWithNegativeChange) // Succeeds if there is sufficient input builder.AddTransparentInput(COutPoint(), scriptPubKey, 10000); BOOST_CHECK(builder.Build().IsTx()); - - // Revert to default - RegtestDeactivateSapling(); } BOOST_AUTO_TEST_CASE(ChangeOutput) { - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); // Generate dummy Sapling address auto sk = libzcash::SaplingSpendingKey::random(); @@ -260,14 +243,11 @@ BOOST_AUTO_TEST_CASE(ChangeOutput) BOOST_CHECK_EQUAL(tx.sapData->valueBalance, 0); BOOST_CHECK_EQUAL(tx.vout[0].nValue, 15000000); } - - // Revert to default - RegtestDeactivateSapling(); } BOOST_AUTO_TEST_CASE(SetFee) { - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); // Generate dummy Sapling address auto sk = libzcash::SaplingSpendingKey::random(); @@ -292,14 +272,10 @@ BOOST_AUTO_TEST_CASE(SetFee) BOOST_CHECK_EQUAL(tx.sapData->vShieldedOutput.size(), 2); BOOST_CHECK_EQUAL(tx.sapData->valueBalance, COIN); } - - // Revert to default - RegtestDeactivateSapling(); } BOOST_AUTO_TEST_CASE(CheckSaplingTxVersion) { - SelectParams(CBaseChainParams::REGTEST); auto consensusParams = Params().GetConsensus(); auto sk = libzcash::SaplingSpendingKey::random(); diff --git a/src/test/librust/utiltest.cpp b/src/test/librust/utiltest.cpp index caf2511daf1b..5864fea63bcb 100644 --- a/src/test/librust/utiltest.cpp +++ b/src/test/librust/utiltest.cpp @@ -13,16 +13,6 @@ static const std::string T_SECRET_REGTEST = "cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN"; -const Consensus::Params& RegtestActivateSapling() { - SelectParams(CBaseChainParams::REGTEST); - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V5_0, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); - return Params().GetConsensus(); -} - -void RegtestDeactivateSapling() { - UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V5_0, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); -} - libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey() { std::vector> rawSeed(32); HDSeed seed(rawSeed); diff --git a/src/test/librust/utiltest.h b/src/test/librust/utiltest.h index 4f6e8412015f..f58f806275d5 100644 --- a/src/test/librust/utiltest.h +++ b/src/test/librust/utiltest.h @@ -28,9 +28,6 @@ struct TransparentInput { CAmount amount; }; -const Consensus::Params& RegtestActivateSapling(); - -void RegtestDeactivateSapling(); libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey(); diff --git a/src/test/librust/wallet_zkeys_tests.cpp b/src/test/librust/wallet_zkeys_tests.cpp index cb06c2efbfa8..19764a4cfd56 100644 --- a/src/test/librust/wallet_zkeys_tests.cpp +++ b/src/test/librust/wallet_zkeys_tests.cpp @@ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(WriteCryptedSaplingZkeyDirectToDb) { BOOST_CHECK_EQUAL(DB_LOAD_OK, wallet2.LoadWallet(fFirstRun)); // Confirm it's not the same as the other wallet - BOOST_CHECK(pwalletMain != &wallet2); + BOOST_CHECK(pwalletMain.get() != &wallet2); BOOST_CHECK(wallet2.HasSaplingSPKM()); // wallet should have two keys diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index af7814566117..6f249891f760 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -17,6 +17,7 @@ #include + // future: this should be MAINNET. BOOST_FIXTURE_TEST_SUITE(miner_tests, WalletRegTestingSetup) @@ -89,7 +90,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) Checkpoints::fEnabled = false; // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false)); + BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false)); // Set genesis block pblocktemplate->block.hashPrevBlock = chainparams.GetConsensus().hashGenesisBlock; @@ -115,7 +116,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) } // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false)); + BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false)); // block sigops > limit: 2000 CHECKMULTISIG + 1 tx.vin.resize(1); @@ -133,7 +134,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbaseOrCoinstake(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false), std::runtime_error, HasReason("bad-blk-sigops")); + BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false), std::runtime_error, HasReason("bad-blk-sigops")); mempool.clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -146,7 +147,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbaseOrCoinstake(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false)); + BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false)); mempool.clear(); // block size > limit @@ -166,13 +167,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbaseOrCoinstake(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false)); + BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false)); mempool.clear(); // orphan in mempool, template creation fails hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); + BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); mempool.clear(); // child with higher priority than parent @@ -189,7 +190,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 5900000000LL; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(400000000LL).Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false)); + BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false)); mempool.clear(); // coinbase in mempool, template creation fails @@ -200,7 +201,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); // give it a fee so it'll get mined mempool.addUnchecked(hash, entry.Fee(100000).Time(GetTime()).SpendsCoinbaseOrCoinstake(false).FromTx(tx)); - BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false), std::runtime_error, HasReason("bad-cb-multiple")); + BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false), std::runtime_error, HasReason("bad-cb-multiple")); mempool.clear(); // invalid (pre-p2sh) txn in mempool, template creation fails @@ -218,7 +219,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbaseOrCoinstake(false).FromTx(tx)); // Should throw block-validation-failed - BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false), std::runtime_error, HasReason("block-validation-failed")); + BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false), std::runtime_error, HasReason("block-validation-failed")); mempool.clear(); // double spend txn pair in mempool, template creation fails @@ -231,7 +232,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx)); - BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); + BOOST_CHECK_EXCEPTION(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); mempool.clear(); // non-final txs in mempool @@ -262,7 +263,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx2)); { LOCK(cs_main); BOOST_CHECK(!CheckFinalTx(MakeTransactionRef(tx2), LOCKTIME_MEDIAN_TIME_PAST)); } - BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false)); + BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false)); // Neither tx should have make it into the template. BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); @@ -279,7 +280,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) //BOOST_CHECK(CheckFinalTx(tx)); //BOOST_CHECK(CheckFinalTx(tx2)); - BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain, false)); + BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, pwalletMain.get(), false)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3); WITH_LOCK(cs_main, chainActive.Tip()->nHeight--); diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp index 41b6dca855b8..b3d2714e99ce 100644 --- a/src/test/validation_tests.cpp +++ b/src/test/validation_tests.cpp @@ -7,6 +7,7 @@ #include "primitives/transaction.h" #include "sapling/sapling_validation.h" #include "test/librust/utiltest.h" +#include "wallet/test/wallet_test_fixture.h" #include @@ -115,14 +116,14 @@ void CheckMempoolZcRejection(CMutableTransaction& mtx) /* * Running on regtest to have v5 upgrade enforced at block 1 and test in-block zc rejection */ -BOOST_FIXTURE_TEST_CASE(zerocoin_rejection_tests, RegTestingSetup) +BOOST_FIXTURE_TEST_CASE(zerocoin_rejection_tests, WalletRegTestingSetup) { UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V5_0, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); const CChainParams& chainparams = Params(); std::unique_ptr pblocktemplate; CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ParseHex("8d5b4f83212214d6ef693e02e6d71969fddad976") << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), false).CreateNewBlock(scriptPubKey, pwalletMain, false)); + BOOST_CHECK(pblocktemplate = BlockAssembler(Params(), false).CreateNewBlock(scriptPubKey, pwalletMain.get(), false)); pblocktemplate->block.hashPrevBlock = chainparams.GetConsensus().hashGenesisBlock; // Base tx diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 7ea9dfbab1e8..b6cf8dfc8cdf 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin developers -// Copyright (c) 2019-2020 The PIVX developers +// Copyright (c) 2009-2021 The Bitcoin developers +// Copyright (c) 2019-2021 The PIVX developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -146,7 +146,7 @@ void CDBEnv::MakeMock() fMockDb = true; } -CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile)) +CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename) { LOCK(cs_db); assert(mapFileUseCount.count(strFile) == 0); @@ -159,21 +159,21 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFu return RECOVER_FAIL; // Try to recover: - bool fRecovered = (*recoverFunc)(strFile); + bool fRecovered = (*recoverFunc)(strFile, out_backup_filename); return (fRecovered ? RECOVER_OK : RECOVER_FAIL); } -bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)) +bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename) { // Recovery procedure: - // move wallet file to wallet.timestamp.bak + // move wallet file to walletfilename.timestamp.bak // Call Salvage with fAggressive=true to // get as much data as possible. // Rewrite salvaged data to fresh wallet file // Set -rescan so any missing transactions will be // found. int64_t now = GetTime(); - std::string newFilename = strprintf("wallet.%d.bak", now); + newFilename = strprintf("%s.%d.bak", filename, now); int result = bitdb.dbenv->dbrename(NULL, filename.c_str(), NULL, newFilename.c_str(), DB_AUTO_COMMIT); @@ -257,16 +257,17 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataD return true; } -bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile)) +bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) { if (fs::exists(dataDir / walletFile)) { - CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc); + std::string backup_filename; + CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename); if (r == CDBEnv::RECOVER_OK) { - warningStr = strprintf(("Warning: Wallet file corrupt, data salvaged!" + warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!" " Original %s saved as %s in %s; if" " your balance or transactions are incorrect you should" " restore from a backup."), - walletFile, "wallet.{timestamp}.bak", dataDir); + walletFile, backup_filename, dataDir); } if (r == CDBEnv::RECOVER_FAIL) { errorStr = strprintf(("%s corrupt, salvage failed"), walletFile); @@ -428,6 +429,11 @@ void CDB::Flush() env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", 100) * 1024 : 0, nMinutes, 0); } +void CWalletDBWrapper::IncrementUpdateCounter() +{ + ++nUpdateCounter; +} + void CDB::Close() { if (!pdb) diff --git a/src/wallet/db.h b/src/wallet/db.h index a280a64007bf..1c76d58c91e8 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin developers -// Copyright (c) 2019-2020 The PIVX developers +// Copyright (c) 2009-2021 The Bitcoin developers +// Copyright (c) 2019-2021 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -14,6 +14,7 @@ #include "sync.h" #include "version.h" +#include #include #include #include @@ -54,9 +55,11 @@ class CDBEnv * Returns true if strFile is OK. */ enum VerifyResult { VERIFY_OK, - RECOVER_OK, - RECOVER_FAIL }; - VerifyResult Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile)); + RECOVER_OK, + RECOVER_FAIL }; + typedef bool (*recoverFunc_type)(const std::string& strFile, std::string& out_backup_filename); + VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename); + /** * Salvage data from a file that Verify says is bad. * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). @@ -95,13 +98,13 @@ class CWalletDBWrapper friend class CDB; public: /** Create dummy DB handle */ - CWalletDBWrapper(): env(nullptr) + CWalletDBWrapper() : nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) { } /** Create DB handle to real database */ CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in): - env(env_in), strFile(strFile_in) + nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in) { } @@ -121,6 +124,13 @@ class CWalletDBWrapper */ void Flush(bool shutdown); + void IncrementUpdateCounter(); + std::atomic nUpdateCounter; + unsigned int nLastSeen; + unsigned int nLastFlushed; + int64_t nLastWalletUpdate; + unsigned int GetUpdateCounter(); + private: /** BerkeleyDB specific */ CDBEnv *env; @@ -151,7 +161,7 @@ class CDB void Flush(); void Close(); - static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)); + static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); /* flush the wallet passively (TRY_LOCK) ideal to be called periodically */ @@ -159,7 +169,7 @@ class CDB /* verifies the database environment */ static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr); /* verifies the database file */ - static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile)); + static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); private: CDB(const CDB&); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index f476efde8d24..302ab5aafafb 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -15,7 +15,9 @@ #include "util/system.h" #include "utilstrencodings.h" #include "utiltime.h" -#include "wallet/wallet.h" +#include "wallet/rpcwallet.h" +#include "wallet.h" +#include "validation.h" #include #include @@ -76,11 +78,16 @@ bool IsStakingDerPath(KeyOriginInfo keyOrigin) UniValue importprivkey(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "importprivkey \"pivxprivkey\" ( \"label\" rescan fStakingAddress )\n" "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"pivxprivkey\" (string, required) The private key (see dumpprivkey)\n" @@ -103,7 +110,7 @@ UniValue importprivkey(const JSONRPCRequest& request) const std::string strLabel = (request.params.size() > 1 ? request.params[1].get_str() : ""); const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true); - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (fRescan && !reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -117,28 +124,28 @@ UniValue importprivkey(const JSONRPCRequest& request) assert(key.VerifyPubKey(pubkey)); CKeyID vchAddress = pubkey.GetID(); { - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); + LOCK2(cs_main, pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); - pwalletMain->MarkDirty(); - pwalletMain->SetAddressBook(vchAddress, strLabel, ( + pwallet->MarkDirty(); + pwallet->SetAddressBook(vchAddress, strLabel, ( fStakingAddress ? AddressBook::AddressBookPurpose::COLD_STAKING : AddressBook::AddressBookPurpose::RECEIVE)); // Don't throw error in case a key is already there - if (pwalletMain->HaveKey(vchAddress)) + if (pwallet->HaveKey(vchAddress)) return NullUniValue; // whenever a key is imported, we need to scan the whole chain - pwalletMain->UpdateTimeFirstKey(1); - pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; + pwallet->UpdateTimeFirstKey(1); + pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1; - if (!pwalletMain->AddKeyPubKey(key, pubkey)) + if (!pwallet->AddKeyPubKey(key, pubkey)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } if (fRescan) { - pwalletMain->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); } return NullUniValue; @@ -146,6 +153,11 @@ UniValue importprivkey(const JSONRPCRequest& request) UniValue abortrescan(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 0) throw std::runtime_error( "abortrescan\n" @@ -159,43 +171,47 @@ UniValue abortrescan(const JSONRPCRequest& request) + HelpExampleRpc("abortrescan", "") ); - EnsureWallet(); - if (!pwalletMain->IsScanning() || pwalletMain->IsAbortingRescan()) return false; - pwalletMain->AbortRescan(); + if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false; + pwallet->AbortRescan(); return true; } -void ImportAddress(const CTxDestination& dest, const std::string& strLabel, const std::string& strPurpose); +static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel, const std::string& strPurpose); -void ImportScript(const CScript& script, const std::string& strLabel, bool isRedeemScript) +static void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) { - if (!isRedeemScript && ::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) + if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); - pwalletMain->MarkDirty(); + pwallet->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script)) + if (!pwallet->HaveWatchOnly(script) && !pwallet->AddWatchOnly(script)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); if (isRedeemScript) { - if (!pwalletMain->HaveCScript(script) && !pwalletMain->AddCScript(script)) + if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); - ImportAddress(CScriptID(script), strLabel, "receive"); + ImportAddress(pwallet, CScriptID(script), strLabel, "receive"); } } -void ImportAddress(const CTxDestination& dest, const std::string& strLabel, const std::string& strPurpose) +static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel, const std::string& strPurpose) { CScript script = GetScriptForDestination(dest); - ImportScript(script, strLabel, false); + ImportScript(pwallet, script, strLabel, false); // add to address book or update label if (IsValidDestination(dest)) { - pwalletMain->SetAddressBook(dest, strLabel, strPurpose); + pwallet->SetAddressBook(dest, strLabel, strPurpose); } } UniValue importaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "importaddress \"script\" ( \"label\" rescan )\n" @@ -220,7 +236,7 @@ UniValue importaddress(const JSONRPCRequest& request) // Whether to perform rescan after import const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true); - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (fRescan && !reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -229,7 +245,7 @@ UniValue importaddress(const JSONRPCRequest& request) const bool fP2SH = (request.params.size() > 3 ? request.params[3].get_bool() : false); { - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); bool isStakingAddress = false; CTxDestination dest = DecodeDestination(request.params[0].get_str(), isStakingAddress); @@ -237,20 +253,20 @@ UniValue importaddress(const JSONRPCRequest& request) if (IsValidDestination(dest)) { if (fP2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); - ImportAddress(dest, strLabel, isStakingAddress ? + ImportAddress(pwallet, dest, strLabel, isStakingAddress ? AddressBook::AddressBookPurpose::COLD_STAKING : AddressBook::AddressBookPurpose::RECEIVE); } else if (IsHex(request.params[0].get_str())) { std::vector data(ParseHex(request.params[0].get_str())); - ImportScript(CScript(data.begin(), data.end()), strLabel, fP2SH); + ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH); } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address or script"); } } if (fRescan) { - pwalletMain->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); - pwalletMain->ReacceptWalletTransactions(); + pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + pwallet->ReacceptWalletTransactions(); } return NullUniValue; @@ -258,6 +274,11 @@ UniValue importaddress(const JSONRPCRequest& request) UniValue importpubkey(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "importpubkey \"pubkey\" ( \"label\" rescan )\n" @@ -281,7 +302,7 @@ UniValue importpubkey(const JSONRPCRequest& request) // Whether to perform rescan after import const bool fRescan = (request.params.size() > 2 ? request.params[2].get_bool() : true); - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (fRescan && !reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -294,14 +315,14 @@ UniValue importpubkey(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); { - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - ImportAddress(pubKey.GetID(), strLabel, "receive"); - ImportScript(GetScriptForRawPubKey(pubKey), strLabel, false); + ImportAddress(pwallet, pubKey.GetID(), strLabel, "receive"); + ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false); } if (fRescan) { - pwalletMain->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); - pwalletMain->ReacceptWalletTransactions(); + pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + pwallet->ReacceptWalletTransactions(); } return NullUniValue; @@ -310,11 +331,16 @@ UniValue importpubkey(const JSONRPCRequest& request) // TODO: Needs further review over the HD flow, staking addresses and multisig import. UniValue importwallet(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "importwallet \"filename\"\n" "\nImports keys from a wallet dump file (see dumpwallet).\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"filename\" (string, required) The wallet file\n" @@ -332,7 +358,7 @@ UniValue importwallet(const JSONRPCRequest& request) if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (!reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -340,16 +366,16 @@ UniValue importwallet(const JSONRPCRequest& request) int64_t nTimeBegin = 0; bool fGood = true; { - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); + LOCK2(cs_main, pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); nTimeBegin = chainActive.Tip()->GetBlockTime(); int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); - pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI + pwallet->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI while (file.good()) { - pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); + pwallet->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); std::string line; std::getline(file, line); if (line.empty() || line[0] == '#') @@ -362,12 +388,12 @@ UniValue importwallet(const JSONRPCRequest& request) // Sapling keys // Let's see if the address is a valid PIVX spending key - if (pwalletMain->HasSaplingSPKM()) { + if (pwallet->HasSaplingSPKM()) { libzcash::SpendingKey spendingkey = KeyIO::DecodeSpendingKey(vstr[0]); int64_t nTime = DecodeDumpTime(vstr[1]); if (IsValidSpendingKey(spendingkey)) { libzcash::SaplingExtendedSpendingKey saplingSpendingKey = *boost::get(&spendingkey); - auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet( + auto addResult = pwallet->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet( Params().GetConsensus(), saplingSpendingKey, nTime); if (addResult == KeyAlreadyExists) { LogPrint(BCLog::SAPLING, "Skipping import of shielded addr (key already present)\n"); @@ -385,7 +411,7 @@ UniValue importwallet(const JSONRPCRequest& request) CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); CKeyID keyid = pubkey.GetID(); - if (pwalletMain->HaveKey(keyid)) { + if (pwallet->HaveKey(keyid)) { LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); continue; } @@ -408,21 +434,21 @@ UniValue importwallet(const JSONRPCRequest& request) } } LogPrintf("Importing %s...\n", EncodeDestination(keyid)); - if (!pwalletMain->AddKeyPubKey(key, pubkey)) { + if (!pwallet->AddKeyPubKey(key, pubkey)) { fGood = false; continue; } - pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime; + pwallet->mapKeyMetadata[keyid].nCreateTime = nTime; if (fLabel) // TODO: This is not entirely true.. needs to be reviewed properly. - pwalletMain->SetAddressBook(keyid, strLabel, AddressBook::AddressBookPurpose::RECEIVE); + pwallet->SetAddressBook(keyid, strLabel, AddressBook::AddressBookPurpose::RECEIVE); nTimeBegin = std::min(nTimeBegin, nTime); } file.close(); - pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI - pwalletMain->UpdateTimeFirstKey(nTimeBegin); + pwallet->ShowProgress("", 100); // hide progress dialog in GUI + pwallet->UpdateTimeFirstKey(nTimeBegin); } - pwalletMain->RescanFromTime(nTimeBegin, reserver, false /* update */); - pwalletMain->MarkDirty(); + pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */); + pwallet->MarkDirty(); if (!fGood) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet"); @@ -432,12 +458,17 @@ UniValue importwallet(const JSONRPCRequest& request) UniValue dumpprivkey(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "dumpprivkey \"pivxaddress\"\n" "\nReveals the private key corresponding to 'pivxaddress'.\n" "Then the importprivkey can be used with this output\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"pivxaddress\" (string, required) The pivx address for the private key\n" @@ -448,9 +479,9 @@ UniValue dumpprivkey(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("dumpprivkey", "\"myaddress\"") + HelpExampleCli("importprivkey", "\"mykey\"") + HelpExampleRpc("dumpprivkey", "\"myaddress\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); CTxDestination dest = DecodeDestination(strAddress); @@ -460,18 +491,23 @@ UniValue dumpprivkey(const JSONRPCRequest& request) if (!keyID) throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); CKey vchSecret; - if (!pwalletMain->GetKey(*keyID, vchSecret)) + if (!pwallet->GetKey(*keyID, vchSecret)) throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); return EncodeSecret(vchSecret); } UniValue dumpwallet(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "dumpwallet \"filename\"\n" "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename\n" @@ -492,13 +528,13 @@ UniValue dumpwallet(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); - ScriptPubKeyMan* spk_man = pwalletMain->GetScriptPubKeyMan(); + ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(); fs::path filepath = request.params[0].get_str().c_str(); filepath = fs::absolute(filepath); @@ -518,7 +554,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); std::map mapKeyBirth; - pwalletMain->GetKeyBirthTimes(mapKeyBirth); + pwallet->GetKeyBirthTimes(mapKeyBirth); const std::map& mapKeyPool = spk_man->GetAllReserveKeys(); @@ -548,7 +584,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) if (!seed_id.IsNull()) { CKey seed; - if (pwalletMain->GetKey(seed_id, seed)) { + if (pwallet->GetKey(seed_id, seed)) { CExtKey masterKey; masterKey.SetSeed(seed.begin(), seed.size()); @@ -560,15 +596,15 @@ UniValue dumpwallet(const JSONRPCRequest& request) const CKeyID& keyid = it->second; std::string strTime = FormatISO8601DateTime(it->first); CKey key; - if (pwalletMain->GetKey(keyid, key)) { - const CKeyMetadata& metadata = pwalletMain->mapKeyMetadata[keyid]; + if (pwallet->GetKey(keyid, key)) { + const CKeyMetadata& metadata = pwallet->mapKeyMetadata[keyid]; std::string strAddr = EncodeDestination(keyid, (metadata.HasKeyOrigin() && IsStakingDerPath(metadata.key_origin) ? CChainParams::STAKING_ADDRESS : CChainParams::PUBKEY_ADDRESS)); file << strprintf("%s %s ", KeyIO::EncodeSecret(key), strTime); - if (pwalletMain->HasAddressBook(keyid)) { - file << strprintf("label=%s", EncodeDumpString(pwalletMain->GetNameForAddressBookEntry(keyid))); + if (pwallet->HasAddressBook(keyid)) { + file << strprintf("label=%s", EncodeDumpString(pwallet->GetNameForAddressBookEntry(keyid))); } else if (keyid == seed_id) { file << "hdseed=1"; } else if (mapKeyPool.count(keyid)) { @@ -584,13 +620,13 @@ UniValue dumpwallet(const JSONRPCRequest& request) file << "# Sapling keys\n"; file << "\n"; std::set saplingAddresses; - pwalletMain->GetSaplingPaymentAddresses(saplingAddresses); + pwallet->GetSaplingPaymentAddresses(saplingAddresses); file << "\n"; for (const auto& addr : saplingAddresses) { libzcash::SaplingExtendedSpendingKey extsk; - if (pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)) { + if (pwallet->GetSaplingExtendedSpendingKey(addr, extsk)) { auto ivk = extsk.expsk.full_viewing_key().in_viewing_key(); - CKeyMetadata keyMeta = pwalletMain->GetSaplingScriptPubKeyMan()->mapSaplingZKeyMetadata[ivk]; + CKeyMetadata keyMeta = pwallet->GetSaplingScriptPubKeyMan()->mapSaplingZKeyMetadata[ivk]; std::string strTime = FormatISO8601DateTime(keyMeta.nCreateTime); // Keys imported with importsaplingkey do not have key origin metadata file << strprintf("%s %s # shielded_addr=%s%s\n", @@ -613,7 +649,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) return reply; } -UniValue processImport(const UniValue& data, const int64_t timestamp) +static UniValue processImport(CWallet* const pwallet, const UniValue& data, const int64_t timestamp) { try { bool success = false; @@ -694,32 +730,32 @@ UniValue processImport(const UniValue& data, const int64_t timestamp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script"); } - pwalletMain->MarkDirty(); + pwallet->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(redeemScript) && !pwalletMain->AddWatchOnly(redeemScript)) { + if (!pwallet->HaveWatchOnly(redeemScript) && !pwallet->AddWatchOnly(redeemScript)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } - if (!pwalletMain->HaveCScript(redeemScript) && !pwalletMain->AddCScript(redeemScript)) { + if (!pwallet->HaveCScript(redeemScript) && !pwallet->AddCScript(redeemScript)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } CTxDestination redeem_dest = CScriptID(redeemScript); CScript redeemDestination = GetScriptForDestination(redeem_dest); - if (::IsMine(*pwalletMain, redeemDestination) == ISMINE_SPENDABLE) { + if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } - pwalletMain->MarkDirty(); + pwallet->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(redeemDestination) && !pwalletMain->AddWatchOnly(redeemDestination)) { + if (!pwallet->HaveWatchOnly(redeemDestination) && !pwallet->AddWatchOnly(redeemDestination)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } // add to address book or update label if (IsValidDestination(dest)) { - pwalletMain->SetAddressBook(dest, label, "receive"); + pwallet->SetAddressBook(dest, label, "receive"); } // Import private keys. @@ -736,21 +772,21 @@ UniValue processImport(const UniValue& data, const int64_t timestamp) assert(key.VerifyPubKey(pubkey)); CKeyID vchAddress = pubkey.GetID(); - pwalletMain->MarkDirty(); - pwalletMain->SetAddressBook(vchAddress, label, "receive"); + pwallet->MarkDirty(); + pwallet->SetAddressBook(vchAddress, label, "receive"); - if (pwalletMain->HaveKey(vchAddress)) { + if (pwallet->HaveKey(vchAddress)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key"); } - pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = timestamp; + pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; - if (!pwalletMain->AddKeyPubKey(key, pubkey)) { + if (!pwallet->AddKeyPubKey(key, pubkey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } - if (timestamp < pwalletMain->nTimeFirstKey) { - pwalletMain->nTimeFirstKey = timestamp; + if (timestamp < pwallet->nTimeFirstKey) { + pwallet->nTimeFirstKey = timestamp; } } } @@ -789,31 +825,31 @@ UniValue processImport(const UniValue& data, const int64_t timestamp) CScript pubKeyScript = GetScriptForDestination(pubkey_dest); - if (::IsMine(*pwalletMain, pubKeyScript) == ISMINE_SPENDABLE) { + if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } - pwalletMain->MarkDirty(); + pwallet->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(pubKeyScript) && !pwalletMain->AddWatchOnly(pubKeyScript)) { + if (!pwallet->HaveWatchOnly(pubKeyScript) && !pwallet->AddWatchOnly(pubKeyScript)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } // add to address book or update label if (IsValidDestination(pubkey_dest)) { - pwalletMain->SetAddressBook(pubkey_dest, label, "receive"); + pwallet->SetAddressBook(pubkey_dest, label, "receive"); } // TODO Is this necessary? CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey); - if (::IsMine(*pwalletMain, scriptRawPubKey) == ISMINE_SPENDABLE) { + if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } - pwalletMain->MarkDirty(); + pwallet->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(scriptRawPubKey) && !pwalletMain->AddWatchOnly(scriptRawPubKey)) { + if (!pwallet->HaveWatchOnly(scriptRawPubKey) && !pwallet->AddWatchOnly(scriptRawPubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } @@ -848,21 +884,21 @@ UniValue processImport(const UniValue& data, const int64_t timestamp) } CKeyID vchAddress = pubKey.GetID(); - pwalletMain->MarkDirty(); - pwalletMain->SetAddressBook(vchAddress, label, "receive"); + pwallet->MarkDirty(); + pwallet->SetAddressBook(vchAddress, label, "receive"); - if (pwalletMain->HaveKey(vchAddress)) { + if (pwallet->HaveKey(vchAddress)) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } - pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = timestamp; + pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; - if (!pwalletMain->AddKeyPubKey(key, pubKey)) { + if (!pwallet->AddKeyPubKey(key, pubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } - if (timestamp < pwalletMain->nTimeFirstKey) { - pwalletMain->nTimeFirstKey = timestamp; + if (timestamp < pwallet->nTimeFirstKey) { + pwallet->nTimeFirstKey = timestamp; } success = true; @@ -870,20 +906,20 @@ UniValue processImport(const UniValue& data, const int64_t timestamp) // Import scriptPubKey only. if (pubKeys.size() == 0 && keys.size() == 0) { - if (::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) { + if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } - pwalletMain->MarkDirty(); + pwallet->MarkDirty(); - if (!pwalletMain->HaveWatchOnly(script) && !pwalletMain->AddWatchOnly(script)) { + if (!pwallet->HaveWatchOnly(script) && !pwallet->AddWatchOnly(script)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } if (scriptPubKey.getType() == UniValue::VOBJ) { // add to address book or update label if (IsValidDestination(dest)) { - pwalletMain->SetAddressBook(dest, label, "receive"); + pwallet->SetAddressBook(dest, label, "receive"); } } @@ -923,11 +959,16 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now) UniValue importmulti(const JSONRPCRequest& mainRequest) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(mainRequest); + + if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) + return NullUniValue; + if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2) throw std::runtime_error( "importmulti \"requests\" ( \"options\" )\n" "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options).\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. requests (array, required) Data to be imported\n" @@ -972,8 +1013,6 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) "{ \"scriptPubKey\": { \"address\": \"\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") + HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")); - EnsureWallet(); - RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ}); const UniValue& requests = mainRequest.params[0]; @@ -988,7 +1027,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } } - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (fRescan && !reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -999,8 +1038,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) UniValue response(UniValue::VARR); { - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); + LOCK2(cs_main, pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); // Verify all timestamps are present before importing any keys. int64_t now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0; @@ -1018,7 +1057,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) for (const UniValue& data: requests.getValues()) { const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp); - const UniValue result = processImport(data, timestamp); + const UniValue result = processImport(pwallet, data, timestamp); response.push_back(result); if (!fRescan) { @@ -1037,8 +1076,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) } } if (fRescan && fRunScan && requests.size()) { - int64_t scannedTime = pwalletMain->RescanFromTime(nLowestTimestamp, reserver, true /* update */); - pwalletMain->ReacceptWalletTransactions(); + int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */); + pwallet->ReacceptWalletTransactions(); if (scannedTime > nLowestTimestamp) { std::vector results = response.getValues(); @@ -1076,11 +1115,16 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) UniValue bip38encrypt(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 2) throw std::runtime_error( "bip38encrypt \"pivxaddress\" \"passphrase\"\n" "\nEncrypts a private key corresponding to 'pivxaddress'.\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"pivxaddress\" (string, required) The pivx address for the private key (you must hold the key already)\n" @@ -1093,9 +1137,9 @@ UniValue bip38encrypt(const JSONRPCRequest& request) HelpExampleCli("bip38encrypt", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"mypasphrase\"") + HelpExampleRpc("bip38encrypt", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"mypasphrase\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); std::string strPassphrase = request.params[1].get_str(); @@ -1107,7 +1151,7 @@ UniValue bip38encrypt(const JSONRPCRequest& request) if (!keyID) throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); CKey vchSecret; - if (!pwalletMain->GetKey(*keyID, vchSecret)) + if (!pwallet->GetKey(*keyID, vchSecret)) throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); uint256 privKey = vchSecret.GetPrivKey_256(); @@ -1122,11 +1166,16 @@ UniValue bip38encrypt(const JSONRPCRequest& request) UniValue bip38decrypt(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 2) throw std::runtime_error( "bip38decrypt \"pivxaddress\" \"passphrase\"\n" "\nDecrypts and then imports password protected private key.\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"encryptedkey\" (string, required) The encrypted private key\n" @@ -1158,7 +1207,7 @@ UniValue bip38decrypt(const JSONRPCRequest& request) if (!key.IsValid()) throw JSONRPCError(RPC_WALLET_ERROR, "Private Key Not Valid"); - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (!reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -1168,23 +1217,23 @@ UniValue bip38decrypt(const JSONRPCRequest& request) result.pushKV("Address", EncodeDestination(pubkey.GetID())); CKeyID vchAddress = pubkey.GetID(); { - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); - pwalletMain->MarkDirty(); - pwalletMain->SetAddressBook(vchAddress, "", AddressBook::AddressBookPurpose::RECEIVE); + LOCK2(cs_main, pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); + pwallet->MarkDirty(); + pwallet->SetAddressBook(vchAddress, "", AddressBook::AddressBookPurpose::RECEIVE); // Don't throw error in case a key is already there - if (pwalletMain->HaveKey(vchAddress)) + if (pwallet->HaveKey(vchAddress)) throw JSONRPCError(RPC_WALLET_ERROR, "Key already held by wallet"); - pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; + pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1; - if (!pwalletMain->AddKeyPubKey(key, pubkey)) + if (!pwallet->AddKeyPubKey(key, pubkey)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); } // whenever a key is imported, we need to scan the whole chain - pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); + pwallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); return result; } @@ -1193,11 +1242,16 @@ UniValue bip38decrypt(const JSONRPCRequest& request) UniValue importsaplingkey(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( "importsaplingkey \"key\" ( rescan startHeight )\n" "\nAdds a key (as returned by exportsaplingkey) to your wallet.\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"key\" (string, required) The zkey (see exportsaplingkey)\n" @@ -1223,8 +1277,6 @@ UniValue importsaplingkey(const JSONRPCRequest& request) + HelpExampleRpc("importsaplingkey", "\"mykey\", \"no\"") ); - EnsureWallet(); - // Whether to perform rescan after import bool fRescan = true; bool fIgnoreExistingKey = true; @@ -1242,7 +1294,7 @@ UniValue importsaplingkey(const JSONRPCRequest& request) } } - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (fRescan && !reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -1250,8 +1302,8 @@ UniValue importsaplingkey(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); CBlockIndex* pindexRescan{nullptr}; { - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); + LOCK2(cs_main, pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); // Height to rescan from int nRescanHeight = 0; @@ -1271,11 +1323,11 @@ UniValue importsaplingkey(const JSONRPCRequest& request) result.pushKV("address", KeyIO::EncodePaymentAddress( saplingSpendingKey.DefaultAddress())); // Sapling support - auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet(Params().GetConsensus(), saplingSpendingKey, -1); + auto addResult = pwallet->GetSaplingScriptPubKeyMan()->AddSpendingKeyToWallet(Params().GetConsensus(), saplingSpendingKey, -1); if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { return result; } - pwalletMain->MarkDirty(); + pwallet->MarkDirty(); if (addResult == KeyNotAdded) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet"); } @@ -1284,7 +1336,7 @@ UniValue importsaplingkey(const JSONRPCRequest& request) // We want to scan for transactions and notes if (fRescan) { - pwalletMain->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true); + pwallet->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true); } return result; @@ -1292,11 +1344,16 @@ UniValue importsaplingkey(const JSONRPCRequest& request) UniValue importsaplingviewingkey(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( "importsaplingviewingkey \"vkey\" ( rescan startHeight )\n" "\nAdds a viewing key (as returned by exportsaplingviewingkey) to your wallet.\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"vkey\" (string, required) The viewing key (see exportsaplingviewingkey)\n" @@ -1322,8 +1379,6 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) + HelpExampleRpc("importsaplingviewingkey", "\"vkey\", \"no\"") ); - EnsureWallet(); - // Whether to perform rescan after import bool fRescan = true; bool fIgnoreExistingKey = true; @@ -1341,7 +1396,7 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) } } - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (fRescan && !reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -1349,8 +1404,8 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); CBlockIndex* pindexRescan{nullptr}; { - LOCK2(cs_main, pwalletMain->cs_wallet); - EnsureWalletIsUnlocked(); + LOCK2(cs_main, pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); // Height to rescan from int nRescanHeight = 0; @@ -1369,7 +1424,7 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) libzcash::SaplingExtendedFullViewingKey efvk = *boost::get(&viewingkey); result.pushKV("address", KeyIO::EncodePaymentAddress(efvk.DefaultAddress())); - auto addResult = pwalletMain->GetSaplingScriptPubKeyMan()->AddViewingKeyToWallet(efvk); + auto addResult = pwallet->GetSaplingScriptPubKeyMan()->AddViewingKeyToWallet(efvk); if (addResult == SpendingKeyExists) { throw JSONRPCError( RPC_WALLET_ERROR, @@ -1377,7 +1432,7 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) } else if (addResult == KeyAlreadyExists && fIgnoreExistingKey) { return result; } - pwalletMain->MarkDirty(); + pwallet->MarkDirty(); if (addResult == KeyNotAdded) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet"); } @@ -1387,7 +1442,7 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) // We want to scan for transactions and notes if (fRescan) { - pwalletMain->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true); + pwallet->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true); } return result; @@ -1395,12 +1450,17 @@ UniValue importsaplingviewingkey(const JSONRPCRequest& request) UniValue exportsaplingviewingkey(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "exportsaplingviewingkey \"shield_addr\"\n" "\nReveals the viewing key corresponding to 'shield_addr'.\n" "Then the importsaplingviewingkey can be used with this output\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"shield_addr\" (string, required) The shield addr for the viewing key\n" @@ -1413,10 +1473,9 @@ UniValue exportsaplingviewingkey(const JSONRPCRequest& request) + HelpExampleRpc("exportsaplingviewingkey", "\"myaddress\"") ); - EnsureWallet(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); auto address = KeyIO::DecodePaymentAddress(strAddress); @@ -1424,7 +1483,7 @@ UniValue exportsaplingviewingkey(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid shield addr"); } const libzcash::SaplingPaymentAddress &sapAddr = *boost::get(&address); - auto vk = pwalletMain->GetSaplingScriptPubKeyMan()->GetViewingKeyForPaymentAddress(sapAddr); + auto vk = pwallet->GetSaplingScriptPubKeyMan()->GetViewingKeyForPaymentAddress(sapAddr); if (vk) { return KeyIO::EncodeViewingKey(vk.get()); } else { @@ -1434,12 +1493,17 @@ UniValue exportsaplingviewingkey(const JSONRPCRequest& request) UniValue exportsaplingkey(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "exportsaplingkey \"shield_addr\"\n" "\nReveals the key corresponding to the 'shield_addr'.\n" "Then the importsaplingkey can be used with this output\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"addr\" (string, required) The shield addr for the private key\n" @@ -1452,11 +1516,9 @@ UniValue exportsaplingkey(const JSONRPCRequest& request) + HelpExampleRpc("exportsaplingkey", "\"myaddress\"") ); - EnsureWallet(); - - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); @@ -1467,7 +1529,7 @@ UniValue exportsaplingkey(const JSONRPCRequest& request) libzcash::SaplingPaymentAddress addr = *boost::get(&address); // Sapling support - Optional sk = pwalletMain->GetSaplingScriptPubKeyMan()->GetSpendingKeyForPaymentAddress(addr); + Optional sk = pwallet->GetSaplingScriptPubKeyMan()->GetSpendingKeyForPaymentAddress(addr); if (!sk) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key for this shield addr"); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ab2b664223e7..f31dac7332b2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5,6 +5,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "wallet/rpcwallet.h" + #include "addressbook.h" #include "amount.h" #include "base58.h" @@ -17,41 +19,47 @@ #include "net.h" #include "policy/feerate.h" #include "rpc/server.h" -#include "timedata.h" -#include "util/system.h" -#include "utilmoneystr.h" -#include "wallet.h" -#include "walletdb.h" -#include "zpivchain.h" - #include "sapling/sapling_operation.h" #include "sapling/transaction_builder.h" #include "sapling/key_io_sapling.h" +#include "spork.h" +#include "timedata.h" +#include "utilmoneystr.h" +#include "wallet/wallet.h" +#include "wallet/walletdb.h" +#include "zpivchain.h" #include +#include -#include "spork.h" -#include -int64_t nWalletUnlockTime; -static RecursiveMutex cs_nWalletUnlockTime; +CWallet* GetWalletForJSONRPCRequest(const JSONRPCRequest& request) +{ + // TODO: Some way to access secondary wallets + return vpwallets.empty() ? nullptr : vpwallets[0]; +} -std::string HelpRequiringPassphrase() +std::string HelpRequiringPassphrase(CWallet* const pwallet) { - return pwalletMain && pwalletMain->IsCrypted() ? "\nRequires wallet passphrase to be set with walletpassphrase call." : ""; + return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to be set with walletpassphrase call." : ""; } -void EnsureWalletIsUnlocked(bool fAllowAnonOnly) +bool EnsureWalletIsAvailable(CWallet* const pwallet, bool avoidException) { - if (pwalletMain->IsLocked() || (!fAllowAnonOnly && pwalletMain->fWalletUnlockStaking)) - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + if (!pwallet) { + if (!avoidException) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet disabled)"); + else + return false; + } + return true; } -void EnsureWallet() +void EnsureWalletIsUnlocked(CWallet* const pwallet, bool fAllowAnonOnly) { - if (!pwalletMain) - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: No wallet loaded in the system"); + if (pwallet->IsLocked() || (!fAllowAnonOnly && pwallet->fWalletUnlockStaking)) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); } void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) @@ -88,17 +96,17 @@ std::string LabelFromValue(const UniValue& value) return label; } -CTxDestination GetNewAddressFromLabel(const std::string purpose, const UniValue ¶ms, - const CChainParams::Base58Type addrType = CChainParams::PUBKEY_ADDRESS) +static CTxDestination GetNewAddressFromLabel(CWallet* const pwallet, const std::string purpose, const UniValue ¶ms, + const CChainParams::Base58Type addrType = CChainParams::PUBKEY_ADDRESS) { - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label; if (!params.isNull() && params.size() > 0) label = LabelFromValue(params[0]); CTxDestination address; - PairResult r = pwalletMain->getNewAddress(address, label, purpose, addrType); + PairResult r = pwallet->getNewAddress(address, label, purpose, addrType); if(!r.result) throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, *r.status); return address; @@ -125,11 +133,10 @@ bool HaveKey(const CWallet* wallet, const CKey& key) UniValue getaddressinfo(const JSONRPCRequest& request) { - CWallet* const pwallet = pwalletMain; + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!pwallet) { + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - } const std::string example_address = "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\""; @@ -198,7 +205,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request) //ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(); - SaplingScriptPubKeyMan* sspk_man = pwalletMain->GetSaplingScriptPubKeyMan(); + SaplingScriptPubKeyMan* sspk_man = pwallet->GetSaplingScriptPubKeyMan(); CKeyMetadata* meta = nullptr; if (spk_man && pTransDest) { @@ -214,7 +221,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request) // shield destination const libzcash::SaplingPaymentAddress pa = *Standard::GetShieldedDestination(dest); libzcash::SaplingExtendedSpendingKey extsk; - if (pwalletMain->GetSaplingExtendedSpendingKey(pa, extsk)) { + if (pwallet->GetSaplingExtendedSpendingKey(pa, extsk)) { const auto& ivk = extsk.expsk.full_viewing_key().in_viewing_key(); auto it = sspk_man->mapSaplingZKeyMetadata.find(ivk); if (it != sspk_man->mapSaplingZKeyMetadata.end()) { @@ -253,10 +260,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request) UniValue getaddressesbylabel(const JSONRPCRequest& request) { - CWallet* const pwallet = pwalletMain; - if (!pwallet) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - } if (request.fHelp || request.params.size() != 1) throw std::runtime_error( @@ -284,7 +291,7 @@ UniValue getaddressesbylabel(const JSONRPCRequest& request) // Find all addresses that have the given label UniValue ret(UniValue::VOBJ); - for (auto it = pwalletMain->NewAddressBookIterator(); it.IsValid(); it.Next()) { + for (auto it = pwallet->NewAddressBookIterator(); it.IsValid(); it.Next()) { auto addrBook = it.GetValue(); if (!addrBook.isShielded() && addrBook.name == label) { ret.pushKV(EncodeDestination(*it.GetCTxDestKey(), AddressBook::IsColdStakingPurpose(addrBook.purpose)), AddressBookDataToJSON(addrBook, false)); @@ -300,10 +307,10 @@ UniValue getaddressesbylabel(const JSONRPCRequest& request) UniValue listlabels(const JSONRPCRequest& request) { - CWallet* const pwallet = pwalletMain; - if (!pwallet) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - } if (request.fHelp || request.params.size() > 1) throw std::runtime_error( @@ -339,7 +346,7 @@ UniValue listlabels(const JSONRPCRequest& request) // Add to a set to sort by label name, then insert into Univalue array std::set label_set; - for (auto it = pwalletMain->NewAddressBookIterator(); it.IsValid(); it.Next()) { + for (auto it = pwallet->NewAddressBookIterator(); it.IsValid(); it.Next()) { auto addrBook = it.GetValue(); if (purpose.empty() || addrBook.purpose == purpose) { label_set.insert(addrBook.name); @@ -369,6 +376,11 @@ CPubKey parseWIFKey(std::string strKey, CWallet* pwallet) UniValue upgradewallet(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) throw std::runtime_error("upgradewallet\n" "Bump the wallet features to the latest supported version. Non-HD wallets will be upgraded to HD wallet functionality. " @@ -378,28 +390,28 @@ UniValue upgradewallet(const JSONRPCRequest& request) "Enabling the Sapling key manager. Sapling keys will be deterministically derived by the same HD wallet seed.\n" "Wallets that are running the latest Sapling version will not be upgraded" "\nNote that you will need to MAKE A NEW BACKUP of your wallet after upgrade it.\n" + + HelpRequiringPassphrase(pwallet) + "\n" + HelpExampleCli("upgradewallet", "") + HelpExampleRpc("upgradewallet", "") ); - EnsureWallet(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); // Do not do anything to wallets already upgraded - if (pwalletMain->CanSupportFeature(FEATURE_LATEST)) { + if (pwallet->CanSupportFeature(FEATURE_LATEST)) { throw JSONRPCError(RPC_WALLET_ERROR, "Cannot upgrade the wallet. The wallet is already running the latest version"); } - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); // Get version - int prev_version = pwalletMain->GetVersion(); + int prev_version = pwallet->GetVersion(); // Upgrade wallet's version - pwalletMain->SetMinVersion(FEATURE_LATEST); - pwalletMain->SetMaxVersion(FEATURE_LATEST); + pwallet->SetMinVersion(FEATURE_LATEST); + pwallet->SetMaxVersion(FEATURE_LATEST); // Upgrade to HD std::string upgradeError; - if (!pwalletMain->Upgrade(upgradeError, prev_version)) { + if (!pwallet->Upgrade(upgradeError, prev_version)) { upgradeError = strprintf("Error: Cannot upgrade wallet, %s", upgradeError); throw JSONRPCError(RPC_WALLET_ERROR, upgradeError); } @@ -409,17 +421,18 @@ UniValue upgradewallet(const JSONRPCRequest& request) UniValue sethdseed(const JSONRPCRequest& request) { - CWallet* pwallet = pwalletMain; + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!pwallet) { + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - } if (request.fHelp || request.params.size() > 2) throw std::runtime_error("sethdseed ( newkeypool \"seed\" )\n" "Set or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n" "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n" - "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n\n" + "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n" + + HelpRequiringPassphrase(pwallet) + "\n" + "\nArguments:\n" "1. newkeypool (boolean, optional, default true): Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n" " If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n" @@ -439,7 +452,7 @@ UniValue sethdseed(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); @@ -474,6 +487,11 @@ UniValue sethdseed(const JSONRPCRequest& request) UniValue getnewaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "getnewaddress ( \"label\" )\n" @@ -490,11 +508,15 @@ UniValue getnewaddress(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getnewaddress", "") + HelpExampleRpc("getnewaddress", "")); - return EncodeDestination(GetNewAddressFromLabel(AddressBook::AddressBookPurpose::RECEIVE, request.params)); + return EncodeDestination(GetNewAddressFromLabel(pwallet, AddressBook::AddressBookPurpose::RECEIVE, request.params)); } UniValue getnewstakingaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; if (request.fHelp || request.params.size() > 1) throw std::runtime_error( @@ -511,16 +533,21 @@ UniValue getnewstakingaddress(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getnewstakingaddress", "") + HelpExampleRpc("getnewstakingaddress", "")); - return EncodeDestination(GetNewAddressFromLabel("coldstaking", request.params, CChainParams::STAKING_ADDRESS), CChainParams::STAKING_ADDRESS); + return EncodeDestination(GetNewAddressFromLabel(pwallet, "coldstaking", request.params, CChainParams::STAKING_ADDRESS), CChainParams::STAKING_ADDRESS); } UniValue getnewshieldaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "getnewshieldaddress\n" "\nReturns a new shield address for receiving payments.\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nResult:\n" "\"address\" (string) The new shield address.\n" @@ -530,17 +557,20 @@ UniValue getnewshieldaddress(const JSONRPCRequest& request) + HelpExampleRpc("getnewshieldaddress", "") ); - EnsureWallet(); - - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); - return KeyIO::EncodePaymentAddress(pwalletMain->GenerateNewSaplingZKey()); + return KeyIO::EncodePaymentAddress(pwallet->GenerateNewSaplingZKey()); } UniValue listshieldunspent(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 4) throw std::runtime_error( "listshieldunspent ( minconf maxconf includeWatchonly [\"shield_addr\",...] )\n" @@ -580,8 +610,6 @@ UniValue listshieldunspent(const JSONRPCRequest& request) + HelpExampleRpc("listshieldunspent", "6 9999999 false \"[\\\"ptestsapling1h0w73csah2aq0a32h42kr7tq4htlt5wfn4ejxfnm56f6ehjvek7k4e244g6v8v3pgylmz5ea8jh\\\",\\\"ptestsapling1h0w73csah2aq0a32h42kr7tq4htlt5wfn4ejxfnm56f6ehjvek7k4e244g6v8v3pgylmz5ea8jh\\\"]\"") ); - EnsureWallet(); - RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM, UniValue::VBOOL, UniValue::VARR}); int nMinDepth = request.params.size() > 0 ? request.params[0].get_int() : 1; @@ -597,7 +625,7 @@ UniValue listshieldunspent(const JSONRPCRequest& request) std::set shieldAddrs = {}; bool fIncludeWatchonly = request.params.size() > 2 && request.params[2].get_bool(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); // User has supplied shield addrs to filter on if (request.params.size() > 3) { @@ -619,7 +647,7 @@ UniValue listshieldunspent(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, address is not a valid shield address: ") + address); } libzcash::SaplingPaymentAddress paymentAddress = *boost::get(&shieldAddr); - bool hasSpendingKey = pwalletMain->HaveSpendingKeyForPaymentAddress(paymentAddress); + bool hasSpendingKey = pwallet->HaveSpendingKeyForPaymentAddress(paymentAddress); if (!fIncludeWatchonly && !hasSpendingKey) { throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, spending key for address does not belong to wallet: ") + address); } @@ -633,7 +661,7 @@ UniValue listshieldunspent(const JSONRPCRequest& request) } else { // User did not provide shield addrs, so use default i.e. all addresses std::set saplingzaddrs = {}; - pwalletMain->GetSaplingPaymentAddresses(saplingzaddrs); + pwallet->GetSaplingPaymentAddresses(saplingzaddrs); shieldAddrs.insert(saplingzaddrs.begin(), saplingzaddrs.end()); } @@ -641,23 +669,23 @@ UniValue listshieldunspent(const JSONRPCRequest& request) if (shieldAddrs.size() > 0) { std::vector saplingEntries; - pwalletMain->GetSaplingScriptPubKeyMan()->GetFilteredNotes(saplingEntries, shieldAddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); - std::set> nullifierSet = pwalletMain->GetSaplingScriptPubKeyMan()->GetNullifiersForAddresses(shieldAddrs); + pwallet->GetSaplingScriptPubKeyMan()->GetFilteredNotes(saplingEntries, shieldAddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); + std::set> nullifierSet = pwallet->GetSaplingScriptPubKeyMan()->GetNullifiersForAddresses(shieldAddrs); for (const auto& entry : saplingEntries) { UniValue obj(UniValue::VOBJ); obj.pushKV("txid", entry.op.hash.ToString()); obj.pushKV("outindex", (int)entry.op.n); obj.pushKV("confirmations", entry.confirmations); - bool hasSaplingSpendingKey = pwalletMain->HaveSpendingKeyForPaymentAddress(entry.address); + bool hasSaplingSpendingKey = pwallet->HaveSpendingKeyForPaymentAddress(entry.address); obj.pushKV("spendable", hasSaplingSpendingKey); obj.pushKV("address", KeyIO::EncodePaymentAddress(entry.address)); obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value() obj.pushKV("memo", HexStrTrimmed(entry.memo)); if (hasSaplingSpendingKey) { - obj.pushKV("change", pwalletMain->GetSaplingScriptPubKeyMan()->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); + obj.pushKV("change", pwallet->GetSaplingScriptPubKeyMan()->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); } - const auto& nd = pwalletMain->mapWallet.at(entry.op.hash).mapSaplingNoteData.at(entry.op); + const auto& nd = pwallet->mapWallet.at(entry.op.hash).mapSaplingNoteData.at(entry.op); if (nd.nullifier) { obj.pushKV("nullifier", nd.nullifier->ToString()); } @@ -670,6 +698,11 @@ UniValue listshieldunspent(const JSONRPCRequest& request) UniValue delegatoradd(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "delegatoradd \"addr\" ( \"label\" )\n" @@ -700,11 +733,16 @@ UniValue delegatoradd(const JSONRPCRequest& request) if (!keyID) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get KeyID from PIVX address"); - return pwalletMain->SetAddressBook(*keyID, strLabel, AddressBook::AddressBookPurpose::DELEGATOR); + return pwallet->SetAddressBook(*keyID, strLabel, AddressBook::AddressBookPurpose::DELEGATOR); } UniValue delegatorremove(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "delegatorremove \"addr\"\n" @@ -730,19 +768,19 @@ UniValue delegatorremove(const JSONRPCRequest& request) if (!keyID) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get KeyID from PIVX address"); - if (!pwalletMain->HasAddressBook(*keyID)) + if (!pwallet->HasAddressBook(*keyID)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get PIVX address from addressBook"); std::string label = ""; - auto optAdd = pwalletMain->GetAddressBookEntry(dest); + auto optAdd = pwallet->GetAddressBookEntry(dest); if (optAdd) { label = optAdd->name; } - return pwalletMain->SetAddressBook(*keyID, label, AddressBook::AddressBookPurpose::DELEGABLE); + return pwallet->SetAddressBook(*keyID, label, AddressBook::AddressBookPurpose::DELEGABLE); } -UniValue ListaddressesForPurpose(const std::string strPurpose) +static UniValue ListaddressesForPurpose(CWallet* const pwallet, const std::string strPurpose) { const CChainParams::Base58Type addrType = ( AddressBook::IsColdStakingPurpose(strPurpose) ? @@ -750,8 +788,8 @@ UniValue ListaddressesForPurpose(const std::string strPurpose) CChainParams::PUBKEY_ADDRESS); UniValue ret(UniValue::VARR); { - LOCK(pwalletMain->cs_wallet); - for (auto it = pwalletMain->NewAddressBookIterator(); it.IsValid(); it.Next()) { + LOCK(pwallet->cs_wallet); + for (auto it = pwallet->NewAddressBookIterator(); it.IsValid(); it.Next()) { auto addrBook = it.GetValue(); if (addrBook.purpose != strPurpose) continue; auto dest = it.GetCTxDestKey(); @@ -768,6 +806,11 @@ UniValue ListaddressesForPurpose(const std::string strPurpose) UniValue listdelegators(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "listdelegators ( fBlacklist )\n" @@ -792,12 +835,17 @@ UniValue listdelegators(const JSONRPCRequest& request) const bool fBlacklist = (request.params.size() > 0 ? request.params[0].get_bool() : false); return (fBlacklist ? - ListaddressesForPurpose(AddressBook::AddressBookPurpose::DELEGABLE) : - ListaddressesForPurpose(AddressBook::AddressBookPurpose::DELEGATOR)); + ListaddressesForPurpose(pwallet, AddressBook::AddressBookPurpose::DELEGABLE) : + ListaddressesForPurpose(pwallet, AddressBook::AddressBookPurpose::DELEGATOR)); } UniValue liststakingaddresses(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) throw std::runtime_error( "liststakingaddresses \"addr\"\n" @@ -816,11 +864,16 @@ UniValue liststakingaddresses(const JSONRPCRequest& request) HelpExampleCli("liststakingaddresses" , "") + HelpExampleRpc("liststakingaddresses", "")); - return ListaddressesForPurpose(AddressBook::AddressBookPurpose::COLD_STAKING); + return ListaddressesForPurpose(pwallet, AddressBook::AddressBookPurpose::COLD_STAKING); } UniValue listshieldaddresses(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "listshieldaddresses ( includeWatchonly )\n" @@ -840,8 +893,7 @@ UniValue listshieldaddresses(const JSONRPCRequest& request) + HelpExampleRpc("listshieldaddresses", "") ); - EnsureWallet(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); bool fIncludeWatchonly = false; if (request.params.size() > 0) { @@ -851,11 +903,11 @@ UniValue listshieldaddresses(const JSONRPCRequest& request) UniValue ret(UniValue::VARR); std::set addresses; - pwalletMain->GetSaplingPaymentAddresses(addresses); + pwallet->GetSaplingPaymentAddresses(addresses); libzcash::SaplingIncomingViewingKey ivk; libzcash::SaplingExtendedFullViewingKey extfvk; for (libzcash::SaplingPaymentAddress addr : addresses) { - if (fIncludeWatchonly || pwalletMain->HaveSpendingKeyForPaymentAddress(addr)) { + if (fIncludeWatchonly || pwallet->HaveSpendingKeyForPaymentAddress(addr)) { ret.push_back(KeyIO::EncodePaymentAddress(addr)); } } @@ -864,6 +916,11 @@ UniValue listshieldaddresses(const JSONRPCRequest& request) UniValue getrawchangeaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "getrawchangeaddress\n" @@ -876,12 +933,12 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getrawchangeaddress", "") + HelpExampleRpc("getrawchangeaddress", "")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - if (!pwalletMain->IsLocked()) - pwalletMain->TopUpKeyPool(); + if (!pwallet->IsLocked()) + pwallet->TopUpKeyPool(); - CReserveKey reservekey(pwalletMain); + CReserveKey reservekey(pwallet); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey, true)) throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); @@ -896,6 +953,11 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request) UniValue setlabel(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 2) throw std::runtime_error( "setlabel \"pivxaddress\" \"label\"\n" @@ -908,66 +970,63 @@ UniValue setlabel(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("setlabel", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" \"tabby\"") + HelpExampleRpc("setlabel", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", \"tabby\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str()); if (!IsValidDestination(dest)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address"); - std::string old_label = pwalletMain->GetNameForAddressBookEntry(dest); + std::string old_label = pwallet->GetNameForAddressBookEntry(dest); std::string label = LabelFromValue(request.params[1]); - pwalletMain->SetAddressBook(dest, label, ""); + pwallet->SetAddressBook(dest, label, ""); return NullUniValue; } -void SendMoney(const CTxDestination& address, CAmount nValue, CTransactionRef& tx) +static void SendMoney(CWallet* const pwallet, const CTxDestination& address, CAmount nValue, CTransactionRef& tx) { + LOCK2(cs_main, pwallet->cs_wallet); + // Check amount if (nValue <= 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); - if (nValue > pwalletMain->GetAvailableBalance()) + if (nValue > pwallet->GetAvailableBalance()) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); if (!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - std::string strError; - if (pwalletMain->IsLocked()) { - strError = "Error: Wallet locked, unable to create transaction!"; - LogPrintf("SendMoney() : %s", strError); - throw JSONRPCError(RPC_WALLET_ERROR, strError); - } - // Parse PIVX address CScript scriptPubKey = GetScriptForDestination(address); // Create and send the transaction - CReserveKey reservekey(pwalletMain); + CReserveKey reservekey(pwallet); CAmount nFeeRequired; - if (!pwalletMain->CreateTransaction(scriptPubKey, nValue, tx, reservekey, nFeeRequired, strError, nullptr, ALL_COINS, (CAmount)0)) { - if (nValue + nFeeRequired > pwalletMain->GetAvailableBalance()) + std::string strError; + if (!pwallet->CreateTransaction(scriptPubKey, nValue, tx, reservekey, nFeeRequired, strError, nullptr, ALL_COINS, (CAmount)0)) { + if (nValue + nFeeRequired > pwallet->GetAvailableBalance()) strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired)); - LogPrintf("SendMoney() : %s\n", strError); + LogPrintf("%s: %s\n", __func__, strError); throw JSONRPCError(RPC_WALLET_ERROR, strError); } - const CWallet::CommitResult&& res = pwalletMain->CommitTransaction(tx, reservekey, g_connman.get()); + const CWallet::CommitResult&& res = pwallet->CommitTransaction(tx, reservekey, g_connman.get()); if (res.status != CWallet::CommitStatus::OK) throw JSONRPCError(RPC_WALLET_ERROR, res.ToString()); } -static SaplingOperation CreateShieldedTransaction(const JSONRPCRequest& request); +static SaplingOperation CreateShieldedTransaction(CWallet* const pwallet, const JSONRPCRequest& request); /* * redirect sendtoaddress/sendmany inputs to shieldsendmany implementation (CreateShieldedTransaction) */ -static UniValue ShieldSendManyTo(const UniValue& sendTo, - const std::string& commentStr, - const std::string& toStr, - int nMinDepth, - bool fIncludeDelegated) +static UniValue ShieldSendManyTo(CWallet * const pwallet, + const UniValue& sendTo, + const std::string& commentStr, + const std::string& toStr, + int nMinDepth, + bool fIncludeDelegated) { // convert params to 'shieldsendmany' format JSONRPCRequest req; @@ -988,7 +1047,7 @@ static UniValue ShieldSendManyTo(const UniValue& sendTo, req.params.push_back(nMinDepth); // send - SaplingOperation operation = CreateShieldedTransaction(req); + SaplingOperation operation = CreateShieldedTransaction(pwallet, req); std::string txid; auto res = operation.send(txid); if (!res) @@ -996,12 +1055,12 @@ static UniValue ShieldSendManyTo(const UniValue& sendTo, // add comments const uint256& txHash = uint256S(txid); - assert(pwalletMain->mapWallet.count(txHash)); + assert(pwallet->mapWallet.count(txHash)); if (!commentStr.empty()) { - pwalletMain->mapWallet.at(txHash).mapValue["comment"] = commentStr; + pwallet->mapWallet.at(txHash).mapValue["comment"] = commentStr; } if (!toStr.empty()) { - pwalletMain->mapWallet.at(txHash).mapValue["to"] = toStr; + pwallet->mapWallet.at(txHash).mapValue["to"] = toStr; } return txid; @@ -1009,11 +1068,16 @@ static UniValue ShieldSendManyTo(const UniValue& sendTo, UniValue sendtoaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( "sendtoaddress \"pivxaddress\" amount ( \"comment\" \"comment-to\" )\n" "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"pivxaddress\" (string, required) The pivx address to send to.\n" @@ -1032,11 +1096,11 @@ UniValue sendtoaddress(const JSONRPCRequest& request) HelpExampleCli("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" 0.1 \"donation\" \"seans outpost\"") + HelpExampleRpc("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", 0.1, \"donation\", \"seans outpost\"")); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); bool isStaking = false, isShielded = false; const std::string addrStr = request.params[0].get_str(); @@ -1051,20 +1115,19 @@ UniValue sendtoaddress(const JSONRPCRequest& request) if (isShielded) { UniValue sendTo(UniValue::VOBJ); sendTo.pushKV(addrStr, request.params[1]); - return ShieldSendManyTo(sendTo, commentStr, toStr, 1, false); + return ShieldSendManyTo(pwallet, sendTo, commentStr, toStr, 1, false); } const CTxDestination& address = *Standard::GetTransparentDestination(destination); - LOCK2(cs_main, pwalletMain->cs_wallet); // Amount CAmount nAmount = AmountFromValue(request.params[1]); CTransactionRef tx; - SendMoney(address, nAmount, tx); + SendMoney(pwallet, address, nAmount, tx); // Wallet comments - CWalletTx& wtx = pwalletMain->mapWallet.at(tx->GetHash()); + CWalletTx& wtx = pwallet->mapWallet.at(tx->GetHash()); if (!commentStr.empty()) wtx.mapValue["comment"] = commentStr; if (!toStr.empty()) @@ -1073,9 +1136,9 @@ UniValue sendtoaddress(const JSONRPCRequest& request) return wtx.GetHash().GetHex(); } -UniValue CreateColdStakeDelegation(const UniValue& params, CTransactionRef& txNew, CReserveKey& reservekey) +static UniValue CreateColdStakeDelegation(CWallet* const pwallet, const UniValue& params, CTransactionRef& txNew, CReserveKey& reservekey) { - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); // Check that Cold Staking has been enforced or fForceNotEnabled = true bool fForceNotEnabled = false; @@ -1111,12 +1174,11 @@ UniValue CreateColdStakeDelegation(const UniValue& params, CTransactionRef& txNe fUseDelegated = params[4].get_bool(); // Check amount - CAmount currBalance = pwalletMain->GetAvailableBalance() + (fUseDelegated ? pwalletMain->GetDelegatedBalance() : 0); + CAmount currBalance = pwallet->GetAvailableBalance() + (fUseDelegated ? pwallet->GetDelegatedBalance() : 0); if (nValue > currBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); std::string strError; - EnsureWalletIsUnlocked(); // Get Owner Address std::string ownerAddressStr; @@ -1130,7 +1192,7 @@ UniValue CreateColdStakeDelegation(const UniValue& params, CTransactionRef& txNe ownerKey = *boost::get(&dest); // Check that the owner address belongs to this wallet, or fForceExternalAddr is true bool fForceExternalAddr = params.size() > 3 && !params[3].isNull() ? params[3].get_bool() : false; - if (!fForceExternalAddr && !pwalletMain->HaveKey(ownerKey)) { + if (!fForceExternalAddr && !pwallet->HaveKey(ownerKey)) { std::string errMsg = strprintf("The provided owneraddress \"%s\" is not present in this wallet.\n", params[2].get_str()); errMsg += "Set 'fExternalOwner' argument to true, in order to force the stake delegation to an external owner address.\n" "e.g. delegatestake stakingaddress amount owneraddress true.\n" @@ -1140,7 +1202,7 @@ UniValue CreateColdStakeDelegation(const UniValue& params, CTransactionRef& txNe ownerAddressStr = params[2].get_str(); } else { // Get new owner address from keypool - CTxDestination ownerAddr = GetNewAddressFromLabel("delegated", NullUniValue); + CTxDestination ownerAddr = GetNewAddressFromLabel(pwallet, "delegated", NullUniValue); CKeyID* pOwnerKey = boost::get(&ownerAddr); assert(pOwnerKey); ownerKey = *pOwnerKey; @@ -1157,7 +1219,7 @@ UniValue CreateColdStakeDelegation(const UniValue& params, CTransactionRef& txNe CAmount nFeeRequired; CScript scriptPubKey = fV6Enforced ? GetScriptForStakeDelegation(*stakeKey, ownerKey) : GetScriptForStakeDelegationLOF(*stakeKey, ownerKey); - if (!pwalletMain->CreateTransaction(scriptPubKey, nValue, txNew, reservekey, nFeeRequired, strError, nullptr, ALL_COINS, (CAmount)0, fUseDelegated)) { + if (!pwallet->CreateTransaction(scriptPubKey, nValue, txNew, reservekey, nFeeRequired, strError, nullptr, ALL_COINS, (CAmount)0, fUseDelegated)) { if (nValue + nFeeRequired > currBalance) strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired)); LogPrintf("%s : %s\n", __func__, strError); @@ -1172,7 +1234,7 @@ UniValue CreateColdStakeDelegation(const UniValue& params, CTransactionRef& txNe throw JSONRPCError(RPC_INVALID_PARAMETER, "SHIELD in maintenance (SPORK 20)"); } std::vector recipients = {SendManyRecipient(ownerKey, *stakeKey, nValue, fV6Enforced)}; - SaplingOperation operation(consensus, nextBlockHeight, pwalletMain); + SaplingOperation operation(consensus, nextBlockHeight, pwallet); OperationResult res = operation.setSelectShieldedCoins(true) ->setRecipients(recipients) ->build(); @@ -1188,11 +1250,16 @@ UniValue CreateColdStakeDelegation(const UniValue& params, CTransactionRef& txNe UniValue delegatestake(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 7) throw std::runtime_error( "delegatestake \"stakingaddress\" amount ( \"owneraddress\" fExternalOwner fUseDelegated fFromShield fForceNotEnabled )\n" "\nDelegate an amount to a given address for cold staking. The amount is a real and is rounded to the nearest 0.00000001\n" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"stakingaddress\" (string, required) The pivx staking address to delegate.\n" @@ -1217,17 +1284,19 @@ UniValue delegatestake(const JSONRPCRequest& request) HelpExampleCli("delegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\" 1000 \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg34fk\"") + HelpExampleRpc("delegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\", 1000, \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg34fk\"")); + EnsureWalletIsUnlocked(pwallet); + // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); CTransactionRef wtx; - CReserveKey reservekey(pwalletMain); - UniValue ret = CreateColdStakeDelegation(request.params, wtx, reservekey); + CReserveKey reservekey(pwallet); + UniValue ret = CreateColdStakeDelegation(pwallet, request.params, wtx, reservekey); - const CWallet::CommitResult& res = pwalletMain->CommitTransaction(wtx, reservekey, g_connman.get()); + const CWallet::CommitResult& res = pwallet->CommitTransaction(wtx, reservekey, g_connman.get()); if (res.status != CWallet::CommitStatus::OK) throw JSONRPCError(RPC_WALLET_ERROR, res.ToString()); @@ -1237,12 +1306,17 @@ UniValue delegatestake(const JSONRPCRequest& request) UniValue rawdelegatestake(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 7) throw std::runtime_error( "rawdelegatestake \"stakingaddress\" amount ( \"owneraddress\" fExternalOwner fUseDelegated fFromShield )\n" "\nDelegate an amount to a given address for cold staking. The amount is a real and is rounded to the nearest 0.00000001\n" "\nDelegate transaction is returned as json object." + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"stakingaddress\" (string, required) The pivx staking address to delegate.\n" @@ -1263,25 +1337,27 @@ UniValue rawdelegatestake(const JSONRPCRequest& request) HelpExampleCli("rawdelegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\" 1000 \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg34fk\"") + HelpExampleRpc("rawdelegatestake", "\"S1t2a3kab9c8c71VA78xxxy4MxZg6vgeS6\", 1000, \"DMJRSsuU9zfyrvxVaAEFQqK4MxZg34fk\"")); + EnsureWalletIsUnlocked(pwallet); + // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); CTransactionRef wtx; - CReserveKey reservekey(pwalletMain); - CreateColdStakeDelegation(request.params, wtx, reservekey); + CReserveKey reservekey(pwallet); + CreateColdStakeDelegation(pwallet, request.params, wtx, reservekey); return EncodeHexTx(*wtx); } -CAmount getBalanceShieldedAddr(Optional& filterAddress, int minDepth = 1, bool ignoreUnspendable=true) { +static CAmount getBalanceShieldedAddr(CWallet* const pwallet, Optional& filterAddress, int minDepth = 1, bool ignoreUnspendable=true) { CAmount balance = 0; std::vector saplingEntries; - LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->GetSaplingScriptPubKeyMan()->GetFilteredNotes(saplingEntries, filterAddress, minDepth, true, ignoreUnspendable); + LOCK2(cs_main, pwallet->cs_wallet); + pwallet->GetSaplingScriptPubKeyMan()->GetFilteredNotes(saplingEntries, filterAddress, minDepth, true, ignoreUnspendable); for (auto & entry : saplingEntries) { balance += CAmount(entry.note.value()); } @@ -1290,7 +1366,9 @@ CAmount getBalanceShieldedAddr(Optional& filter UniValue getshieldbalance(const JSONRPCRequest& request) { - if (!pwalletMain) + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; if (request.fHelp || request.params.size() > 3) @@ -1320,7 +1398,7 @@ UniValue getshieldbalance(const JSONRPCRequest& request) + HelpExampleRpc("getshieldbalance", "\"*\" \"5\"") ); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); Optional address; if (request.params.size() > 0) { @@ -1339,17 +1417,23 @@ UniValue getshieldbalance(const JSONRPCRequest& request) } const bool fIncludeWatchonly = request.params.size() > 2 && request.params[2].get_bool(); - CAmount nBalance = getBalanceShieldedAddr(address, nMinDepth, !fIncludeWatchonly); + CAmount nBalance = getBalanceShieldedAddr(pwallet, address, nMinDepth, !fIncludeWatchonly); return ValueFromAmount(nBalance); } UniValue viewshieldtransaction(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "viewshieldtransaction \"txid\"\n" "\nGet detailed shield information about in-wallet transaction \"txid\"\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" + "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" "\nResult:\n" @@ -1386,25 +1470,25 @@ UniValue viewshieldtransaction(const JSONRPCRequest& request) + HelpExampleRpc("viewshieldtransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); - if (!pwalletMain->HasSaplingSPKM()) { + if (!pwallet->HasSaplingSPKM()) { throw JSONRPCError(RPC_WALLET_ERROR, "Sapling wallet not initialized."); } - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; hash.SetHex(request.params[0].get_str()); UniValue entry(UniValue::VOBJ); - if (!pwalletMain->mapWallet.count(hash)) + if (!pwallet->mapWallet.count(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); - const CWalletTx& wtx = pwalletMain->mapWallet.at(hash); + const CWalletTx& wtx = pwallet->mapWallet.at(hash); if (!wtx.tx->IsShieldedTx()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid transaction, no shield data available"); @@ -1435,7 +1519,7 @@ UniValue viewshieldtransaction(const JSONRPCRequest& request) } }; - auto sspkm = pwalletMain->GetSaplingScriptPubKeyMan(); + auto sspkm = pwallet->GetSaplingScriptPubKeyMan(); // Collect OutgoingViewingKeys for recovering output information std::set ovks; @@ -1457,8 +1541,8 @@ UniValue viewshieldtransaction(const JSONRPCRequest& request) std::string addrStr = "unknown"; UniValue amountStr = UniValue("unknown"); CAmount amount = 0; - auto wtxPrevIt = pwalletMain->mapWallet.find(op.hash); - if (wtxPrevIt != pwalletMain->mapWallet.end()) { + auto wtxPrevIt = pwallet->mapWallet.find(op.hash); + if (wtxPrevIt != pwallet->mapWallet.end()) { const auto ndIt = wtxPrevIt->second.mapSaplingNoteData.find(op); if (ndIt != wtxPrevIt->second.mapSaplingNoteData.end()) { // get cached address and amount @@ -1518,12 +1602,11 @@ UniValue viewshieldtransaction(const JSONRPCRequest& request) return entry; } -static SaplingOperation CreateShieldedTransaction(const JSONRPCRequest& request) +static SaplingOperation CreateShieldedTransaction(CWallet* const pwallet, const JSONRPCRequest& request) { - EnsureWalletIsUnlocked(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); int nextBlockHeight = chainActive.Height() + 1; - SaplingOperation operation(Params().GetConsensus(), nextBlockHeight, pwalletMain); + SaplingOperation operation(Params().GetConsensus(), nextBlockHeight, pwallet); // Param 0: source of funds. Can either be a valid address, sapling address, // or the string "from_transparent"|"from_trans_cold"|"from_shield" @@ -1547,7 +1630,7 @@ static SaplingOperation CreateShieldedTransaction(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or shield addr."); } libzcash::SaplingPaymentAddress fromShieldedAddress = *boost::get(&res); - if (!pwalletMain->HaveSpendingKeyForPaymentAddress(fromShieldedAddress)) { + if (!pwallet->HaveSpendingKeyForPaymentAddress(fromShieldedAddress)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, shield addr spending key not found."); } // send from user-supplied shield address @@ -1674,13 +1757,19 @@ static SaplingOperation CreateShieldedTransaction(const JSONRPCRequest& request) UniValue shieldsendmany(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( "shieldsendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf fee )\n" "\nSend to many recipients. Amounts are decimal numbers with at most 8 digits of precision." "\nChange generated from a transparent addr flows to a new transparent addr address, while change generated from a shield addr returns to itself." "\nWhen sending coinbase UTXOs to a shield addr, change is not allowed. The entire value of the UTXO(s) must be consumed." - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" + "\nArguments:\n" "1. \"fromaddress\" (string, required) The transparent addr or shield addr to send the funds from.\n" " It can also be the string \"from_transparent\"|\"from_shield\" to send the funds\n" @@ -1706,11 +1795,13 @@ UniValue shieldsendmany(const JSONRPCRequest& request) "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", [{\"address\": \"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\" ,\"amount\": 5.0}]") ); + EnsureWalletIsUnlocked(pwallet); + // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - SaplingOperation operation = CreateShieldedTransaction(request); + SaplingOperation operation = CreateShieldedTransaction(pwallet, request); std::string txHash; auto res = operation.send(txHash); if (!res) @@ -1720,6 +1811,11 @@ UniValue shieldsendmany(const JSONRPCRequest& request) UniValue rawshieldsendmany(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( "rawshieldsendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf fee )\n" @@ -1727,7 +1823,8 @@ UniValue rawshieldsendmany(const JSONRPCRequest& request) "\nAmounts are decimal numbers with at most 8 digits of precision." "\nChange generated from a transparent addr flows to a new transparent addr address, while change generated from a shield addr returns to itself." "\nWhen sending coinbase UTXOs to a shield addr, change is not allowed. The entire value of the UTXO(s) must be consumed." - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" + "\nArguments:\n" "1. \"fromaddress\" (string, required) The transparent addr or shield addr to send the funds from.\n" " It can also be the string \"from_transparent\"|\"from_shield\" to send the funds\n" @@ -1754,16 +1851,23 @@ UniValue rawshieldsendmany(const JSONRPCRequest& request) "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", [{\"address\": \"ps1ra969yfhvhp73rw5ak2xvtcm9fkuqsnmad7qln79mphhdrst3lwu9vvv03yuyqlh42p42st47qd\" ,\"amount\": 5.0}]") ); + EnsureWalletIsUnlocked(pwallet); + // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - CTransaction tx = CreateShieldedTransaction(request).getFinalTx(); + CTransaction tx = CreateShieldedTransaction(pwallet, request).getFinalTx(); return EncodeHexTx(tx); } UniValue listaddressgroupings(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp) throw std::runtime_error( "listaddressgroupings\n" @@ -1789,19 +1893,19 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); - std::map balances = pwalletMain->GetAddressBalances(); - for (std::set grouping : pwalletMain->GetAddressGroupings()) { + std::map balances = pwallet->GetAddressBalances(); + for (std::set grouping : pwallet->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); for (CTxDestination address : grouping) { UniValue addressInfo(UniValue::VARR); addressInfo.push_back(EncodeDestination(address)); addressInfo.push_back(ValueFromAmount(balances[address])); - auto optAdd = pwalletMain->GetAddressBookEntry(address); + auto optAdd = pwallet->GetAddressBookEntry(address); if (optAdd) { addressInfo.push_back(optAdd->name); } @@ -1814,11 +1918,16 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) UniValue signmessage(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 2) throw std::runtime_error( "signmessage \"pivxaddress\" \"message\"\n" "\nSign a message with the private key of an address" + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"pivxaddress\" (string, required) The pivx address to use for the private key.\n" @@ -1837,9 +1946,9 @@ UniValue signmessage(const JSONRPCRequest& request) "\nAs json rpc\n" + HelpExampleRpc("signmessage", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", \"my message\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); @@ -1853,7 +1962,7 @@ UniValue signmessage(const JSONRPCRequest& request) throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); CKey key; - if (!pwalletMain->GetKey(*keyID, key)) + if (!pwallet->GetKey(*keyID, key)) throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); CHashWriter ss(SER_GETHASH, 0); @@ -1869,6 +1978,11 @@ UniValue signmessage(const JSONRPCRequest& request) UniValue getreceivedbyaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "getreceivedbyaddress \"pivxaddress\" ( minconf )\n" @@ -1893,9 +2007,9 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); int nBlockHeight = chainActive.Height(); // pivx address @@ -1903,7 +2017,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) if (!IsValidDestination(address)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address"); CScript scriptPubKey = GetScriptForDestination(address); - if (!IsMine(*pwalletMain, scriptPubKey)) + if (!IsMine(*pwallet, scriptPubKey)) throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet"); // Minimum confirmations @@ -1913,7 +2027,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) // Tally CAmount nAmount = 0; - for (std::map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + for (std::map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (wtx.IsCoinBase() || !IsFinalTx(wtx.tx, nBlockHeight)) continue; @@ -1930,6 +2044,11 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) UniValue getreceivedbylabel(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "getreceivedbylabel \"label\" ( minconf )\n" @@ -1954,9 +2073,9 @@ UniValue getreceivedbylabel(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); int nBlockHeight = chainActive.Height(); // Minimum confirmations @@ -1966,18 +2085,18 @@ UniValue getreceivedbylabel(const JSONRPCRequest& request) // Get the set of pub keys assigned to label std::string label = LabelFromValue(request.params[0]); - std::set setAddress = pwalletMain->GetLabelAddresses(label); + std::set setAddress = pwallet->GetLabelAddresses(label); // Tally CAmount nAmount = 0; - for (std::map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + for (std::map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (wtx.IsCoinBase() || !IsFinalTx(wtx.tx, nBlockHeight)) continue; for (const CTxOut& txout : wtx.tx->vout) { CTxDestination address; - if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) if (wtx.GetDepthInMainChain() >= nMinDepth) nAmount += txout.nValue; } @@ -1988,6 +2107,11 @@ UniValue getreceivedbylabel(const JSONRPCRequest& request) UniValue getbalance(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || (request.params.size() > 4 )) throw std::runtime_error( "getbalance ( minconf includeWatchonly includeDelegated includeShield )\n" @@ -2014,9 +2138,9 @@ UniValue getbalance(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); const int paramsSize = request.params.size(); const int nMinDepth = paramsSize > 0 ? request.params[0].get_int() : 0; @@ -2028,11 +2152,16 @@ UniValue getbalance(const JSONRPCRequest& request) (fIncludeShielded ? ISMINE_WATCH_ONLY_ALL : ISMINE_WATCH_ONLY) : ISMINE_NO); filter |= fIncludeDelegated ? ISMINE_SPENDABLE_DELEGATED : ISMINE_NO; filter |= fIncludeShielded ? ISMINE_SPENDABLE_SHIELDED : ISMINE_NO; - return ValueFromAmount(pwalletMain->GetAvailableBalance(filter, true, nMinDepth)); + return ValueFromAmount(pwallet->GetAvailableBalance(filter, true, nMinDepth)); } UniValue getcoldstakingbalance(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || (request.params.size() != 0)) throw std::runtime_error( "getcoldstakingbalance\n" @@ -2049,15 +2178,20 @@ UniValue getcoldstakingbalance(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - return ValueFromAmount(pwalletMain->GetColdStakingBalance()); + return ValueFromAmount(pwallet->GetColdStakingBalance()); } UniValue getdelegatedbalance(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || (request.params.size() != 0)) throw std::runtime_error( "getdelegatedbalance\n" @@ -2075,15 +2209,20 @@ UniValue getdelegatedbalance(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - return ValueFromAmount(pwalletMain->GetDelegatedBalance()); + return ValueFromAmount(pwallet->GetDelegatedBalance()); } UniValue getunconfirmedbalance(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 0) throw std::runtime_error( "getunconfirmedbalance\n" @@ -2091,19 +2230,19 @@ UniValue getunconfirmedbalance(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); + return ValueFromAmount(pwallet->GetUnconfirmedBalance()); } /* * Only used for t->t transactions (via sendmany RPC) */ -static UniValue legacy_sendmany(const UniValue& sendTo, int nMinDepth, std::string comment, bool fIncludeDelegated) +static UniValue legacy_sendmany(CWallet* const pwallet, const UniValue& sendTo, int nMinDepth, std::string comment, bool fIncludeDelegated) { - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); if (!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -2133,37 +2272,30 @@ static UniValue legacy_sendmany(const UniValue& sendTo, int nMinDepth, std::stri vecSend.emplace_back(scriptPubKey, nAmount, false); } - EnsureWalletIsUnlocked(); - // Check funds - if (totalAmount > pwalletMain->GetLegacyBalance(filter, nMinDepth)) { + if (totalAmount > pwallet->GetLegacyBalance(filter, nMinDepth)) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds"); } // Send - CReserveKey keyChange(pwalletMain); + CReserveKey keyChange(pwallet); CAmount nFeeRequired = 0; std::string strFailReason; int nChangePosInOut = -1; - bool fCreated = pwalletMain->CreateTransaction(vecSend, - txNew, - keyChange, - nFeeRequired, - nChangePosInOut, - strFailReason, - nullptr, // coinControl - ALL_COINS, // inputType - true, // sign - 0, // nFeePay - fIncludeDelegated); + bool fCreated = pwallet->CreateTransaction(vecSend, txNew, keyChange, nFeeRequired, nChangePosInOut, strFailReason, + nullptr, // coinControl + ALL_COINS, // inputType + true, // sign + 0, // nFeePay + fIncludeDelegated); if (!fCreated) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); - const CWallet::CommitResult& res = pwalletMain->CommitTransaction(txNew, keyChange, g_connman.get()); + const CWallet::CommitResult& res = pwallet->CommitTransaction(txNew, keyChange, g_connman.get()); if (res.status != CWallet::CommitStatus::OK) throw JSONRPCError(RPC_WALLET_ERROR, res.ToString()); // Set comment - CWalletTx& wtx = pwalletMain->mapWallet.at(txNew->GetHash()); + CWalletTx& wtx = pwallet->mapWallet.at(txNew->GetHash()); if (!comment.empty()) { wtx.mapValue["comment"] = comment; } @@ -2177,12 +2309,17 @@ static UniValue legacy_sendmany(const UniValue& sendTo, int nMinDepth, std::stri */ UniValue sendmany(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) throw std::runtime_error( "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" includeDelegated )\n" "\nSend to multiple destinations. Recipients are transparent or shield PIVX addresses.\n" "\nAmounts are double-precision floating point numbers.\n" - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n" @@ -2211,11 +2348,11 @@ UniValue sendmany(const JSONRPCRequest& request) HelpExampleRpc("sendmany", "\"\", \"{\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\":0.01,\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\":0.02}\", 6, \"testing\"") ); - EnsureWalletIsUnlocked(); + EnsureWalletIsUnlocked(pwallet); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); // Read Params if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { @@ -2239,18 +2376,23 @@ UniValue sendmany(const JSONRPCRequest& request) } if (fShieldSend) { - return ShieldSendManyTo(sendTo, comment, "", nMinDepth, fIncludeDelegated); + return ShieldSendManyTo(pwallet, sendTo, comment, "", nMinDepth, fIncludeDelegated); } // All recipients are transparent: use Legacy sendmany t->t - return legacy_sendmany(sendTo, nMinDepth, comment, fIncludeDelegated); + return legacy_sendmany(pwallet, sendTo, nMinDepth, comment, fIncludeDelegated); } // Defined in rpc/misc.cpp -extern CScript _createmultisig_redeemScript(const UniValue& params); +extern CScript _createmultisig_redeemScript(CWallet* const pwallet, const UniValue& params); UniValue addmultisigaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw std::runtime_error( "addmultisigaddress nrequired [\"key\",...] ( \"label\" )\n" @@ -2276,18 +2418,18 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) "\nAs json rpc call\n" + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\\\",\\\"DAD3Y6ivr8nPQLT1NEPX84DxGCw9jz9Jvg\\\"]\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); std::string label; if (request.params.size() > 2) label = LabelFromValue(request.params[2]); // Construct using pay-to-script-hash: - CScript inner = _createmultisig_redeemScript(request.params); + CScript inner = _createmultisig_redeemScript(pwallet, request.params); CScriptID innerID(inner); - pwalletMain->AddCScript(inner); + pwallet->AddCScript(inner); - pwalletMain->SetAddressBook(innerID, label, AddressBook::AddressBookPurpose::SEND); + pwallet->SetAddressBook(innerID, label, AddressBook::AddressBookPurpose::SEND); return EncodeDestination(innerID); } @@ -2305,7 +2447,7 @@ struct tallyitem { } }; -UniValue ListReceived(const UniValue& params, bool by_label, int nBlockHeight) +static UniValue ListReceived(CWallet* const pwallet, const UniValue& params, bool by_label, int nBlockHeight) { // Minimum confirmations int nMinDepth = 1; @@ -2335,7 +2477,7 @@ UniValue ListReceived(const UniValue& params, bool by_label, int nBlockHeight) // Tally std::map mapTally; - for (std::map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + for (std::map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (!IsFinalTx(wtx.tx, nBlockHeight)) { @@ -2357,7 +2499,7 @@ UniValue ListReceived(const UniValue& params, bool by_label, int nBlockHeight) continue; } - isminefilter mine = IsMine(*pwalletMain, address); + isminefilter mine = IsMine(*pwallet, address); if (!(mine & filter)) { continue; } @@ -2373,7 +2515,7 @@ UniValue ListReceived(const UniValue& params, bool by_label, int nBlockHeight) // Create mapAddressBook iterator // If we aren't filtering, go from begin() to end() - auto itAddr = pwalletMain->NewAddressBookIterator(); + auto itAddr = pwallet->NewAddressBookIterator(); // If we are filtering, find() the applicable entry if (has_filtered_address) { itAddr.SetFilter(filtered_address); @@ -2449,6 +2591,11 @@ UniValue ListReceived(const UniValue& params, bool by_label, int nBlockHeight) UniValue listreceivedbyaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 4) throw std::runtime_error( "listreceivedbyaddress ( minconf includeempty includeWatchonly addressFilter)\n" @@ -2481,15 +2628,20 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); int nBlockHeight = chainActive.Height(); - return ListReceived(request.params, false, nBlockHeight); + return ListReceived(pwallet, request.params, false, nBlockHeight); } UniValue listreceivedbyshieldaddress(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size()==0 || request.params.size() >2) throw std::runtime_error( "listreceivedbyshieldaddress \"address\" ( minconf )\n" @@ -2519,9 +2671,9 @@ UniValue listreceivedbyshieldaddress(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); int nMinDepth = 1; if (request.params.size() > 1) { @@ -2540,7 +2692,7 @@ UniValue listreceivedbyshieldaddress(const JSONRPCRequest& request) } libzcash::SaplingPaymentAddress shieldAddr = *zaddr; - auto sspkm = pwalletMain->GetSaplingScriptPubKeyMan(); + auto sspkm = pwallet->GetSaplingScriptPubKeyMan(); // Visitor to support Sapling addrs if (!sspkm->PaymentAddressBelongsToWallet(shieldAddr)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, shield addr spending key or viewing key not found."); @@ -2551,7 +2703,7 @@ UniValue listreceivedbyshieldaddress(const JSONRPCRequest& request) sspkm->GetFilteredNotes(saplingEntries, zaddr, nMinDepth, false, false); std::set> nullifierSet; - bool hasSpendingKey = pwalletMain->HaveSpendingKeyForPaymentAddress(shieldAddr); + bool hasSpendingKey = pwallet->HaveSpendingKeyForPaymentAddress(shieldAddr); if (hasSpendingKey) { nullifierSet = sspkm->GetNullifiersForAddresses({*zaddr}); } @@ -2568,8 +2720,8 @@ UniValue listreceivedbyshieldaddress(const JSONRPCRequest& request) int index = -1; int64_t time = 0; - if (pwalletMain->mapWallet.count(entry.op.hash)) { - const CWalletTx& wtx = pwalletMain->mapWallet.at(entry.op.hash); + if (pwallet->mapWallet.count(entry.op.hash)) { + const CWalletTx& wtx = pwallet->mapWallet.at(entry.op.hash); if (!wtx.m_confirm.hashBlock.IsNull()) height = mapBlockIndex[wtx.m_confirm.hashBlock]->nHeight; index = wtx.m_confirm.nIndex; @@ -2590,6 +2742,11 @@ UniValue listreceivedbyshieldaddress(const JSONRPCRequest& request) UniValue listreceivedbylabel(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 3) throw std::runtime_error( "listreceivedbylabel ( minconf includeempty includeWatchonly)\n" @@ -2617,15 +2774,20 @@ UniValue listreceivedbylabel(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); int nBlockHeight = chainActive.Height(); - return ListReceived(request.params, true, nBlockHeight); + return ListReceived(pwallet, request.params, true, nBlockHeight); } UniValue listcoldutxos(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "listcoldutxos ( nonWhitelistedOnly )\n" @@ -2653,9 +2815,9 @@ UniValue listcoldutxos(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); bool fExcludeWhitelisted = false; if (request.params.size() > 0) @@ -2663,7 +2825,7 @@ UniValue listcoldutxos(const JSONRPCRequest& request) UniValue results(UniValue::VARR); for (std::map::const_iterator it = - pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { const uint256& wtxid = it->first; const CWalletTx* pcoin = &(*it).second; if (!CheckFinalTx(pcoin->tx) || !pcoin->IsTrusted()) @@ -2675,7 +2837,7 @@ UniValue listcoldutxos(const JSONRPCRequest& request) for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { const CTxOut& out = pcoin->tx->vout[i]; - isminetype mine = pwalletMain->IsMine(out); + isminetype mine = pwallet->IsMine(out); if (!bool(mine & ISMINE_COLD) && !bool(mine & ISMINE_SPENDABLE_DELEGATED)) continue; txnouttype type; @@ -2683,7 +2845,7 @@ UniValue listcoldutxos(const JSONRPCRequest& request) int nRequired; if (!ExtractDestinations(out.scriptPubKey, type, addresses, nRequired)) continue; - const bool fWhitelisted = pwalletMain->HasAddressBook(addresses[1]) > 0; + const bool fWhitelisted = pwallet->HasAddressBook(addresses[1]) > 0; if (fExcludeWhitelisted && fWhitelisted) continue; UniValue entry(UniValue::VOBJ); @@ -2707,7 +2869,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) entry.pushKV("address", EncodeDestination(dest)); } -void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) +static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) { CAmount nFee; std::list listReceived; @@ -2721,13 +2883,13 @@ void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& if ((!listSent.empty() || nFee != 0)) { for (const COutputEntry& s : listSent) { UniValue entry(UniValue::VOBJ); - if (involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) + if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) entry.pushKV("involvesWatchonly", true); MaybePushAddress(entry, s.destination); entry.pushKV("category", "send"); entry.pushKV("amount", ValueFromAmount(-s.amount)); - if (pwalletMain->HasAddressBook(s.destination)) { - entry.pushKV("label", pwalletMain->GetNameForAddressBookEntry(s.destination)); + if (pwallet->HasAddressBook(s.destination)) { + entry.pushKV("label", pwallet->GetNameForAddressBookEntry(s.destination)); } entry.pushKV("vout", s.vout); entry.pushKV("fee", ValueFromAmount(-nFee)); @@ -2742,10 +2904,10 @@ void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& if (listReceived.size() > 0 && depth >= nMinDepth) { for (const COutputEntry& r : listReceived) { std::string label; - if (pwalletMain->HasAddressBook(r.destination)) - label = pwalletMain->GetNameForAddressBookEntry(r.destination); + if (pwallet->HasAddressBook(r.destination)) + label = pwallet->GetNameForAddressBookEntry(r.destination); UniValue entry(UniValue::VOBJ); - if (involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) + if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) entry.pushKV("involvesWatchonly", true); MaybePushAddress(entry, r.destination); if (wtx.IsCoinBase()) { @@ -2759,7 +2921,7 @@ void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& entry.pushKV("category", "receive"); } entry.pushKV("amount", ValueFromAmount(r.amount)); - if (pwalletMain->HasAddressBook(r.destination)) { + if (pwallet->HasAddressBook(r.destination)) { entry.pushKV("label", label); } entry.pushKV("vout", r.vout); @@ -2772,6 +2934,11 @@ void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& UniValue listtransactions(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 6) throw std::runtime_error( "listtransactions ( \"dummy\" count from includeWatchonly includeDelegated )\n" "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n" @@ -2823,9 +2990,9 @@ UniValue listtransactions(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); if (!request.params[0].isNull() && request.params[0].get_str() != "*") { throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\""); @@ -2851,12 +3018,12 @@ UniValue listtransactions(const JSONRPCRequest& request) UniValue ret(UniValue::VARR); - const CWallet::TxItems & txOrdered = pwalletMain->wtxOrdered; + const CWallet::TxItems & txOrdered = pwallet->wtxOrdered; // iterate backwards until we have nCount items to return: for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx* const pwtx = (*it).second; - ListTransactions(*pwtx, 0, true, ret, filter); + ListTransactions(pwallet, *pwtx, 0, true, ret, filter); if ((int)ret.size() >= (nCount + nFrom)) break; } // ret is newest to oldest @@ -2887,6 +3054,11 @@ UniValue listtransactions(const JSONRPCRequest& request) UniValue listsinceblock(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp) throw std::runtime_error( "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" @@ -2928,9 +3100,9 @@ UniValue listsinceblock(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); CBlockIndex* pindex = NULL; int target_confirms = 1; @@ -2960,11 +3132,11 @@ UniValue listsinceblock(const JSONRPCRequest& request) UniValue transactions(UniValue::VARR); - for (std::map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) { + for (std::map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); it++) { CWalletTx tx = (*it).second; if (depth == -1 || tx.GetDepthInMainChain() < depth) - ListTransactions(tx, 0, true, transactions, filter); + ListTransactions(pwallet, tx, 0, true, transactions, filter); } CBlockIndex* pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; @@ -2979,6 +3151,11 @@ UniValue listsinceblock(const JSONRPCRequest& request) UniValue gettransaction(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "gettransaction \"txid\" ( includeWatchonly )\n" @@ -3019,9 +3196,9 @@ UniValue gettransaction(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; hash.SetHex(request.params[0].get_str()); @@ -3032,9 +3209,9 @@ UniValue gettransaction(const JSONRPCRequest& request) filter = filter | ISMINE_WATCH_ONLY; UniValue entry(UniValue::VOBJ); - if (!pwalletMain->mapWallet.count(hash)) + if (!pwallet->mapWallet.count(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); - const CWalletTx& wtx = pwalletMain->mapWallet.at(hash); + const CWalletTx& wtx = pwallet->mapWallet.at(hash); CAmount nCredit = wtx.GetCredit(filter); CAmount nDebit = wtx.GetDebit(filter); @@ -3048,7 +3225,7 @@ UniValue gettransaction(const JSONRPCRequest& request) WalletTxToJSON(wtx, entry); UniValue details(UniValue::VARR); - ListTransactions(wtx, 0, false, details, filter); + ListTransactions(pwallet, wtx, 0, false, details, filter); entry.pushKV("details", details); std::string strHex = EncodeHexTx(*wtx.tx); @@ -3059,6 +3236,11 @@ UniValue gettransaction(const JSONRPCRequest& request) UniValue abandontransaction(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "abandontransaction \"txid\"\n" @@ -3077,21 +3259,18 @@ UniValue abandontransaction(const JSONRPCRequest& request) "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); - EnsureWallet(); - EnsureWalletIsUnlocked(); - // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; hash.SetHex(request.params[0].get_str()); - if (!pwalletMain->mapWallet.count(hash)) + if (!pwallet->mapWallet.count(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); - if (!pwalletMain->AbandonTransaction(hash)) + if (!pwallet->AbandonTransaction(hash)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment"); return NullUniValue; @@ -3100,6 +3279,11 @@ UniValue abandontransaction(const JSONRPCRequest& request) UniValue backupwallet(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "backupwallet \"destination\"\n" @@ -3113,12 +3297,12 @@ UniValue backupwallet(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); std::string strDest = request.params[0].get_str(); - if (!BackupWallet(*pwalletMain, strDest)) + if (!BackupWallet(*pwallet, strDest)) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); return NullUniValue; @@ -3127,11 +3311,16 @@ UniValue backupwallet(const JSONRPCRequest& request) UniValue keypoolrefill(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "keypoolrefill ( newsize )\n" "\nFills the keypool." + - HelpRequiringPassphrase() + "\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments\n" "1. newsize (numeric, optional, default=100) The new keypool size\n" @@ -3139,7 +3328,7 @@ UniValue keypoolrefill(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("keypoolrefill", "") + HelpExampleRpc("keypoolrefill", "")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool unsigned int kpSize = 0; @@ -3149,10 +3338,10 @@ UniValue keypoolrefill(const JSONRPCRequest& request) kpSize = (unsigned int)request.params[0].get_int(); } - EnsureWalletIsUnlocked(); - pwalletMain->TopUpKeyPool(kpSize); + EnsureWalletIsUnlocked(pwallet); + pwallet->TopUpKeyPool(kpSize); - if (pwalletMain->GetKeyPoolSize() < kpSize) + if (pwallet->GetKeyPoolSize() < kpSize) throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); return NullUniValue; @@ -3161,15 +3350,20 @@ UniValue keypoolrefill(const JSONRPCRequest& request) static void LockWallet(CWallet* pWallet) { - LOCK(cs_nWalletUnlockTime); - nWalletUnlockTime = 0; + LOCK(pWallet->cs_wallet); + pWallet->nRelockTime = 0; pWallet->fWalletUnlockStaking = false; pWallet->Lock(); } UniValue walletpassphrase(const JSONRPCRequest& request) { - if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() < 2 || request.params.size() > 3)) + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + + if (pwallet->IsCrypted() && (request.fHelp || request.params.size() < 2 || request.params.size() > 3)) throw std::runtime_error( "walletpassphrase \"passphrase\" timeout ( stakingonly )\n" "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" @@ -3194,11 +3388,11 @@ UniValue walletpassphrase(const JSONRPCRequest& request) "\nAs json rpc call\n" + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); if (request.fHelp) return true; - if (!pwalletMain->IsCrypted()) + if (!pwallet->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); // Note that the walletpassphrase is stored in params[0] which is not mlock()ed @@ -3212,7 +3406,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request) if (request.params.size() == 3) stakingOnly = request.params[2].get_bool(); - if (!pwalletMain->IsLocked() && pwalletMain->fWalletUnlockStaking && stakingOnly) + if (!pwallet->IsLocked() && pwallet->fWalletUnlockStaking && stakingOnly) throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked."); // Get the timeout @@ -3227,14 +3421,14 @@ UniValue walletpassphrase(const JSONRPCRequest& request) nSleepTime = MAX_SLEEP_TIME; } - if (!pwalletMain->Unlock(strWalletPass, stakingOnly)) + if (!pwallet->Unlock(strWalletPass, stakingOnly)) throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); - pwalletMain->TopUpKeyPool(); + pwallet->TopUpKeyPool(); if (nSleepTime > 0) { - nWalletUnlockTime = GetTime () + nSleepTime; - RPCRunLater ("lockwallet", std::bind (LockWallet, pwalletMain), nSleepTime); + pwallet->nRelockTime = GetTime () + nSleepTime; + RPCRunLater (strprintf("lockwallet(%s)", pwallet->GetName()), std::bind (LockWallet, pwallet), nSleepTime); } return NullUniValue; @@ -3243,7 +3437,12 @@ UniValue walletpassphrase(const JSONRPCRequest& request) UniValue walletpassphrasechange(const JSONRPCRequest& request) { - if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 2)) + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + + if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) throw std::runtime_error( "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" @@ -3255,11 +3454,11 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); if (request.fHelp) return true; - if (!pwalletMain->IsCrypted()) + if (!pwallet->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) @@ -3277,7 +3476,7 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request) "walletpassphrasechange \n" "Changes the wallet passphrase from to ."); - if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); return NullUniValue; @@ -3286,7 +3485,12 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request) UniValue walletlock(const JSONRPCRequest& request) { - if (pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 0)) + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + + if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) throw std::runtime_error( "walletlock\n" "\nRemoves the wallet encryption key from memory, locking the wallet.\n" @@ -3305,20 +3509,17 @@ UniValue walletlock(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); if (request.fHelp) return true; - if (!pwalletMain->IsCrypted()) + if (!pwallet->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); - { - LOCK(cs_nWalletUnlockTime); - pwalletMain->Lock(); - nWalletUnlockTime = 0; - } + pwallet->Lock(); + pwallet->nRelockTime = 0; return NullUniValue; } @@ -3326,7 +3527,12 @@ UniValue walletlock(const JSONRPCRequest& request) UniValue encryptwallet(const JSONRPCRequest& request) { - if (!pwalletMain->IsCrypted() && (request.fHelp || request.params.size() != 1)) + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + + if (!pwallet->IsCrypted() && (request.fHelp || request.params.size() != 1)) throw std::runtime_error( "encryptwallet \"passphrase\"\n" "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" @@ -3351,11 +3557,11 @@ UniValue encryptwallet(const JSONRPCRequest& request) "\nAs a json rpc call\n" + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); if (request.fHelp) return true; - if (pwalletMain->IsCrypted()) + if (pwallet->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) @@ -3369,7 +3575,7 @@ UniValue encryptwallet(const JSONRPCRequest& request) "encryptwallet \n" "Encrypts the wallet with ."); - if (!pwalletMain->EncryptWallet(strWalletPass)) + if (!pwallet->EncryptWallet(strWalletPass)) throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); // BDB seems to have a bad habit of writing old data into @@ -3381,6 +3587,11 @@ UniValue encryptwallet(const JSONRPCRequest& request) UniValue listunspent(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 5) throw std::runtime_error( "listunspent ( minconf maxconf [\"address\",...] watchonlyconfig [query_options])\n" @@ -3433,7 +3644,7 @@ UniValue listunspent(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); int nMinDepth = 1; if (request.params.size() > 0) { @@ -3505,9 +3716,10 @@ UniValue listunspent(const JSONRPCRequest& request) UniValue results(UniValue::VARR); std::vector vecOutputs; - LOCK2(cs_main, pwalletMain->cs_wallet); + + LOCK2(cs_main, pwallet->cs_wallet); coinFilter.fOnlyConfirmed = false; - pwalletMain->AvailableCoins(&vecOutputs, &coinControl, coinFilter); + pwallet->AvailableCoins(&vecOutputs, &coinControl, coinFilter); for (const COutput& out : vecOutputs) { if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; @@ -3530,8 +3742,8 @@ UniValue listunspent(const JSONRPCRequest& request) CTxDestination address; if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address)) { entry.pushKV("address", EncodeDestination(address)); - if (pwalletMain->HasAddressBook(address)) { - entry.pushKV("label", pwalletMain->GetNameForAddressBookEntry(address)); + if (pwallet->HasAddressBook(address)) { + entry.pushKV("label", pwallet->GetNameForAddressBookEntry(address)); } } entry.pushKV("scriptPubKey", HexStr(pk.begin(), pk.end())); @@ -3540,7 +3752,7 @@ UniValue listunspent(const JSONRPCRequest& request) if (ExtractDestination(pk, address)) { const CScriptID& hash = boost::get(address); CScript redeemScript; - if (pwalletMain->GetCScript(hash, redeemScript)) + if (pwallet->GetCScript(hash, redeemScript)) entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); } } @@ -3556,6 +3768,11 @@ UniValue listunspent(const JSONRPCRequest& request) UniValue lockunspent(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" @@ -3594,9 +3811,9 @@ UniValue lockunspent(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); if (request.params.size() == 1) RPCTypeCheck(request.params, {UniValue::VBOOL}); @@ -3607,7 +3824,7 @@ UniValue lockunspent(const JSONRPCRequest& request) if (request.params.size() == 1) { if (fUnlock) - pwalletMain->UnlockAllCoins(); + pwallet->UnlockAllCoins(); return true; } @@ -3641,8 +3858,8 @@ UniValue lockunspent(const JSONRPCRequest& request) const COutPoint outpt(uint256S(txid), nOutput); - const auto it = pwalletMain->mapWallet.find(outpt.hash); - if (it == pwalletMain->mapWallet.end()) { + const auto it = pwallet->mapWallet.find(outpt.hash); + if (it == pwallet->mapWallet.end()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction"); } @@ -3652,11 +3869,11 @@ UniValue lockunspent(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds"); } - if (pwalletMain->IsSpent(outpt.hash, outpt.n)) { + if (pwallet->IsSpent(outpt.hash, outpt.n)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output"); } - const bool is_locked = pwalletMain->IsLockedCoin(outpt.hash, outpt.n); + const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n); if (fUnlock && !is_locked) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output"); @@ -3671,8 +3888,8 @@ UniValue lockunspent(const JSONRPCRequest& request) // Atomically set (un)locked status for the outputs. for (const COutPoint& outpt : outputs) { - if (fUnlock) pwalletMain->UnlockCoin(outpt); - else pwalletMain->LockCoin(outpt); + if (fUnlock) pwallet->UnlockCoin(outpt); + else pwallet->LockCoin(outpt); } return true; @@ -3680,6 +3897,11 @@ UniValue lockunspent(const JSONRPCRequest& request) UniValue listlockunspent(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 0) throw std::runtime_error( "listlockunspent\n" @@ -3707,9 +3929,9 @@ UniValue listlockunspent(const JSONRPCRequest& request) "\nAs a json rpc call\n" + HelpExampleRpc("listlockunspent", "")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); - std::set vOutpts = pwalletMain->ListLockedCoins(); + std::set vOutpts = pwallet->ListLockedCoins(); UniValue ret(UniValue::VARR); for (const COutPoint& outpt : vOutpts) { @@ -3725,6 +3947,11 @@ UniValue listlockunspent(const JSONRPCRequest& request) UniValue settxfee(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) throw std::runtime_error( "settxfee amount\n" @@ -3738,7 +3965,7 @@ UniValue settxfee(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("settxfee", "0.00001") + HelpExampleRpc("settxfee", "0.00001")); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); // Amount CAmount nAmount = 0; @@ -3751,6 +3978,11 @@ UniValue settxfee(const JSONRPCRequest& request) UniValue getwalletinfo(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) throw std::runtime_error( "getwalletinfo\n" @@ -3784,51 +4016,56 @@ UniValue getwalletinfo(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); UniValue obj(UniValue::VOBJ); - obj.pushKV("walletversion", pwalletMain->GetVersion()); - obj.pushKV("balance", ValueFromAmount(pwalletMain->GetAvailableBalance())); - obj.pushKV("delegated_balance", ValueFromAmount(pwalletMain->GetDelegatedBalance())); - obj.pushKV("cold_staking_balance", ValueFromAmount(pwalletMain->GetColdStakingBalance())); - obj.pushKV("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance())); - obj.pushKV("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance())); - obj.pushKV("immature_delegated_balance", ValueFromAmount(pwalletMain->GetImmatureDelegatedBalance())); - obj.pushKV("immature_cold_staking_balance", ValueFromAmount(pwalletMain->GetImmatureColdStakingBalance())); - obj.pushKV("txcount", (int)pwalletMain->mapWallet.size()); + obj.pushKV("walletversion", pwallet->GetVersion()); + obj.pushKV("balance", ValueFromAmount(pwallet->GetAvailableBalance())); + obj.pushKV("delegated_balance", ValueFromAmount(pwallet->GetDelegatedBalance())); + obj.pushKV("cold_staking_balance", ValueFromAmount(pwallet->GetColdStakingBalance())); + obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance())); + obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance())); + obj.pushKV("immature_delegated_balance", ValueFromAmount(pwallet->GetImmatureDelegatedBalance())); + obj.pushKV("immature_cold_staking_balance", ValueFromAmount(pwallet->GetImmatureColdStakingBalance())); + obj.pushKV("txcount", (int)pwallet->mapWallet.size()); // Autocombine settings - obj.pushKV("autocombine_enabled", pwalletMain->fCombineDust); - obj.pushKV("autocombine_threshold", ValueFromAmount(pwalletMain->nAutoCombineThreshold)); + obj.pushKV("autocombine_enabled", pwallet->fCombineDust); + obj.pushKV("autocombine_threshold", ValueFromAmount(pwallet->nAutoCombineThreshold)); // Keypool information - obj.pushKV("keypoololdest", pwalletMain->GetOldestKeyPoolTime()); - size_t kpExternalSize = pwalletMain->KeypoolCountExternalKeys(); + obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime()); + size_t kpExternalSize = pwallet->KeypoolCountExternalKeys(); obj.pushKV("keypoolsize", (int64_t)kpExternalSize); - ScriptPubKeyMan* spk_man = pwalletMain->GetScriptPubKeyMan(); + ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(); if (spk_man) { const CKeyID& seed_id = spk_man->GetHDChain().GetID(); if (!seed_id.IsNull()) { obj.pushKV("hdseedid", seed_id.GetHex()); } } - if (pwalletMain->IsHDEnabled()) { - obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwalletMain->GetKeyPoolSize() - kpExternalSize)); - obj.pushKV("keypoolsize_hd_staking", (int64_t)(pwalletMain->GetStakingKeyPoolSize())); + if (pwallet->IsHDEnabled()) { + obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize)); + obj.pushKV("keypoolsize_hd_staking", (int64_t)(pwallet->GetStakingKeyPoolSize())); } - if (pwalletMain->IsCrypted()) - obj.pushKV("unlocked_until", nWalletUnlockTime); - obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); - obj.pushKV("last_processed_block", pwalletMain->GetLastBlockHeight()); + if (pwallet->IsCrypted()) + obj.pushKV("unlocked_until", pwallet->nRelockTime); + obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); + obj.pushKV("last_processed_block", pwallet->GetLastBlockHeight()); return obj; } UniValue getstakingstatus(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) throw std::runtime_error( "getstakingstatus\n" @@ -3856,24 +4093,24 @@ UniValue getstakingstatus(const JSONRPCRequest& request) HelpExampleCli("getstakingstatus", "") + HelpExampleRpc("getstakingstatus", "")); - if (!pwalletMain) + if (!pwallet) throw JSONRPCError(RPC_IN_WARMUP, "Try again after active chain is loaded"); { - LOCK2(cs_main, &pwalletMain->cs_wallet); + LOCK2(cs_main, &pwallet->cs_wallet); UniValue obj(UniValue::VOBJ); - obj.pushKV("staking_status", pwalletMain->pStakerStatus->IsActive()); + obj.pushKV("staking_status", pwallet->pStakerStatus->IsActive()); obj.pushKV("staking_enabled", gArgs.GetBoolArg("-staking", DEFAULT_STAKING)); bool fColdStaking = gArgs.GetBoolArg("-coldstaking", true); obj.pushKV("coldstaking_enabled", fColdStaking); obj.pushKV("haveconnections", (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) > 0)); obj.pushKV("mnsync", !masternodeSync.NotCompleted()); - obj.pushKV("walletunlocked", !pwalletMain->IsLocked()); + obj.pushKV("walletunlocked", !pwallet->IsLocked()); std::vector vCoins; - pwalletMain->StakeableCoins(&vCoins); + pwallet->StakeableCoins(&vCoins); obj.pushKV("stakeablecoins", (int)vCoins.size()); - obj.pushKV("stakingbalance", ValueFromAmount(pwalletMain->GetStakingBalance(fColdStaking))); - obj.pushKV("stakesplitthreshold", ValueFromAmount(pwalletMain->nStakeSplitThreshold)); - CStakerStatus* ss = pwalletMain->pStakerStatus; + obj.pushKV("stakingbalance", ValueFromAmount(pwallet->GetStakingBalance(fColdStaking))); + obj.pushKV("stakesplitthreshold", ValueFromAmount(pwallet->nStakeSplitThreshold)); + CStakerStatus* ss = pwallet->pStakerStatus; if (ss) { obj.pushKV("lastattempt_age", (int)(GetTime() - ss->GetLastTime())); obj.pushKV("lastattempt_depth", (chainActive.Height() - ss->GetLastHeight())); @@ -3887,6 +4124,11 @@ UniValue getstakingstatus(const JSONRPCRequest& request) UniValue setstakesplitthreshold(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "setstakesplitthreshold value\n\n" @@ -3895,7 +4137,7 @@ UniValue setstakesplitthreshold(const JSONRPCRequest& request) "higher than the threshold) as possible.\n" "E.g. If the coinstake input + the block reward is 2000, and the split threshold is 499, the corresponding\n" "coinstake transaction will have 4 outputs (of 500 PIV each)." - + HelpRequiringPassphrase() + "\n" + + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. value (numeric, required) Threshold value (in PIV).\n" @@ -3918,12 +4160,17 @@ UniValue setstakesplitthreshold(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); result.pushKV("threshold", request.params[0]); - result.pushKV("saved", pwalletMain->SetStakeSplitThreshold(nStakeSplitThreshold)); + result.pushKV("saved", pwallet->SetStakeSplitThreshold(nStakeSplitThreshold)); return result; } UniValue getstakesplitthreshold(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() != 0) throw std::runtime_error( "getstakesplitthreshold\n" @@ -3935,11 +4182,16 @@ UniValue getstakesplitthreshold(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getstakesplitthreshold", "") + HelpExampleRpc("getstakesplitthreshold", "")); - return ValueFromAmount(pwalletMain->GetStakeSplitThreshold()); + return ValueFromAmount(pwallet->GetStakeSplitThreshold()); } UniValue autocombinerewards(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + bool fEnable; if (request.params.size() >= 1) fEnable = request.params[0].get_bool(); @@ -3958,7 +4210,7 @@ UniValue autocombinerewards(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("autocombinerewards", "true 500") + HelpExampleRpc("autocombinerewards", "true 500")); - CWalletDB walletdb(pwalletMain->GetDBHandle()); + CWalletDB walletdb(pwallet->GetDBHandle()); CAmount nThreshold = 0; if (fEnable && request.params.size() > 1) { @@ -3967,8 +4219,8 @@ UniValue autocombinerewards(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("The threshold value cannot be less than %s", FormatMoney(COIN))); } - pwalletMain->fCombineDust = fEnable; - pwalletMain->nAutoCombineThreshold = nThreshold; + pwallet->fCombineDust = fEnable; + pwallet->nAutoCombineThreshold = nThreshold; if (!walletdb.WriteAutoCombineSettings(fEnable, nThreshold)) throw std::runtime_error("Changed settings in wallet but failed to save to database\n"); @@ -3978,6 +4230,11 @@ UniValue autocombinerewards(const JSONRPCRequest& request) UniValue setautocombinethreshold(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.empty() || request.params.size() > 2) throw std::runtime_error( "setautocombinethreshold enable ( value )\n" @@ -3999,8 +4256,6 @@ UniValue setautocombinethreshold(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("setautocombinethreshold", "500.12") + HelpExampleRpc("setautocombinethreshold", "500.12")); - EnsureWallet(); - RPCTypeCheck(request.params, {UniValue::VBOOL, UniValue::VNUM}); bool fEnable = request.params[0].get_bool(); @@ -4015,16 +4270,16 @@ UniValue setautocombinethreshold(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("The threshold value cannot be less than %s", FormatMoney(COIN))); } - CWalletDB walletdb(pwalletMain->GetDBHandle()); + CWalletDB walletdb(pwallet->GetDBHandle()); { - LOCK(pwalletMain->cs_wallet); - pwalletMain->fCombineDust = fEnable; - pwalletMain->nAutoCombineThreshold = nThreshold; + LOCK(pwallet->cs_wallet); + pwallet->fCombineDust = fEnable; + pwallet->nAutoCombineThreshold = nThreshold; UniValue result(UniValue::VOBJ); result.pushKV("enabled", fEnable); - result.pushKV("threshold", ValueFromAmount(pwalletMain->nAutoCombineThreshold)); + result.pushKV("threshold", ValueFromAmount(pwallet->nAutoCombineThreshold)); if (walletdb.WriteAutoCombineSettings(fEnable, nThreshold)) { result.pushKV("saved", "true"); } else { @@ -4037,6 +4292,11 @@ UniValue setautocombinethreshold(const JSONRPCRequest& request) UniValue getautocombinethreshold(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || !request.params.empty()) throw std::runtime_error( "getautocombinethreshold\n" @@ -4051,48 +4311,22 @@ UniValue getautocombinethreshold(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("getautocombinethreshold", "") + HelpExampleRpc("getautocombinethreshold", "")); - EnsureWallet(); - - LOCK(pwalletMain->cs_wallet); + LOCK(pwallet->cs_wallet); UniValue result(UniValue::VOBJ); - result.pushKV("enabled", pwalletMain->fCombineDust); - result.pushKV("threshold", ValueFromAmount(pwalletMain->nAutoCombineThreshold)); + result.pushKV("enabled", pwallet->fCombineDust); + result.pushKV("threshold", ValueFromAmount(pwallet->nAutoCombineThreshold)); return result; } -UniValue printAddresses() +UniValue getsaplingnotescount(const JSONRPCRequest& request) { - std::vector vCoins; - pwalletMain->AvailableCoins(&vCoins); - std::map mapAddresses; - for (const COutput& out : vCoins) { - CTxDestination utxoAddress; - ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, utxoAddress); - std::string strAdd = EncodeDestination(utxoAddress); - - if (mapAddresses.find(strAdd) == mapAddresses.end()) //if strAdd is not already part of the map - mapAddresses[strAdd] = (double)out.tx->tx->vout[out.i].nValue / (double)COIN; - else - mapAddresses[strAdd] += (double)out.tx->tx->vout[out.i].nValue / (double)COIN; - } + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - UniValue ret(UniValue::VARR); - for (std::map::const_iterator it = mapAddresses.begin(); it != mapAddresses.end(); ++it) { - UniValue obj(UniValue::VOBJ); - const std::string* strAdd = &(*it).first; - const double* nBalance = &(*it).second; - obj.pushKV("Address ", *strAdd); - obj.pushKV("Balance ", *nBalance); - ret.push_back(obj); - } - - return ret; -} + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; -UniValue getsaplingnotescount(const JSONRPCRequest& request) -{ if (request.fHelp || request.params.size() > 1) throw std::runtime_error( "getsaplingnotescount ( minconf )\n" @@ -4111,13 +4345,13 @@ UniValue getsaplingnotescount(const JSONRPCRequest& request) // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now - pwalletMain->BlockUntilSyncedToCurrentChain(); + pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, pwallet->cs_wallet); int nMinDepth = !request.params.empty() ? request.params[0].get_int() : 1; int count = 0; - for (const auto& wtx : pwalletMain->mapWallet) { + for (const auto& wtx : pwallet->mapWallet) { if (wtx.second.GetDepthInMainChain() >= nMinDepth) { for (const auto& nd : wtx.second.mapSaplingNoteData) { if (nd.second.IsMyNote()) count++; @@ -4129,6 +4363,11 @@ UniValue getsaplingnotescount(const JSONRPCRequest& request) UniValue rescanblockchain(const JSONRPCRequest& request) { + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "rescanblockchain (start_height) (stop_height)\n" @@ -4147,8 +4386,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request) ); } - EnsureWallet(); - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwallet); if (!reserver.reserve()) { throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } @@ -4179,9 +4417,9 @@ UniValue rescanblockchain(const JSONRPCRequest& request) } } - CBlockIndex *stopBlock = pwalletMain->ScanForWalletTransactions(pindexStart, pindexStop, reserver, true); + CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, true); if (!stopBlock) { - if (pwalletMain->IsAbortingRescan()) { + if (pwallet->IsAbortingRescan()) { throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted."); } // if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index a5de7e2de145..6ba3bc41bf20 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -1,12 +1,28 @@ -// Copyright (c) 2016 The Bitcoin Core developers +// Copyright (c) 2016-2021 The Bitcoin Core developers +// Copyright (c) 2021 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef PIVX_WALLET_RPCWALLET_H +#define PIVX_WALLET_RPCWALLET_H -#ifndef BITCOIN_WALLET_RPCWALLET_H -#define BITCOIN_WALLET_RPCWALLET_H +#include class CRPCTable; +class CWallet; +class JSONRPCRequest; void RegisterWalletRPCCommands(CRPCTable &tableRPC); -#endif //BITCOIN_WALLET_RPCWALLET_H +/** + * Figures out what wallet, if any, to use for a JSONRPCRequest. + * + * @param[in] request JSONRPCRequest that wishes to access a wallet + * @return NULL if no wallet should be used, or a pointer to the CWallet + */ +CWallet* GetWalletForJSONRPCRequest(const JSONRPCRequest& request); + +std::string HelpRequiringPassphrase(CWallet* const pwallet); +bool EnsureWalletIsAvailable(CWallet* const pwallet, bool avoidException); +void EnsureWalletIsUnlocked(CWallet* const pwallet, bool fAllowAnonOnly = false); + +#endif //PIVX_WALLET_RPCWALLET_H diff --git a/src/wallet/test/wallet_sapling_transactions_validations_tests.cpp b/src/wallet/test/wallet_sapling_transactions_validations_tests.cpp index 672adc74bae1..6e026fba8f28 100644 --- a/src/wallet/test/wallet_sapling_transactions_validations_tests.cpp +++ b/src/wallet/test/wallet_sapling_transactions_validations_tests.cpp @@ -19,6 +19,8 @@ */ struct TestSaplingChainSetup: public TestChain100Setup { + std::unique_ptr pwalletMain; + TestSaplingChainSetup() : TestChain100Setup() { initZKSNARKS(); // init zk-snarks lib @@ -26,9 +28,9 @@ struct TestSaplingChainSetup: public TestChain100Setup bitdb.MakeMock(); bool fFirstRun; std::unique_ptr dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat")); - pwalletMain = new CWallet(std::move(dbw)); + pwalletMain = MakeUnique(std::move(dbw)); pwalletMain->LoadWallet(fFirstRun); - RegisterValidationInterface(pwalletMain); + RegisterValidationInterface(pwalletMain.get()); int SAPLING_ACTIVATION_HEIGHT = 101; UpdateNetworkUpgradeParameters(Consensus::UPGRADE_V5_0, SAPLING_ACTIVATION_HEIGHT); @@ -43,16 +45,14 @@ struct TestSaplingChainSetup: public TestChain100Setup // import coinbase key used to generate the 100-blocks chain BOOST_CHECK(pwalletMain->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey())); } - WalletRescanReserver reserver(pwalletMain); + WalletRescanReserver reserver(pwalletMain.get()); BOOST_CHECK(reserver.reserve()); pwalletMain->RescanFromTime(0, reserver, true /* update */); } ~TestSaplingChainSetup() { - UnregisterValidationInterface(pwalletMain); - delete pwalletMain; - pwalletMain = nullptr; + UnregisterValidationInterface(pwalletMain.get()); bitdb.Flush(true); bitdb.Reset(); } @@ -60,12 +60,13 @@ struct TestSaplingChainSetup: public TestChain100Setup BOOST_FIXTURE_TEST_SUITE(wallet_sapling_transactions_validations_tests, TestSaplingChainSetup) -SaplingOperation createOperationAndBuildTx(std::vector recipients, - int nextBlockHeight, - bool selectTransparentCoins) +static SaplingOperation createOperationAndBuildTx(std::unique_ptr& pwallet, + std::vector recipients, + int nextBlockHeight, + bool selectTransparentCoins) { // Create the operation - SaplingOperation operation(Params().GetConsensus(), nextBlockHeight, pwalletMain); + SaplingOperation operation(Params().GetConsensus(), nextBlockHeight, pwallet.get()); auto operationResult = operation.setRecipients(recipients) ->setSelectTransparentCoins(selectTransparentCoins) ->setSelectShieldedCoins(!selectTransparentCoins) @@ -112,7 +113,7 @@ BOOST_AUTO_TEST_CASE(test_in_block_and_mempool_notes_double_spend) recipients.emplace_back(pa, CAmount(100 * COIN), ""); // Create the operation and build the transaction - SaplingOperation operation = createOperationAndBuildTx(recipients, tipHeight + 1, true); + SaplingOperation operation = createOperationAndBuildTx(pwalletMain, recipients, tipHeight + 1, true); // broadcast the tx to the network std::string retHash; BOOST_ASSERT_MSG(operation.send(retHash), "error committing and broadcasting the transaction"); @@ -135,14 +136,14 @@ BOOST_AUTO_TEST_CASE(test_in_block_and_mempool_notes_double_spend) pwalletMain->getNewAddress(tDest2, "receiveValid"); std::vector recipients2; recipients2.emplace_back(tDest2, CAmount(90 * COIN)); - SaplingOperation operation2 = createOperationAndBuildTx(recipients2, tipHeight + 1, false); + SaplingOperation operation2 = createOperationAndBuildTx(pwalletMain, recipients2, tipHeight + 1, false); // Create a second transaction that spends the same note with a different output now CTxDestination tDest3; pwalletMain->getNewAddress(tDest3, "receiveInvalid"); std::vector recipients3; recipients3.emplace_back(tDest3, CAmount(5 * COIN)); - SaplingOperation operation3 = createOperationAndBuildTx(recipients3, tipHeight + 1, false); + SaplingOperation operation3 = createOperationAndBuildTx(pwalletMain, recipients3, tipHeight + 1, false); // Now that both transactions were created, broadcast the first one std::string retTxHash2; diff --git a/src/wallet/test/wallet_shielded_balances_tests.cpp b/src/wallet/test/wallet_shielded_balances_tests.cpp index 8ab4fe8c2306..237663b3ced9 100644 --- a/src/wallet/test/wallet_shielded_balances_tests.cpp +++ b/src/wallet/test/wallet_shielded_balances_tests.cpp @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(GetShieldedSimpleCachedCreditAndDebit) //////// Credit //////// /////////////////////// - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); // Main wallet CWallet &wallet = *pwalletMain; @@ -191,9 +191,6 @@ BOOST_AUTO_TEST_CASE(GetShieldedSimpleCachedCreditAndDebit) // Checks that the only shielded output of this tx is change. BOOST_CHECK(wallet.GetSaplingScriptPubKeyMan()->IsNoteSaplingChange( SaplingOutPoint(wtxDebitUpdated.GetHash(), 0), pa)); - - // Revert to default - RegtestDeactivateSapling(); } libzcash::SaplingPaymentAddress getNewDummyShieldedAddress() @@ -237,7 +234,7 @@ CWalletTx& buildTxAndLoadToWallet(CWallet& wallet, const libzcash::SaplingExtend */ BOOST_AUTO_TEST_CASE(VerifyShieldedToRemoteShieldedCachedBalance) { - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); // Main wallet CWallet &wallet = *pwalletMain; @@ -280,9 +277,6 @@ BOOST_AUTO_TEST_CASE(VerifyShieldedToRemoteShieldedCachedBalance) // Plus, change should be same and be cached as well BOOST_CHECK_EQUAL(wtxDebitUpdated.GetShieldedChange(), expectedShieldedChange); BOOST_CHECK(wtxDebitUpdated.fShieldedChangeCached); - - // Revert to default - RegtestDeactivateSapling(); } struct FakeBlock @@ -320,7 +314,7 @@ FakeBlock SimpleFakeMine(CWalletTx& wtx, SaplingMerkleTree& currentTree, CWallet */ BOOST_AUTO_TEST_CASE(GetShieldedAvailableCredit) { - auto consensusParams = RegtestActivateSapling(); + auto consensusParams = Params().GetConsensus(); // Main wallet CWallet &wallet = *pwalletMain; diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index cbfeaca0e3fe..085f29bdf925 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2016 The Bitcoin Core developers -// Copyright (c) 2020 The PIVX developers +// Copyright (c) 2016-2021 The Bitcoin Core developers +// Copyright (c) 2020-2021 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,34 +7,26 @@ #include "rpc/server.h" #include "wallet/db.h" -#include "wallet/wallet.h" #include "wallet/rpcwallet.h" -void clean() -{ - delete pwalletMain; - pwalletMain = nullptr; - - bitdb.Flush(true); - bitdb.Reset(); -} - WalletTestingSetup::WalletTestingSetup(const std::string& chainName): SaplingTestingSetup(chainName) { - clean(); // todo: research why we have an initialized bitdb here. bitdb.MakeMock(); - RegisterWalletRPCCommands(tableRPC); bool fFirstRun; std::unique_ptr dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat")); - pwalletMain = new CWallet(std::move(dbw)); + pwalletMain = MakeUnique(std::move(dbw)); pwalletMain->LoadWallet(fFirstRun); - RegisterValidationInterface(pwalletMain); + RegisterValidationInterface(pwalletMain.get()); + + RegisterWalletRPCCommands(tableRPC); } WalletTestingSetup::~WalletTestingSetup() { - UnregisterValidationInterface(pwalletMain); - clean(); + UnregisterValidationInterface(pwalletMain.get()); + + bitdb.Flush(true); + bitdb.Reset(); } diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h index 616ce9926214..e9006ffd88f9 100644 --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -1,11 +1,14 @@ -// Copyright (c) 2016 The Bitcoin Core developers +// Copyright (c) 2016-2021 The Bitcoin Core developers +// Copyright (c) 2020-2021 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_WALLET_TEST_FIXTURE_H -#define BITCOIN_WALLET_TEST_FIXTURE_H +#ifndef PIVX_WALLET_TEST_FIXTURE_H +#define PIVX_WALLET_TEST_FIXTURE_H #include "test/librust/sapling_test_fixture.h" +#include "wallet/wallet.h" + /** Testing setup and teardown for wallet. */ @@ -13,6 +16,8 @@ struct WalletTestingSetup : public SaplingTestingSetup { WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~WalletTestingSetup(); + + std::unique_ptr pwalletMain; }; struct WalletRegTestingSetup : public WalletTestingSetup @@ -20,5 +25,5 @@ struct WalletRegTestingSetup : public WalletTestingSetup WalletRegTestingSetup() : WalletTestingSetup(CBaseChainParams::REGTEST) {} }; -#endif +#endif // PIVX_WALLET_TEST_FIXTURE_H diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 32397d0d106c..483073906c1f 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -22,7 +22,6 @@ extern UniValue importmulti(const JSONRPCRequest& request); extern UniValue dumpwallet(const JSONRPCRequest& request); extern UniValue importwallet(const JSONRPCRequest& request); - // how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles #define RUN_TESTS 100 @@ -38,7 +37,7 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) static std::vector vCoins; -static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) +static void add_coin(std::unique_ptr& pwallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) { static int nextLockTime = 0; CMutableTransaction tx; @@ -50,7 +49,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() tx.vin.resize(1); } - std::unique_ptr wtx(new CWalletTx(pwalletMain, MakeTransactionRef(std::move(tx)))); + std::unique_ptr wtx(new CWalletTx(pwallet.get(), MakeTransactionRef(std::move(tx)))); if (fIsFromMe) { wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1); } @@ -86,7 +85,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // with an empty wallet we can't even pay one cent BOOST_CHECK(!pwalletMain->SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); - add_coin(1*CENT, 4); // add a new 1 cent coin + add_coin(pwalletMain, 1*CENT, 4); // add a new 1 cent coin // with a new 1 cent coin, we still can't find a mature 1 cent BOOST_CHECK(!pwalletMain->SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); @@ -95,7 +94,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK(pwalletMain->SelectCoinsMinConf( 1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); - add_coin(2*CENT); // add a mature 2 cent coin + add_coin(pwalletMain, 2*CENT); // add a mature 2 cent coin // we can't make 3 cents of mature coins BOOST_CHECK(!pwalletMain->SelectCoinsMinConf( 3 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); @@ -104,9 +103,9 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK(pwalletMain->SelectCoinsMinConf( 3 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 3 * CENT); - add_coin(5*CENT); // add a mature 5 cent coin, - add_coin(10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses - add_coin(20*CENT); // and a mature 20 cent coin + add_coin(pwalletMain, 5*CENT); // add a mature 5 cent coin, + add_coin(pwalletMain, 10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses + add_coin(pwalletMain, 20*CENT); // and a mature 20 cent coin // now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38 @@ -144,11 +143,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin empty_wallet(); - add_coin( 6*CENT); - add_coin( 7*CENT); - add_coin( 8*CENT); - add_coin(20*CENT); - add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total + add_coin(pwalletMain, 6*CENT); + add_coin(pwalletMain, 7*CENT); + add_coin(pwalletMain, 8*CENT); + add_coin(pwalletMain, 20*CENT); + add_coin(pwalletMain, 30*CENT); // now we have 6+7+8+20+30 = 71 cents total // check that we have 71 and not 72 BOOST_CHECK(pwalletMain->SelectCoinsMinConf(71 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); @@ -159,14 +158,14 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); - add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total + add_coin(pwalletMain, 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20 BOOST_CHECK(pwalletMain->SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); - add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30 + add_coin(pwalletMain, 18*CENT); // now we have 5+6+7+8+18+20+30 // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18 BOOST_CHECK(pwalletMain->SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); @@ -179,10 +178,10 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // check that the smallest bigger coin is used - add_coin( 1*COIN); - add_coin( 2*COIN); - add_coin( 3*COIN); - add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents + add_coin(pwalletMain, 1*COIN); + add_coin(pwalletMain, 2*COIN); + add_coin(pwalletMain, 3*COIN); + add_coin(pwalletMain, 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents BOOST_CHECK(pwalletMain->SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); @@ -193,11 +192,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // empty the wallet and start again, now with fractions of a cent, to test sub-cent change avoidance empty_wallet(); - add_coin(0.1*CENT); - add_coin(0.2*CENT); - add_coin(0.3*CENT); - add_coin(0.4*CENT); - add_coin(0.5*CENT); + add_coin(pwalletMain, 0.1*CENT); + add_coin(pwalletMain, 0.2*CENT); + add_coin(pwalletMain, 0.3*CENT); + add_coin(pwalletMain, 0.4*CENT); + add_coin(pwalletMain, 0.5*CENT); // try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 = 1.5 cents // we'll get sub-cent change whatever happens, so can expect 1.0 exactly @@ -205,15 +204,15 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // but if we add a bigger coin, making it possible to avoid sub-cent change, things change: - add_coin(1111*CENT); + add_coin(pwalletMain, 1111*CENT); // try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 cents BOOST_CHECK(pwalletMain->SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount // if we add more sub-cent coins: - add_coin(0.6*CENT); - add_coin(0.7*CENT); + add_coin(pwalletMain, 0.6*CENT); + add_coin(pwalletMain, 0.7*CENT); // and try again to make 1.0 cents, we can still make 1.0 cents BOOST_CHECK(pwalletMain->SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); @@ -223,7 +222,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change empty_wallet(); for (int j = 0; j < 20; j++) - add_coin(50000 * COIN); + add_coin(pwalletMain, 50000 * COIN); BOOST_CHECK(pwalletMain->SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount @@ -234,29 +233,29 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // sometimes it will fail, and so we use the next biggest coin: empty_wallet(); - add_coin(0.5 * CENT); - add_coin(0.6 * CENT); - add_coin(0.7 * CENT); - add_coin(1111 * CENT); + add_coin(pwalletMain, 0.5 * CENT); + add_coin(pwalletMain, 0.6 * CENT); + add_coin(pwalletMain, 0.7 * CENT); + add_coin(pwalletMain, 1111 * CENT); BOOST_CHECK(pwalletMain->SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1111 * CENT); // we get the bigger coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0) empty_wallet(); - add_coin(0.4 * CENT); - add_coin(0.6 * CENT); - add_coin(0.8 * CENT); - add_coin(1111 * CENT); + add_coin(pwalletMain, 0.4 * CENT); + add_coin(pwalletMain, 0.6 * CENT); + add_coin(pwalletMain, 0.8 * CENT); + add_coin(pwalletMain, 1111 * CENT); BOOST_CHECK(pwalletMain->SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6 // test avoiding sub-cent change empty_wallet(); - add_coin(0.0005 * COIN); - add_coin(0.01 * COIN); - add_coin(1 * COIN); + add_coin(pwalletMain, 0.0005 * COIN); + add_coin(pwalletMain, 0.01 * COIN); + add_coin(pwalletMain, 1 * COIN); // trying to make 1.0001 from these three coins BOOST_CHECK(pwalletMain->SelectCoinsMinConf(1.0001 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); @@ -272,7 +271,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) { empty_wallet(); for (int i2 = 0; i2 < 100; i2++) - add_coin(COIN); + add_coin(pwalletMain, COIN); // picking 50 from 100 coins doesn't depend on the shuffle, // but does depend on randomness in the stochastic approximation code @@ -295,7 +294,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // add 75 cents in small change. not enough to make 90 cents, // then try making 90 cents. there are multiple competing "smallest bigger" coins, // one of which should be picked at random - add_coin( 5*CENT); add_coin(10*CENT); add_coin(15*CENT); add_coin(20*CENT); add_coin(25*CENT); + add_coin(pwalletMain, 5*CENT); + add_coin(pwalletMain, 10*CENT); + add_coin(pwalletMain, 15*CENT); + add_coin(pwalletMain, 20*CENT); + add_coin(pwalletMain, 25*CENT); fails = 0; for (int j = 0; j < RANDOM_REPEATS; j++) @@ -365,8 +368,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { CWallet wallet; WITH_LOCK(wallet.cs_wallet, wallet.SetLastBlockProcessed(newTip); ); - CWallet *backup = ::pwalletMain; - ::pwalletMain = &wallet; + vpwallets.insert(vpwallets.begin(), &wallet); UniValue keys; keys.setArray(); UniValue key; @@ -390,7 +392,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) UniValue response = importmulti(request); // !TODO: after pruning, check that the rescan for the first key fails. BOOST_CHECK_EQUAL(response.write(), "[{\"success\":true},{\"success\":true}]"); - ::pwalletMain = backup; + vpwallets.erase(vpwallets.begin()); } } @@ -400,7 +402,6 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // than or equal to key birthday. BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) { - CWallet *pwalletMainBackup = ::pwalletMain; // Create one block const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 15; SetMockTime(BLOCK_TIME); @@ -426,7 +427,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back(backup_file); - ::pwalletMain = &wallet; + vpwallets.insert(vpwallets.begin(), &wallet); ::dumpwallet(request); } @@ -438,7 +439,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back(backup_file); - ::pwalletMain = &wallet; + vpwallets[0] = &wallet; ::importwallet(request); LOCK(wallet.cs_wallet); @@ -452,7 +453,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) } SetMockTime(0); - ::pwalletMain = pwalletMainBackup; + vpwallets.erase(vpwallets.begin()); } void removeTxFromMempool(CWalletTx& wtx) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d7a9b7aa054d..71a509eb0be7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -25,7 +25,7 @@ #include #include -CWallet* pwalletMain = nullptr; +std::vector vpwallets; /** * Settings */ @@ -654,6 +654,37 @@ bool CWallet::ParameterInteraction() return UIError("-sysperms is not allowed in combination with enabled wallet functionality"); } + gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + + if (gArgs.GetBoolArg("-salvagewallet", false) && gArgs.SoftSetBoolArg("-rescan", true)) { + if (is_multiwallet) { + return UIError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); + } + // Rewrite just private keys: rescan to find transactions + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } + + int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); + // -zapwallettxes implies dropping the mempool on startup + if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); + } + + // -zapwallettxes implies a rescan + if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-rescan", true)) { + if (is_multiwallet) { + return UIError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); + } + LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); + } + + if (is_multiwallet) { + if (gArgs.GetBoolArg("-upgradewallet", false)) { + return UIError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); + } + } + if (gArgs.IsArgSet("-mintxfee")) { CAmount n = 0; if (ParseMoney(gArgs.GetArg("-mintxfee", ""), n) && n > 0) @@ -2069,60 +2100,75 @@ std::set CWalletTx::GetConflicts() const return result; } +void CWallet::Flush(bool shutdown) +{ + bitdb.Flush(shutdown); +} + bool CWallet::Verify() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; } - uiInterface.InitMessage(_("Verifying wallet...")); - std::string walletFile = gArgs.GetArg("-wallet", DEFAULT_WALLET_DAT); - std::string strDataDir = GetDataDir().string(); + uiInterface.InitMessage(_("Verifying wallet(s)...")); - std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) - return UIError(strError); + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + if (fs::path(walletFile).filename() != walletFile) { + return UIError(_("-wallet parameter must only specify a filename (not a path)")); + } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + return UIError(_("Invalid characters in -wallet filename")); + } - if (gArgs.GetBoolArg("-salvagewallet", false)) { - // Recover readable keypairs: - CWallet dummyWallet; - // Even if we don't use this lock in this function, we want to preserve - // lock order in LoadToWallet if query of chain state is needed to know - // tx status. If lock can't be taken, tx confirmation status may be not - // reliable. - LOCK(cs_main); - if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter)) - return false; - } + std::string strError; + if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { + return UIError(strError); + } - std::string strWarning; - bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); - if (!strWarning.empty()) { - UIWarning(strWarning); - } - if (!dbV) { - UIError(strError); - return false; + if (gArgs.GetBoolArg("-salvagewallet", false)) { + // Recover readable keypairs: + CWallet dummyWallet; + std::string backup_filename; + // Even if we don't use this lock in this function, we want to preserve + // lock order in LoadToWallet if query of chain state is needed to know + // tx status. If lock can't be taken, tx confirmation status may be not + // reliable. + LOCK(cs_main); + if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { + return false; + } + } + + std::string strWarning; + bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); + if (!strWarning.empty()) { + UIWarning(strWarning); + } + if (!dbV) { + return UIError(strError); + } } + return true; } - - void CWallet::ResendWalletTransactions(CConnman* connman) { // Do this infrequently and randomly to avoid giving away // that these are our transactions. - if (GetTime() < nNextResend) + if (GetTime() < nNextResend) { return; + } bool fFirst = (nNextResend == 0); nNextResend = GetTime() + GetRand(30 * 60); - if (fFirst) + if (fFirst) { return; + } // Only do it if there's been a new block since last time - if (nTimeBestReceived < nLastResend) + if (nTimeBestReceived < nLastResend) { return; + } nLastResend = GetTime(); // Rebroadcast any of our txes that aren't in a block yet @@ -2135,8 +2181,9 @@ void CWallet::ResendWalletTransactions(CConnman* connman) CWalletTx& wtx = item.second; // Don't rebroadcast until it's had plenty of time that // it should have gotten in already by now. - if (nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60) + if (nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60) { mapSorted.emplace(wtx.nTimeReceived, &wtx); + } } for (std::pair & item : mapSorted) { CWalletTx& wtx = *item.second; @@ -4216,8 +4263,9 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); nMaxVersion = FEATURE_LATEST; walletInstance->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately - } else + } else { LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); + } if (nMaxVersion < walletInstance->GetVersion()) { UIError("Cannot downgrade wallet\n"); return nullptr; @@ -4309,7 +4357,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) } LogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nWalletRescanTime); walletInstance->SetBestChain(chainActive.GetLocator()); - CWalletDB::IncrementUpdateCounter(); + walletInstance->dbw->IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2") { @@ -4339,18 +4387,29 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) bool CWallet::InitLoadWallet() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - pwalletMain = nullptr; LogPrintf("Wallet disabled!\n"); return true; } - std::string walletFile = gArgs.GetArg("-wallet", DEFAULT_WALLET_DAT); + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + // automatic backups + std::string strWarning, strError; + if(!AutoBackupWallet(walletFile, strWarning, strError)) { + if (!strWarning.empty()) { + UIWarning(strprintf("%s: %s", walletFile, strWarning)); + } + if (!strError.empty()) { + return UIError(strprintf("%s: %s", walletFile, strError)); + } + } - CWallet * const pwallet = CreateWalletFromFile(walletFile); - if (!pwallet) { - return false; + CWallet * const pwallet = CreateWalletFromFile(walletFile); + if (!pwallet) { + return false; + } + vpwallets.emplace_back(pwallet); } - pwalletMain = pwallet; + return true; } @@ -4875,19 +4934,3 @@ CStakeableOutput::CStakeableOutput(const CWalletTx* txIn, int iIn, int nDepthIn, const CBlockIndex*& _pindex) : COutput(txIn, iIn, nDepthIn, fSpendableIn, fSolvableIn), pindex(_pindex) {} -bool InitAutoBackupWallet() -{ - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - return true; - } - - std::string strWalletFile = gArgs.GetArg("-wallet", DEFAULT_WALLET_DAT); - - std::string strWarning, strError; - if(!AutoBackupWallet(strWalletFile, strWarning, strError)) { - if (!strWarning.empty()) UIWarning(strWarning); - if (!strError.empty()) return UIError(strError); - } - - return true; -} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 545bef77de88..5b4a7e6e0365 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -45,7 +45,7 @@ #include typedef CWallet* CWalletRef; -extern CWalletRef pwalletMain; +extern std::vector vpwallets; /** * Settings @@ -934,9 +934,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool LoadWatchOnly(const CScript& dest); //! Lock Wallet + //! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock(). + int64_t nRelockTime; bool Lock(); bool Unlock(const SecureString& strWalletPassphrase, bool anonimizeOnly = false); bool Unlock(const CKeyingMaterial& vMasterKeyIn); + bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); @@ -1128,6 +1131,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Get wallet transactions that conflict with given transaction (spend same outputs) std::set GetConflicts(const uint256& txid) const; + //! Flush wallet (bitdb flush) + void Flush(bool shutdown=false); + //! Verify the wallet database and perform salvage if required static bool Verify(); @@ -1270,7 +1276,4 @@ class WalletRescanReserver } }; -// !TODO: move to wallet/init.* -bool InitAutoBackupWallet(); - #endif // PIVX_WALLET_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 0e7b50007caf..c164b5b81fb8 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2009-2021 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers -// Copyright (c) 2015-2020 The PIVX developers +// Copyright (c) 2015-2021 The PIVX developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -61,7 +61,6 @@ namespace DBKeys { } // namespace DBKeys -static std::atomic nWalletDBUpdateCounter; // // CWalletDB @@ -69,49 +68,41 @@ static std::atomic nWalletDBUpdateCounter; bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string(DBKeys::NAME), strAddress), strName); + return WriteIC(std::make_pair(std::string(DBKeys::NAME), strAddress), strName); } bool CWalletDB::EraseName(const std::string& strAddress) { // This should only be used for sending addresses, never for receiving addresses, // receiving addresses must always have an address book entry if they're not change return. - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string(DBKeys::NAME), strAddress)); + return EraseIC(std::make_pair(std::string(DBKeys::NAME), strAddress)); } bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string(DBKeys::PURPOSE), strAddress), strPurpose); + return WriteIC(std::make_pair(std::string(DBKeys::PURPOSE), strAddress), strPurpose); } bool CWalletDB::ErasePurpose(const std::string& strPurpose) { - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string(DBKeys::PURPOSE), strPurpose)); + return EraseIC(std::make_pair(std::string(DBKeys::PURPOSE), strPurpose)); } bool CWalletDB::WriteTx(const CWalletTx& wtx) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string(DBKeys::TX), wtx.GetHash()), wtx); + return WriteIC(std::make_pair(std::string(DBKeys::TX), wtx.GetHash()), wtx); } bool CWalletDB::EraseTx(uint256 hash) { - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string(DBKeys::TX), hash)); + return EraseIC(std::make_pair(std::string(DBKeys::TX), hash)); } bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) { - nWalletDBUpdateCounter++; - - if (!batch.Write(std::make_pair(std::string(DBKeys::KEYMETA), vchPubKey), - keyMeta, false)) + if (!WriteIC(std::make_pair(std::string(DBKeys::KEYMETA), vchPubKey), keyMeta, false)) { return false; + } // hash pubkey/privkey to accelerate wallet load std::vector vchKey; @@ -119,25 +110,26 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, c vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); - return batch.Write(std::make_pair(std::string(DBKeys::KEY), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); + return WriteIC(std::make_pair(std::string(DBKeys::KEY), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); } bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, - const std::vector& vchCryptedSecret, - const CKeyMetadata& keyMeta) + const std::vector& vchCryptedSecret, + const CKeyMetadata& keyMeta) { const bool fEraseUnencryptedKey = true; - nWalletDBUpdateCounter++; - if (!batch.Write(std::make_pair(std::string(DBKeys::KEYMETA), vchPubKey), - keyMeta)) + if (!WriteIC(std::make_pair(std::string(DBKeys::KEYMETA), vchPubKey), keyMeta)) { return false; + } - if (!batch.Write(std::make_pair(std::string(DBKeys::CRYPTED_KEY), vchPubKey), vchCryptedSecret, false)) + if (!WriteIC(std::make_pair(std::string(DBKeys::CRYPTED_KEY), vchPubKey), vchCryptedSecret, false)) { return false; + } if (fEraseUnencryptedKey) { - batch.Erase(std::make_pair(std::string(DBKeys::KEY), vchPubKey)); + EraseIC(std::make_pair(std::string(DBKeys::KEY), vchPubKey)); } + return true; } @@ -145,48 +137,44 @@ bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, const libzcash::SaplingExtendedSpendingKey &key, const CKeyMetadata &keyMeta) { - nWalletDBUpdateCounter++; - - if (!batch.Write(std::make_pair(std::string(DBKeys::SAP_KEYMETA), ivk), keyMeta)) + if (!WriteIC(std::make_pair(std::string(DBKeys::SAP_KEYMETA), ivk), keyMeta)) { return false; + } - return batch.Write(std::make_pair(std::string(DBKeys::SAP_KEY), ivk), key, false); + return WriteIC(std::make_pair(std::string(DBKeys::SAP_KEY), ivk), key, false); } -bool CWalletDB::WriteSaplingPaymentAddress( - const libzcash::SaplingPaymentAddress &addr, - const libzcash::SaplingIncomingViewingKey &ivk) +bool CWalletDB::WriteSaplingPaymentAddress(const libzcash::SaplingPaymentAddress &addr, + const libzcash::SaplingIncomingViewingKey &ivk) { - nWalletDBUpdateCounter++; - - return batch.Write(std::make_pair(std::string(DBKeys::SAP_ADDR), addr), ivk, false); + return WriteIC(std::make_pair(std::string(DBKeys::SAP_ADDR), addr), ivk, false); } -bool CWalletDB::WriteCryptedSaplingZKey( - const libzcash::SaplingExtendedFullViewingKey &extfvk, - const std::vector& vchCryptedSecret, - const CKeyMetadata &keyMeta) +bool CWalletDB::WriteCryptedSaplingZKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, + const std::vector& vchCryptedSecret, + const CKeyMetadata &keyMeta) { const bool fEraseUnencryptedKey = true; - nWalletDBUpdateCounter++; auto ivk = extfvk.fvk.in_viewing_key(); - if (!batch.Write(std::make_pair(std::string(DBKeys::SAP_KEYMETA), ivk), keyMeta)) + if (!WriteIC(std::make_pair(std::string(DBKeys::SAP_KEYMETA), ivk), keyMeta)) { return false; + } - if (!batch.Write(std::make_pair(std::string(DBKeys::SAP_KEY_CRIPTED), ivk), std::make_pair(extfvk, vchCryptedSecret), false)) + if (!WriteIC(std::make_pair(std::string(DBKeys::SAP_KEY_CRIPTED), ivk), + std::make_pair(extfvk, vchCryptedSecret), false)) { return false; + } if (fEraseUnencryptedKey) { - batch.Erase(std::make_pair(std::string(DBKeys::SAP_KEY), ivk)); + EraseIC(std::make_pair(std::string(DBKeys::SAP_KEY), ivk)); } return true; } bool CWalletDB::WriteSaplingCommonOVK(const uint256& ovk) { - nWalletDBUpdateCounter++; - return batch.Write(std::string(DBKeys::SAP_COMMON_OVK), ovk); + return WriteIC(std::string(DBKeys::SAP_COMMON_OVK), ovk); } bool CWalletDB::ReadSaplingCommonOVK(uint256& ovkRet) @@ -196,79 +184,69 @@ bool CWalletDB::ReadSaplingCommonOVK(uint256& ovkRet) bool CWalletDB::WriteWitnessCacheSize(int64_t nWitnessCacheSize) { - nWalletDBUpdateCounter++; - return batch.Write(std::string(DBKeys::SAP_WITNESS_CACHE_SIZE), nWitnessCacheSize); + return WriteIC(std::string(DBKeys::SAP_WITNESS_CACHE_SIZE), nWitnessCacheSize); } bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string(DBKeys::MASTER_KEY), nID), kMasterKey, true); + return WriteIC(std::make_pair(std::string(DBKeys::MASTER_KEY), nID), kMasterKey, true); } bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string(DBKeys::CSCRIPT), hash), redeemScript, false); + return WriteIC(std::make_pair(std::string(DBKeys::CSCRIPT), hash), redeemScript, false); } bool CWalletDB::WriteWatchOnly(const CScript& dest) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string(DBKeys::WATCHS), dest), '1'); + return WriteIC(std::make_pair(std::string(DBKeys::WATCHS), dest), '1'); } bool CWalletDB::EraseWatchOnly(const CScript& dest) { - - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string(DBKeys::WATCHS), dest)); + return EraseIC(std::make_pair(std::string(DBKeys::WATCHS), dest)); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { - nWalletDBUpdateCounter++; - batch.Write(std::string(DBKeys::BESTBLOCK), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan - return batch.Write(std::string(DBKeys::BESTBLOCK_NOMERKLE), locator); + WriteIC(std::string(DBKeys::BESTBLOCK), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan + return WriteIC(std::string(DBKeys::BESTBLOCK_NOMERKLE), locator); } bool CWalletDB::ReadBestBlock(CBlockLocator& locator) { - if (batch.Read(std::string(DBKeys::BESTBLOCK), locator) && !locator.vHave.empty()) return true; + if (batch.Read(std::string(DBKeys::BESTBLOCK), locator) && !locator.vHave.empty()) { + return true; + } return batch.Read(std::string(DBKeys::BESTBLOCK_NOMERKLE), locator); } bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) { - nWalletDBUpdateCounter++; - return batch.Write(std::string(DBKeys::ORDERPOSNEXT), nOrderPosNext); + return WriteIC(std::string(DBKeys::ORDERPOSNEXT), nOrderPosNext); } bool CWalletDB::WriteStakeSplitThreshold(const CAmount& nStakeSplitThreshold) { - nWalletDBUpdateCounter++; - return batch.Write(std::string(DBKeys::STAKE_SPLIT_THRESHOLD), nStakeSplitThreshold); + return WriteIC(std::string(DBKeys::STAKE_SPLIT_THRESHOLD), nStakeSplitThreshold); } bool CWalletDB::WriteUseCustomFee(bool fUse) { - nWalletDBUpdateCounter++; - return batch.Write(std::string(DBKeys::USE_CUSTOM_FEE), fUse); + return WriteIC(std::string(DBKeys::USE_CUSTOM_FEE), fUse); } bool CWalletDB::WriteCustomFeeValue(const CAmount& nFee) { - nWalletDBUpdateCounter++; - return batch.Write(std::string(DBKeys::CUSTOM_FEE_VALUE), nFee); + return WriteIC(std::string(DBKeys::CUSTOM_FEE_VALUE), nFee); } bool CWalletDB::WriteAutoCombineSettings(bool fEnable, CAmount nCombineThreshold) { - nWalletDBUpdateCounter++; std::pair pSettings; pSettings.first = fEnable; pSettings.second = nCombineThreshold; - return batch.Write(std::string(DBKeys::AUTOCOMBINE), pSettings, true); + return WriteIC(std::string(DBKeys::AUTOCOMBINE), pSettings, true); } bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) @@ -278,27 +256,24 @@ bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string(DBKeys::POOL), nPool), keypool); + return WriteIC(std::make_pair(std::string(DBKeys::POOL), nPool), keypool); } bool CWalletDB::ErasePool(int64_t nPool) { - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string(DBKeys::POOL), nPool)); + return EraseIC(std::make_pair(std::string(DBKeys::POOL), nPool)); } bool CWalletDB::WriteMinVersion(int nVersion) { - return batch.Write(std::string(DBKeys::MINVERSION), nVersion); + return WriteIC(std::string(DBKeys::MINVERSION), nVersion); } bool CWalletDB::WriteHDChain(const CHDChain& chain) { - nWalletDBUpdateCounter++; std::string key = chain.chainType == HDChain::ChainCounterType::Sapling ? DBKeys::SAP_HDCHAIN : DBKeys::HDCHAIN; - return batch.Write(key, chain); + return WriteIC(key, chain); } DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) @@ -311,7 +286,7 @@ DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) typedef std::multimap TxItems; TxItems txByTime; - for (std::map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { + for (auto it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { CWalletTx* wtx = &((*it).second); txByTime.insert(std::make_pair(wtx->nTimeReceived, wtx)); } @@ -327,8 +302,8 @@ DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) nOrderPos = nOrderPosNext++; nOrderPosOffsets.push_back(nOrderPos); - if (!WriteTx(*pwtx)) - return DB_LOAD_FAIL; + if (!WriteTx(*pwtx)) return DB_LOAD_FAIL; + } else { int64_t nOrderPosOff = 0; for (const int64_t& nOffsetStart : nOrderPosOffsets) { @@ -338,12 +313,10 @@ DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) nOrderPos += nOrderPosOff; nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); - if (!nOrderPosOff) - continue; + if (!nOrderPosOff) continue; // Since we're changing the order, write it back - if (!WriteTx(*pwtx)) - return DB_LOAD_FAIL; + if (!WriteTx(*pwtx)) return DB_LOAD_FAIL; } } WriteOrderPosNext(nOrderPosNext); @@ -658,8 +631,9 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) try { int nMinVersion = 0; if (batch.Read((std::string) DBKeys::MINVERSION, nMinVersion)) { - if (nMinVersion > CLIENT_VERSION) + if (nMinVersion > CLIENT_VERSION) { return DB_TOO_NEW; + } pwallet->LoadMinVersion(nMinVersion); } @@ -675,9 +649,9 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) + if (ret == DB_NOTFOUND) { break; - else if (ret != 0) { + } else if (ret != 0) { LogPrintf("Error reading next record from wallet database\n"); return DB_CORRUPT; } @@ -687,9 +661,9 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { // losing keys is considered a catastrophic error, anything else // we assume the user can live with: - if (IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) + if (IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) { result = DB_CORRUPT; - else { + } else { // Leave other errors alone, if we try to fix them we might make things worse. fNoncriticalErrors = true; // ... but do warn the user there is something wrong. if (strType == DBKeys::TX) @@ -727,18 +701,22 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta) pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value' - for (const uint256& hash : wss.vWalletUpgrade) + for (const uint256& hash : wss.vWalletUpgrade) { WriteTx(pwallet->mapWallet.at(hash)); + } // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: - if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) + if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) { return DB_NEED_REWRITE; + } - if (wss.nFileVersion < CLIENT_VERSION) // Update + if (wss.nFileVersion < CLIENT_VERSION) { // Update WriteVersion(CLIENT_VERSION); + } - if (wss.fAnyUnordered) + if (wss.fAnyUnordered) { result = ReorderTransactions(pwallet); + } return result; } @@ -752,8 +730,9 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, std::vector& vTxHash LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (batch.Read((std::string) DBKeys::MINVERSION, nMinVersion)) { - if (nMinVersion > CLIENT_VERSION) + if (nMinVersion > CLIENT_VERSION) { return DB_TOO_NEW; + } pwallet->LoadMinVersion(nMinVersion); } @@ -769,9 +748,9 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, std::vector& vTxHash CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = batch.ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) + if (ret == DB_NOTFOUND) { break; - else if (ret != 0) { + } else if (ret != 0) { LogPrintf("Error reading next record from wallet database\n"); return DB_CORRUPT; } @@ -807,13 +786,13 @@ DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, std::vector& vWtx) // build list of wallet TXs std::vector vTxHash; DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); - if (err != DB_LOAD_OK) + if (err != DB_LOAD_OK) { return err; + } // erase each wallet TX for (uint256& hash : vTxHash) { - if (!EraseTx(hash)) - return DB_CORRUPT; + if (!EraseTx(hash)) return DB_CORRUPT; } return DB_LOAD_OK; @@ -829,20 +808,22 @@ void MaybeCompactWalletDB() return; } - static unsigned int nLastSeen = CWalletDB::GetUpdateCounter(); - static unsigned int nLastFlushed = CWalletDB::GetUpdateCounter(); - static int64_t nLastWalletUpdate = GetTime(); + for (CWalletRef pwallet : vpwallets) { + CWalletDBWrapper& dbh = pwallet->GetDBHandle(); - if (nLastSeen != CWalletDB::GetUpdateCounter()) { - nLastSeen = CWalletDB::GetUpdateCounter(); - nLastWalletUpdate = GetTime(); - } + unsigned int nUpdateCounter = dbh.nUpdateCounter; + if (dbh.nLastSeen != nUpdateCounter) { + dbh.nLastSeen = nUpdateCounter; + dbh.nLastWalletUpdate = GetTime(); + } - if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2) { - if (CDB::PeriodicFlush(pwalletMain->GetDBHandle())) { - nLastFlushed = CWalletDB::GetUpdateCounter(); + if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) { + if (CDB::PeriodicFlush(dbh)) { + dbh.nLastFlushed = nUpdateCounter; + } } } + fOneThread = false; } @@ -1093,16 +1074,16 @@ bool AttemptBackupWallet(const CWallet& wallet, const fs::path& pathSrc, const f // // Try to (very carefully!) recover wallet file if there is a problem. // -bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)) +bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename) { - return CDB::Recover(filename, callbackDataIn, recoverKVcallback); + return CDB::Recover(filename, callbackDataIn, recoverKVcallback, out_backup_filename); } -bool CWalletDB::Recover(const std::string& filename) +bool CWalletDB::Recover(const std::string& filename, std::string& out_backup_filename) { // recover without a key filter callback // results in recovering all record types - return CWalletDB::Recover(filename, NULL, NULL); + return CWalletDB::Recover(filename, NULL, NULL, out_backup_filename); } bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue) @@ -1140,24 +1121,12 @@ bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path bool CWalletDB::WriteDestData(const std::string& address, const std::string& key, const std::string& value) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string(DBKeys::DESTDATA), std::make_pair(address, key)), value); + return WriteIC(std::make_pair(std::string(DBKeys::DESTDATA), std::make_pair(address, key)), value); } bool CWalletDB::EraseDestData(const std::string& address, const std::string& key) { - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string(DBKeys::DESTDATA), std::make_pair(address, key))); -} - -void CWalletDB::IncrementUpdateCounter() -{ - nWalletDBUpdateCounter++; -} - -unsigned int CWalletDB::GetUpdateCounter() -{ - return nWalletDBUpdateCounter; + return EraseIC(std::make_pair(std::string(DBKeys::DESTDATA), std::make_pair(address, key))); } bool CWalletDB::TxnBegin() diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 981750975fdb..281bba204508 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -1,11 +1,11 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2013 The Bitcoin developers -// Copyright (c) 2016-2020 The PIVX developers +// Copyright (c) 2009-2021 The Bitcoin developers +// Copyright (c) 2016-2021 The PIVX developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_WALLETDB_H -#define BITCOIN_WALLETDB_H +#ifndef PIVX_WALLETDB_H +#define PIVX_WALLETDB_H #include "amount.h" #include "wallet/db.h" @@ -114,9 +114,31 @@ class CKeyMetadata */ class CWalletDB { +private: + template + bool WriteIC(const K& key, const T& value, bool fOverwrite = true) + { + if (!batch.Write(key, value, fOverwrite)) { + return false; + } + m_dbw.IncrementUpdateCounter(); + return true; + } + + template + bool EraseIC(const K& key) + { + if (!batch.Erase(key)) { + return false; + } + m_dbw.IncrementUpdateCounter(); + return true; + } + public: CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) : - batch(dbw, pszMode, _fFlushOnClose) + batch(dbw, pszMode, _fFlushOnClose), + m_dbw(dbw) { } @@ -185,9 +207,9 @@ class CWalletDB DBErrors FindWalletTx(CWallet* pwallet, std::vector& vTxHash, std::vector& vWtx); DBErrors ZapWalletTx(CWallet* pwallet, std::vector& vWtx); /* Try to (very carefully!) recover wallet database (with a possible key type filter) */ - static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)); - /* Recover convenience-function to bypass the key filter callback, called when verify failes, recoveres everything */ - static bool Recover(const std::string& filename); + static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); + /* Recover convenience-function to bypass the key filter callback, called when verify fails, recovers everything */ + static bool Recover(const std::string& filename, std::string& out_backup_filename); /* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */ static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue); /* Function to determin if a certain KV/key-type is a key (cryptographical key) type */ @@ -197,9 +219,6 @@ class CWalletDB /* verifies the database file */ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr); - static void IncrementUpdateCounter(); - static unsigned int GetUpdateCounter(); - //! Begin a new transaction bool TxnBegin(); //! Commit current transaction @@ -212,6 +231,7 @@ class CWalletDB bool WriteVersion(int nVersion); private: CDB batch; + CWalletDBWrapper& m_dbw; CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); @@ -227,4 +247,4 @@ bool AutoBackupWallet(const std::string& strWalletFile, std::string& strBackupWa //! Compacts BDB state so that wallet.dat is self-contained (if there are changes) void MaybeCompactWalletDB(); -#endif // BITCOIN_WALLETDB_H +#endif // PIVX_WALLETDB_H