From 83783229a33a96be0bacf64affd2229198b37b56 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Sun, 25 Apr 2021 22:37:54 +0200 Subject: [PATCH 01/50] [Refactor] Replace optional reserveKey in PBF with unique pointer --- src/miner.cpp | 15 ++++++--------- src/miner.h | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index 6695eef856d45..3f782bea7eff6 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -39,7 +39,7 @@ double dHashesPerSec = 0.0; int64_t nHPSTimerStart = 0; -std::unique_ptr CreateNewBlockWithKey(CReserveKey* reservekey, CWallet* pwallet) +std::unique_ptr CreateNewBlockWithKey(std::unique_ptr& reservekey, CWallet* pwallet) { CPubKey pubkey; if (!reservekey->GetReservedKey(pubkey)) return nullptr; @@ -62,7 +62,7 @@ std::unique_ptr CreateNewBlockWithScript(const CScript& coinbase return BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(coinbaseScript, pwallet, false); } -bool ProcessBlockFound(const std::shared_ptr& pblock, CWallet& wallet, Optional& reservekey) +bool ProcessBlockFound(const std::shared_ptr& pblock, CWallet& wallet, std::unique_ptr& reservekey) { LogPrintf("%s\n", pblock->ToString()); LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0]->vout[0].nValue)); @@ -118,10 +118,7 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) const int64_t nSpacingMillis = consensus.nTargetSpacing * 1000; // Each thread has its own key and counter - Optional opReservekey{nullopt}; - if (!fProofOfStake) { - opReservekey = CReserveKey(pwallet); - } + std::unique_ptr pReservekey = fProofOfStake ? nullptr : std::make_unique(pwallet); // Available UTXO set std::vector availableCoins; @@ -171,7 +168,7 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) std::unique_ptr pblocktemplate((fProofOfStake ? BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), pwallet, true, &availableCoins) : - CreateNewBlockWithKey(opReservekey.get_ptr(), pwallet))); + CreateNewBlockWithKey(pReservekey, pwallet))); if (!pblocktemplate) continue; std::shared_ptr pblock = std::make_shared(pblocktemplate->block); @@ -179,7 +176,7 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) if (fProofOfStake) { LogPrintf("%s : proof-of-stake block was signed %s \n", __func__, pblock->GetHash().ToString().c_str()); SetThreadPriority(THREAD_PRIORITY_NORMAL); - if (!ProcessBlockFound(pblock, *pwallet, opReservekey)) { + if (!ProcessBlockFound(pblock, *pwallet, pReservekey)) { LogPrintf("%s: New block orphaned\n", __func__); continue; } @@ -209,7 +206,7 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) SetThreadPriority(THREAD_PRIORITY_NORMAL); LogPrintf("%s:\n", __func__); LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex(), hashTarget.GetHex()); - ProcessBlockFound(pblock, *pwallet, opReservekey); + ProcessBlockFound(pblock, *pwallet, pReservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); // In regression test mode, stop mining after a block is found. This diff --git a/src/miner.h b/src/miner.h index 6a141e9920678..22bd7f9fe1dd7 100644 --- a/src/miner.h +++ b/src/miner.h @@ -27,7 +27,7 @@ static const bool DEFAULT_PRINTPRIORITY = false; /** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads); /** Generate a new PoW block, without valid proof-of-work */ - std::unique_ptr CreateNewBlockWithKey(CReserveKey* reservekey, CWallet* pwallet); + std::unique_ptr CreateNewBlockWithKey(std::unique_ptr& reservekey, CWallet* pwallet); std::unique_ptr CreateNewBlockWithScript(const CScript& coinbaseScript, CWallet* pwallet); void BitcoinMiner(CWallet* pwallet, bool fProofOfStake); From 75c8c6dd40090020a36a99f6878c3c4c7f92014e Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Thu, 2 Mar 2017 17:04:39 -0800 Subject: [PATCH 02/50] Disallow copy of CReserveKeys --- src/wallet/wallet.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index d9d8113e21939..df02b8048f3dc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1218,6 +1218,10 @@ class CReserveKey pwallet = pwalletIn; } + CReserveKey() = default; + CReserveKey(const CReserveKey&) = delete; + CReserveKey& operator=(const CReserveKey&) = delete; + ~CReserveKey() { ReturnKey(); From 0201065ce3d500a124070169354c927f7212b685 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Sun, 25 Apr 2021 21:56:46 +0200 Subject: [PATCH 03/50] Add COutput::fSafe member for safe handling of unconfirmed outputs >>> adapted from bitcoin@af61d9f78bec62ff3688d88409a53df9ff5bc591 This exposes a value computed in CWallet::AvailableCoins so it can used for other things, like inclusion in listunspent output. --- src/sapling/sapling_operation.cpp | 2 +- src/wallet/rpcwallet.cpp | 3 ++- src/wallet/test/wallet_tests.cpp | 2 +- src/wallet/wallet.cpp | 41 ++++++++++++++++++------------- src/wallet/wallet.h | 24 +++++++++++++----- 5 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/sapling/sapling_operation.cpp b/src/sapling/sapling_operation.cpp index b513e33d16f31..006e905584c61 100644 --- a/src/sapling/sapling_operation.cpp +++ b/src/sapling/sapling_operation.cpp @@ -287,7 +287,7 @@ OperationResult SaplingOperation::loadUtxos(TxValues& txValues) const auto* tx = wallet->GetWalletTx(outpoint.outPoint.hash); if (!tx) continue; nSelectedValue += tx->tx->vout[outpoint.outPoint.n].nValue; - selectedUTXOInputs.emplace_back(tx, outpoint.outPoint.n, 0, true, true); + selectedUTXOInputs.emplace_back(tx, outpoint.outPoint.n, 0, true, true, true); } return loadUtxos(txValues, selectedUTXOInputs, nSelectedValue); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e0a879746589d..5051c7567fdac 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3713,11 +3713,12 @@ UniValue listunspent(const JSONRPCRequest& request) CCoinControl coinControl; coinControl.fAllowWatchOnly = nWatchonlyConfig == 2; + coinFilter.fOnlySafe = false; + UniValue results(UniValue::VARR); std::vector vecOutputs; LOCK2(cs_main, pwallet->cs_wallet); - coinFilter.fOnlyConfirmed = false; pwallet->AvailableCoins(&vecOutputs, &coinControl, coinFilter); for (const COutput& out : vecOutputs) { if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 483073906c1fa..09213a4b9fe8f 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -53,7 +53,7 @@ static void add_coin(std::unique_ptr& pwallet, const CAmount& nValue, i if (fIsFromMe) { wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1); } - COutput output(wtx.get(), nInput, nAge, true, true); + COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */); vCoins.push_back(output); wtxn.emplace_back(std::move(wtx)); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d7b86d9bee64a..c61c56eee3a7f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2439,6 +2439,8 @@ void CWallet::GetAvailableP2CSCoins(std::vector& vCoins) const { if (fConflicted || nDepth < 0) continue; + bool fSafe = pcoin->IsTrusted(); + if (pcoin->tx->HasP2CSOutputs()) { for (int i = 0; i < (int) pcoin->tx->vout.size(); i++) { const auto &utxo = pcoin->tx->vout[i]; @@ -2451,7 +2453,7 @@ void CWallet::GetAvailableP2CSCoins(std::vector& vCoins) const { bool isMineSpendable = mine & ISMINE_SPENDABLE_DELEGATED; if (mine & ISMINE_COLD || isMineSpendable) // Depth and solvability members are not used, no need waste resources and set them for now. - vCoins.emplace_back(pcoin, i, 0, isMineSpendable, true); + vCoins.emplace_back(pcoin, i, 0, isMineSpendable, true, fSafe); } } } @@ -2463,9 +2465,9 @@ void CWallet::GetAvailableP2CSCoins(std::vector& vCoins) const { /** * Test if the transaction is spendable. */ -static bool CheckTXAvailabilityInternal(const CWalletTx* pcoin, bool fOnlyConfirmed, int& nDepth) +static bool CheckTXAvailabilityInternal(const CWalletTx* pcoin, bool fOnlySafe, int& nDepth) { - if (fOnlyConfirmed && !pcoin->IsTrusted()) return false; + if (fOnlySafe && !pcoin->IsTrusted()) return false; if (pcoin->GetBlocksToMaturity() > 0) return false; nDepth = pcoin->GetDepthInMainChain(); @@ -2478,22 +2480,22 @@ static bool CheckTXAvailabilityInternal(const CWalletTx* pcoin, bool fOnlyConfir } // cs_main lock required -static bool CheckTXAvailability(const CWalletTx* pcoin, bool fOnlyConfirmed, int& nDepth) +static bool CheckTXAvailability(const CWalletTx* pcoin, bool fOnlySafe, int& nDepth) { AssertLockHeld(cs_main); if (!CheckFinalTx(pcoin->tx)) return false; - return CheckTXAvailabilityInternal(pcoin, fOnlyConfirmed, nDepth); + return CheckTXAvailabilityInternal(pcoin, fOnlySafe, nDepth); } // cs_main lock NOT required static bool CheckTXAvailability(const CWalletTx* pcoin, - bool fOnlyConfirmed, + bool fOnlySafe, int& nDepth, int nBlockHeight) { // Mimic CheckFinalTx without cs_main lock if (!IsFinalTx(pcoin->tx, nBlockHeight + 1, GetAdjustedTime())) return false; - return CheckTXAvailabilityInternal(pcoin, fOnlyConfirmed, nDepth); + return CheckTXAvailabilityInternal(pcoin, fOnlySafe, nDepth); } bool CWallet::GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash, std::string strOutputIndex, std::string& strError) @@ -2566,7 +2568,7 @@ bool CWallet::GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& } return GetVinAndKeysFromOutput( - COutput(wtx, nOutputIndex, nDepth, true, true), + COutput(wtx, nOutputIndex, nDepth, true, true, true), txinRet, pubKeyRet, keyRet); @@ -2639,12 +2641,14 @@ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates // Check if the tx is selectable int nDepth; - if (!CheckTXAvailability(pcoin, coinsFilter.fOnlyConfirmed, nDepth, m_last_block_processed_height)) + if (!CheckTXAvailability(pcoin, coinsFilter.fOnlySafe, nDepth, m_last_block_processed_height)) continue; // Check min depth filtering requirements if (nDepth < coinsFilter.minDepth) continue; + bool safeTx = pcoin->IsTrusted(); + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { const auto& output = pcoin->tx->vout[i]; @@ -2680,7 +2684,7 @@ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates // found valid coin if (!pCoins) return true; - pCoins->emplace_back(pcoin, (int) i, nDepth, res.spendable, res.solvable); + pCoins->emplace_back(pcoin, (int) i, nDepth, res.spendable, res.solvable, safeTx); // Checks the sum amount of all UTXO's. if (coinsFilter.nMinimumSumAmount != 0) { @@ -2701,11 +2705,11 @@ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates } } -std::map > CWallet::AvailableCoinsByAddress(bool fConfirmed, CAmount maxCoinValue, bool fIncludeColdStaking) +std::map > CWallet::AvailableCoinsByAddress(bool fOnlySafe, CAmount maxCoinValue, bool fIncludeColdStaking) { CWallet::AvailableCoinsFilter coinFilter; coinFilter.fIncludeColdStaking = true; - coinFilter.fOnlyConfirmed = fConfirmed; + coinFilter.fOnlySafe = fOnlySafe; coinFilter.fIncludeColdStaking = fIncludeColdStaking; coinFilter.nMaxOutValue = maxCoinValue; std::vector vCoins; @@ -2810,7 +2814,7 @@ bool CWallet::StakeableCoins(std::vector* pCoins) // found valid coin if (!pCoins) return true; if (!pindex) pindex = mapBlockIndex.at(pcoin->m_confirm.hashBlock); - pCoins->emplace_back(CStakeableOutput(pcoin, (int) index, nDepth, res.spendable, res.solvable, pindex)); + pCoins->emplace_back(pcoin, (int) index, nDepth, pindex); } } return (pCoins && !pCoins->empty()); @@ -4959,7 +4963,10 @@ const CWDestination* CAddressBookIterator::GetDestKey() return &it->first; } -CStakeableOutput::CStakeableOutput(const CWalletTx* txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, - const CBlockIndex*& _pindex) : COutput(txIn, iIn, nDepthIn, fSpendableIn, fSolvableIn), - pindex(_pindex) {} - +CStakeableOutput::CStakeableOutput(const CWalletTx* txIn, + int iIn, + int nDepthIn, + const CBlockIndex*& _pindex) : + COutput(txIn, iIn, nDepthIn, true /*fSpendable*/, true/*fSolvable*/, true/*fSafe*/), + pindex(_pindex) +{} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index df02b8048f3dc..64e821ed1ec5d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -766,7 +766,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface AvailableCoinsFilter() {} AvailableCoinsFilter(bool _fIncludeDelegated, bool _fIncludeColdStaking, - bool _fOnlyConfirmed, + bool _fOnlySafe, bool _fOnlySpendable, std::set* _onlyFilteredDest, int _minDepth, @@ -774,7 +774,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface CAmount _nMaxOutValue = 0) : fIncludeDelegated(_fIncludeDelegated), fIncludeColdStaking(_fIncludeColdStaking), - fOnlyConfirmed(_fOnlyConfirmed), + fOnlySafe(_fOnlySafe), fOnlySpendable(_fOnlySpendable), onlyFilteredDest(_onlyFilteredDest), minDepth(_minDepth), @@ -783,7 +783,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool fIncludeDelegated{true}; bool fIncludeColdStaking{false}; - bool fOnlyConfirmed{true}; + bool fOnlySafe{true}; bool fOnlySpendable{false}; std::set* onlyFilteredDest{nullptr}; int minDepth{0}; @@ -1238,11 +1238,23 @@ class COutput const CWalletTx* tx; int i; int nDepth; + + /** Whether we have the private keys to spend this output */ bool fSpendable; + + /** Whether we know how to spend this output, ignoring the lack of keys */ bool fSolvable; - COutput(const CWalletTx* txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn) : - tx(txIn), i(iIn), nDepth(nDepthIn), fSpendable(fSpendableIn), fSolvable(fSolvableIn) {} + /** + * Whether this output is considered safe to spend. Unconfirmed transactions + * from outside keys and unconfirmed replacement transactions are considered + * unsafe and will not be used to fund new spending transactions. + */ + bool fSafe; + + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn) : + tx(txIn), i(iIn), nDepth(nDepthIn), fSpendable(fSpendableIn), fSolvable(fSolvableIn), fSafe(fSafeIn) + {} CAmount Value() const { return tx->tx->vout[i].nValue; } std::string ToString() const; @@ -1253,7 +1265,7 @@ class CStakeableOutput : public COutput public: const CBlockIndex* pindex{nullptr}; - CStakeableOutput(const CWalletTx* txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, + CStakeableOutput(const CWalletTx* txIn, int iIn, int nDepthIn, const CBlockIndex*& pindex); }; From f219be937ad3d23276f7a1512821b21070821d09 Mon Sep 17 00:00:00 2001 From: NicolasDorier Date: Thu, 23 Feb 2017 05:58:31 +0000 Subject: [PATCH 04/50] Add safe flag to listunspent result --- src/rpc/client.cpp | 1 + src/wallet/rpcwallet.cpp | 19 ++++++++++++++----- test/functional/wallet_basic.py | 22 ++++++++++++++++++---- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 1a0dd69efb810..002d4b91e6a0f 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -109,6 +109,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { { "listunspent", 2, "addresses" }, { "listunspent", 3, "watchonly_config" }, { "listunspent", 4, "query_options" }, + { "listunspent", 5, "include_unsafe" }, { "lockunspent", 0, "unlock" }, { "lockunspent", 1, "transactions" }, { "logging", 0, "include" }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5051c7567fdac..6dfb0c845ae46 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3591,9 +3591,9 @@ UniValue listunspent(const JSONRPCRequest& request) if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - if (request.fHelp || request.params.size() > 5) + if (request.fHelp || request.params.size() > 6) throw std::runtime_error( - "listunspent ( minconf maxconf [\"address\",...] watchonly_config [query_options])\n" + "listunspent ( minconf maxconf [\"address\",...] watchonly_config [query_options] include_unsafe)\n" "\nReturns array of unspent transaction outputs\n" "with between minconf and maxconf (inclusive) confirmations.\n" "Optionally filter to only include txouts paid to specified addresses.\n" @@ -3616,6 +3616,9 @@ UniValue listunspent(const JSONRPCRequest& request) " \"maximumCount\" (numeric or string, default=unlimited) Maximum number of UTXOs\n" " \"minimumSumAmount\" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in " + CURRENCY_UNIT + "\n" " }\n" + "6. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n" + " See description of \"safe\" attribute below.\n" + "\nResult\n" "[ (array of json object)\n" " {\n" @@ -3629,7 +3632,10 @@ UniValue listunspent(const JSONRPCRequest& request) " \"amount\" : x.xxx, (numeric) the transaction amount in PIV\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"spendable\" : true|false (boolean) Whether we have the private keys to spend this output\n" - " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" + " \"solvable\" : xxx (boolean) Whether we know how to spend this output, ignoring the lack of keys\n" + " \"safe\" : xxx (boolean) Whether this output is considered safe to spend. Unconfirmed transactions\n" + " from outside keys and unconfirmed replacement transactions are considered unsafe\n" + " and are not eligible for spending by fundrawtransaction and sendtoaddress.\n" " }\n" " ,...\n" "]\n" @@ -3638,6 +3644,7 @@ UniValue listunspent(const JSONRPCRequest& request) HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + HelpExampleCli("listunspent", "6 9999999 '[]' 1 '{ \"minimumAmount\": 0.005 }'") + + HelpExampleCli("listunspent", "6 9999999 '[]' 1 '{ \"minimumAmount\": 0.005 }' false") + HelpExampleRpc("listunspent", "6, 9999999, [] , 1, { \"minimumAmount\": 0.005 } ") ); @@ -3713,7 +3720,8 @@ UniValue listunspent(const JSONRPCRequest& request) CCoinControl coinControl; coinControl.fAllowWatchOnly = nWatchonlyConfig == 2; - coinFilter.fOnlySafe = false; + bool include_unsafe = request.params.size() < 6 || request.params[5].get_bool(); + coinFilter.fOnlySafe = !include_unsafe; UniValue results(UniValue::VARR); std::vector vecOutputs; @@ -3760,6 +3768,7 @@ UniValue listunspent(const JSONRPCRequest& request) entry.pushKV("confirmations", out.nDepth); entry.pushKV("spendable", out.fSpendable); entry.pushKV("solvable", out.fSolvable); + entry.pushKV("safe", out.fSafe); results.push_back(entry); } @@ -4492,7 +4501,7 @@ static const CRPCCommand commands[] = { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly","filter"} }, { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} }, { "wallet", "listtransactions", &listtransactions, false, {"dummy","count","from","include_watchonly","include_delegated","include_cold"} }, - { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","watchonly_config" } }, + { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","watchonly_config","query_options","include_unsafe" } }, { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, { "wallet", "rawdelegatestake", &rawdelegatestake, false, {"staking_addr","amount","owner_addr","ext_owner","include_delegated","from_shield","force"} }, { "wallet", "sendmany", &sendmany, false, {"dummy","amounts","minconf","comment","include_delegated"} }, diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index b78231f713673..753a59d83e728 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -76,6 +76,9 @@ def run_test(self): # Exercise locking of unspent outputs unspent_0 = self.nodes[1].listunspent()[0] + assert unspent_0["solvable"] + assert unspent_0["spendable"] + assert unspent_0["safe"] unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]} self.nodes[1].lockunspent(False, [unspent_0]) assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[1].sendtoaddress, self.nodes[1].getnewaddress(), 20) @@ -85,15 +88,26 @@ def run_test(self): # Send 21 PIV from 1 to 0 using sendtoaddress call. self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 21) + self.sync_mempools(self.nodes[0:3]) + + # Node0 should have two unspent outputs. + # One safe, the other one not yet + node0utxos = self.nodes[0].listunspent(0) + assert_equal(len(node0utxos), 2) + newutxos = [x for x in node0utxos if x["txid"] != utxos[0]["txid"]] + assert_equal(len(newutxos), 1) + assert not newutxos[0]["safe"] + + # Mine the other tx self.nodes[1].generate(1) self.sync_all(self.nodes[0:3]) + node0utxos = self.nodes[0].listunspent() + assert_equal(len(node0utxos), 2) + for u in node0utxos: + assert u["safe"] - # Node0 should have two unspent outputs. # Create a couple of transactions to send them to node2, submit them through # node1, and make sure both node0 and node2 pick them up properly: - node0utxos = self.nodes[0].listunspent(1) - assert_equal(len(node0utxos), 2) - # create both transactions fee_per_kbyte = Decimal('0.001') txns_to_send = [] From 3a599d0a8a9f0d90b817a7224c151c8e25d4f2b1 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Fri, 28 May 2021 09:55:09 +0200 Subject: [PATCH 05/50] [Refactor] Return safeTx boolean in CheckTXAvailability --- src/wallet/wallet.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c61c56eee3a7f..2b99d30bd5b66 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2465,9 +2465,10 @@ void CWallet::GetAvailableP2CSCoins(std::vector& vCoins) const { /** * Test if the transaction is spendable. */ -static bool CheckTXAvailabilityInternal(const CWalletTx* pcoin, bool fOnlySafe, int& nDepth) +static bool CheckTXAvailabilityInternal(const CWalletTx* pcoin, bool fOnlySafe, int& nDepth, bool& safeTx) { - if (fOnlySafe && !pcoin->IsTrusted()) return false; + safeTx = pcoin->IsTrusted(); + if (fOnlySafe && !safeTx) return false; if (pcoin->GetBlocksToMaturity() > 0) return false; nDepth = pcoin->GetDepthInMainChain(); @@ -2480,22 +2481,23 @@ static bool CheckTXAvailabilityInternal(const CWalletTx* pcoin, bool fOnlySafe, } // cs_main lock required -static bool CheckTXAvailability(const CWalletTx* pcoin, bool fOnlySafe, int& nDepth) +static bool CheckTXAvailability(const CWalletTx* pcoin, bool fOnlySafe, int& nDepth, bool& safeTx) { AssertLockHeld(cs_main); if (!CheckFinalTx(pcoin->tx)) return false; - return CheckTXAvailabilityInternal(pcoin, fOnlySafe, nDepth); + return CheckTXAvailabilityInternal(pcoin, fOnlySafe, nDepth, safeTx); } // cs_main lock NOT required static bool CheckTXAvailability(const CWalletTx* pcoin, bool fOnlySafe, int& nDepth, + bool& safeTx, int nBlockHeight) { // Mimic CheckFinalTx without cs_main lock if (!IsFinalTx(pcoin->tx, nBlockHeight + 1, GetAdjustedTime())) return false; - return CheckTXAvailabilityInternal(pcoin, fOnlySafe, nDepth); + return CheckTXAvailabilityInternal(pcoin, fOnlySafe, nDepth, safeTx); } bool CWallet::GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash, std::string strOutputIndex, std::string& strError) @@ -2539,10 +2541,11 @@ bool CWallet::GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& } int nDepth = 0; + bool safeTx = false; { LOCK(cs_wallet); // Check availability - if (!CheckTXAvailability(wtx, true, nDepth, m_last_block_processed_height)) { + if (!CheckTXAvailability(wtx, true, nDepth, safeTx, m_last_block_processed_height)) { strError = "Not available collateral transaction"; return error("%s: tx %s not available", __func__, strTxHash); } @@ -2640,15 +2643,14 @@ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates const CWalletTx* pcoin = &(*it).second; // Check if the tx is selectable - int nDepth; - if (!CheckTXAvailability(pcoin, coinsFilter.fOnlySafe, nDepth, m_last_block_processed_height)) + int nDepth = 0; + bool safeTx = false; + if (!CheckTXAvailability(pcoin, coinsFilter.fOnlySafe, nDepth, safeTx, m_last_block_processed_height)) continue; // Check min depth filtering requirements if (nDepth < coinsFilter.minDepth) continue; - bool safeTx = pcoin->IsTrusted(); - for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { const auto& output = pcoin->tx->vout[i]; @@ -2789,8 +2791,9 @@ bool CWallet::StakeableCoins(std::vector* pCoins) const CWalletTx* pcoin = &(it).second; // Check if the tx is selectable - int nDepth; - if (!CheckTXAvailability(pcoin, true, nDepth)) + int nDepth = 0; + bool safeTx = false; + if (!CheckTXAvailability(pcoin, true, nDepth, safeTx)) continue; // Check min depth requirement for stake inputs From 3633d75d945454d4d5884115866219ef6dbdeed1 Mon Sep 17 00:00:00 2001 From: Patrick Strateman Date: Tue, 14 Mar 2017 15:48:08 -0700 Subject: [PATCH 06/50] Initialize nRelockTime --- src/wallet/wallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2b99d30bd5b66..a56f182789ddc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4552,6 +4552,7 @@ void CWallet::SetNull() nNextResend = 0; nLastResend = 0; nTimeFirstKey = 0; + nRelockTime = 0; fAbortRescan = false; fScanningWallet = false; fWalletUnlockStaking = false; From 60bb4daafd5ba70711f14d09a67272712a671110 Mon Sep 17 00:00:00 2001 From: Ryan Havar Date: Tue, 28 Mar 2017 18:11:44 +0000 Subject: [PATCH 07/50] ApproximateBestSubset should take inputs by reference, not value --- src/wallet/wallet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a56f182789ddc..334641d4621db 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2739,7 +2739,8 @@ std::map > CWallet::AvailableCoinsByAddres return mapCoins; } -static void ApproximateBestSubset(std::vector > > vValue, const CAmount& nTotalLower, const CAmount& nTargetValue, std::vector& vfBest, CAmount& nBest, int iterations = 1000) +static void ApproximateBestSubset(const std::vector > >& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue, + std::vector& vfBest, CAmount& nBest, int iterations = 1000) { std::vector vfIncluded; From 7d977acec65fb3f96ef071bd7be554213b1cc1c2 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Sun, 25 Apr 2021 23:13:04 +0200 Subject: [PATCH 08/50] Remove unused C++ code not covered by unit tests >>> based on bitcoin@b51aaf1c42d9936ddbe8ec48a0d9b675221ab7ba --- src/netaddress.cpp | 6 ------ src/netaddress.h | 1 - src/script/interpreter.cpp | 3 --- src/wallet/db.cpp | 9 --------- src/wallet/db.h | 1 - 5 files changed, 20 deletions(-) diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 87ce9e53a0118..7c2cb6862da34 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -180,12 +180,6 @@ bool CNetAddr::IsLocal() const return false; } -bool CNetAddr::IsMulticast() const -{ - return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0) - || (GetByte(15) == 0xFF); -} - bool CNetAddr::IsValid() const { // Cleanup 3-byte shifted addresses caused by garbage in size field diff --git a/src/netaddress.h b/src/netaddress.h index 7894e2aff5c21..18a3a826bfc55 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -65,7 +65,6 @@ class CNetAddr bool IsLocal() const; bool IsRoutable() const; bool IsValid() const; - bool IsMulticast() const; enum Network GetNetwork() const; std::string ToString() const; std::string ToStringIP() const; diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 837bb84cdd9ac..b036da1091ea7 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -240,10 +240,7 @@ bool EvalScript(std::vector >& stack, const CScript& { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); - static const CScriptNum bnFalse(0); - static const CScriptNum bnTrue(1); static const valtype vchFalse(0); - static const valtype vchZero(0); static const valtype vchTrue(1, 1); CScript::const_iterator pc = script.begin(); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 6e36edf870097..b64004a45f913 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -466,15 +466,6 @@ void CDBEnv::CloseDb(const std::string& strFile) } } -bool CDBEnv::RemoveDb(const std::string& strFile) -{ - this->CloseDb(strFile); - - LOCK(cs_db); - int rc = dbenv->dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); - return (rc == 0); -} - bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) { if (dbw.IsDummy()) { diff --git a/src/wallet/db.h b/src/wallet/db.h index 1c76d58c91e85..f39e60f2c0c15 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -76,7 +76,6 @@ class CDBEnv void CheckpointLSN(const std::string& strFile); void CloseDb(const std::string& strFile); - bool RemoveDb(const std::string& strFile); DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) { From 239408316f9cc2ca28202355965c5dd6015de0c2 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 28 Apr 2017 17:22:37 -0400 Subject: [PATCH 09/50] [Wallet] unset change position when there is no change on exact match --- src/wallet/wallet.cpp | 4 +++- test/functional/rpc_fundrawtransaction.py | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 334641d4621db..1f83430969b50 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3176,8 +3176,10 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, txNew.vout.insert(position, newTxOut); } } - } else + } else { reservekey.ReturnKey(); + nChangePosInOut = -1; + } // Fill vin for (const std::pair& coin : setCoins) { diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 396008674f95c..d00dcfd880d3b 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -76,6 +76,12 @@ def run_test(self): self.nodes[0].generate(121) self.sync_all() + # ensure that setting changePosition in fundraw with an exact match is handled properly + out_no_change = 250.0 - 0.0000374 # one coinbase input minus min fee + rawmatch = self.nodes[0].createrawtransaction([], {self.nodes[2].getnewaddress(): DecimalAmt(out_no_change)}) + rawmatch = self.nodes[0].fundrawtransaction(rawmatch, {"changePosition": 1}) + assert_equal(rawmatch["changepos"], -1) + watchonly_address = self.nodes[0].getnewaddress() watchonly_pubkey = self.nodes[0].validateaddress(watchonly_address)["pubkey"] self.watchonly_amount = DecimalAmt(200.0) From 494ba6471140bbfeb063a1cd1249aaed41834291 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Mon, 26 Apr 2021 00:00:56 +0200 Subject: [PATCH 10/50] [test] Add test for getmemoryinfo >>> adapted from bitcoin@d4668f35ab230083e39ab05857ccae4fe77dcb50 --- test/functional/wallet_basic.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 753a59d83e728..3d5b04d62730e 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -87,7 +87,12 @@ def run_test(self): assert_equal(len(self.nodes[1].listlockunspent()), 0) # Send 21 PIV from 1 to 0 using sendtoaddress call. + # Locked memory should use at least 32 bytes to sign the transaction + self.log.info("test getmemoryinfo") + memory_before = self.nodes[0].getmemoryinfo() self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 21) + memory_after = self.nodes[0].getmemoryinfo() + assert(memory_before['locked']['used'] + 32 <= memory_after['locked']['used']) self.sync_mempools(self.nodes[0:3]) # Node0 should have two unspent outputs. From 54fa12279deab5508a787a779f2ab8e4a2ea694a Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 12:44:58 +0200 Subject: [PATCH 11/50] [qt] Move some WalletModel functions into CWallet >>> inspired by bitcoin@d944bd7a27abe0fa74d40ba5e90f345f51bb5141 Motivation for moving these is to make supporting IPC simpler (see bitcoin PR 10102), so these lookups can be one-shot IPC requests, instead of back-and-forth interactions over the IPC channel. Also these functions are potentially useful outside of the bitcoin GUI (e.g. for RPCs). --- src/qt/walletmodel.cpp | 62 +++++++++++++----------------------------- src/qt/walletmodel.h | 5 ++++ src/wallet/wallet.cpp | 44 ++++++++++++++++++++++++++++++ src/wallet/wallet.h | 9 ++++++ 4 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 1575a002d2a50..232ed49cbd98d 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1063,44 +1063,25 @@ void WalletModel::listAvailableNotes(std::map>& mapCoins) const { - CWallet::AvailableCoinsFilter filter; - filter.fIncludeLocked = true; - std::vector vCoins; - wallet->AvailableCoins(&vCoins, nullptr, filter); - - for (const COutput& out : vCoins) { - if (!out.fSpendable) continue; - - const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; - const bool isP2CS = scriptPubKey.IsPayToColdStaking(); - - CTxDestination outputAddress; - CTxDestination outputAddressStaker; - if (isP2CS) { - txnouttype type; std::vector addresses; int nRequired; - if(!ExtractDestinations(scriptPubKey, type, addresses, nRequired) - || addresses.size() != 2) throw std::runtime_error("Cannot extract P2CS addresses from a stored transaction"); - outputAddressStaker = addresses[0]; - outputAddress = addresses[1]; - } else { - if (!ExtractDestination(scriptPubKey, outputAddress)) - continue; + for (const auto& it: wallet->ListCoins()) { + const std::pair>& addresses = it.first; + const std::vector& coins = it.second; + + const QString& address = QString::fromStdString(EncodeDestination(addresses.first)); + const Optional& stakerAddr = addresses.second == nullopt ? nullopt : Optional( + QString::fromStdString(EncodeDestination(*addresses.second, CChainParams::STAKING_ADDRESS))); + // P2CS cannot be "change" + const bool isChange = stakerAddr == nullopt ? wallet->IsChange(addresses.first) : false; + + const ListCoinsKey key{address, isChange, stakerAddr}; + + for (const COutput& out: coins) { + mapCoins[key].emplace_back(out.tx->GetHash(), + out.i, + out.tx->tx->vout[out.i].nValue, + out.tx->GetTxTime(), + out.nDepth); } - - QString address = QString::fromStdString(EncodeDestination(outputAddress)); - Optional stakerAddr = IsValidDestination(outputAddressStaker) ? - Optional(QString::fromStdString(EncodeDestination(outputAddressStaker, CChainParams::STAKING_ADDRESS))) : - nullopt; - - ListCoinsKey key{address, wallet->IsChange(outputAddress), stakerAddr}; - ListCoinsValue value{ - out.tx->GetHash(), - out.i, - out.tx->tx->vout[out.i].nValue, - out.tx->GetTxTime(), - out.nDepth - }; - mapCoins[key].emplace_back(value); } } @@ -1130,12 +1111,7 @@ std::set WalletModel::listLockedCoins() void WalletModel::loadReceiveRequests(std::vector& vReceiveRequests) { - LOCK(wallet->cs_wallet); - for (auto it = wallet->NewAddressBookIterator(); it.IsValid(); it.Next()) { - for (const std::pair &item2 : it.GetValue().destdata) - if (item2.first.size() > 2 && item2.first.substr(0, 2) == "rr") // receive request - vReceiveRequests.push_back(item2.second); - } + vReceiveRequests = wallet->GetDestValues("rr"); // receive request } bool WalletModel::saveReceiveRequest(const std::string& sAddress, const int64_t nId, const std::string& sRequest) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index b5977b7ae2c09..2cdcec7575d20 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -323,6 +323,11 @@ class WalletModel : public QObject class ListCoinsValue { public: + ListCoinsValue() = delete; + ListCoinsValue(const uint256& _txhash, int _outIndex, CAmount _nValue, int64_t _nTime, int _nDepth) : + txhash(_txhash), outIndex(_outIndex), nValue(_nValue), nTime(_nTime), nDepth(_nDepth) + {} + uint256 txhash; int outIndex; CAmount nValue; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1f83430969b50..65464565990d8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2969,6 +2969,36 @@ bool CWallet::SelectCoinsToSpend(const std::vector& vAvailableCoins, co return res; } +std::map>, std::vector> CWallet::ListCoins() const +{ + std::map>, std::vector> result; + + CWallet::AvailableCoinsFilter filter; + filter.fIncludeLocked = true; + filter.fOnlySpendable = true; + std::vector availableCoins; + AvailableCoins(&availableCoins, nullptr, filter); + + for (const COutput& coin : availableCoins) { + const CScript& scriptPubKey = coin.tx->tx->vout[coin.i].scriptPubKey; + txnouttype type; std::vector addresses; int nRequired; + if (ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { + if (addresses.size() == 1) { + // P2PK, P2PKH scripts + const auto& addrpair = std::make_pair(addresses[0], nullopt); + result[addrpair].emplace_back(std::move(coin)); + } else if (type == TX_COLDSTAKE) { + // P2CS scripts + assert(addresses.size() == 2); + const auto& addrpair = std::make_pair(addresses[1], Optional(addresses[0])); + result[addrpair].emplace_back(std::move(coin)); + } + } + } + + return result; +} + bool CWallet::CreateBudgetFeeTX(CTransactionRef& tx, const uint256& hash, CReserveKey& keyChange, bool fFinalization) { CScript scriptChange; @@ -4047,6 +4077,20 @@ bool CWallet::LoadDestData(const CTxDestination& dest, const std::string& key, c return true; } +std::vector CWallet::GetDestValues(const std::string& prefix) const +{ + LOCK(cs_wallet); + std::vector values; + for (const auto& address : mapAddressBook) { + for (const auto& data : address.second.destdata) { + if (!data.first.compare(0, prefix.size(), prefix)) { + values.emplace_back(data.second); + } + } + } + return values; +} + void CWallet::AutoCombineDust(CConnman* connman) { { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 64e821ed1ec5d..80b5d0bc6244a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -810,6 +810,13 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map > AvailableCoinsByAddress(bool fConfirmed, CAmount maxCoinValue, bool fIncludeColdStaking); + /** + * Return list of available coins and locked coins grouped by non-change output address. + * PIVX: group coins by pair >. The optional destination + * is reserved for the staker address in case of P2CS. + */ + std::map>, std::vector> ListCoins() const; + /// Get 10000 PIV output and keys which can be used for the Masternode bool GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash, std::string strOutputIndex, std::string& strError); @@ -940,6 +947,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool EraseDestData(const CTxDestination& dest, const std::string& key); //! Adds a destination data tuple to the store, without saving it to disk bool LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value); + //! Get all destination values matching a prefix. + std::vector GetDestValues(const std::string& prefix) const; //! Adds a watch-only address to the store, and saves it to disk. bool AddWatchOnly(const CScript& dest) override; From e7cafaba3fe3b88936054ccd1418b8b0c5ecd326 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 13:11:13 +0200 Subject: [PATCH 12/50] [Refactoring] Mimic ListCoins for sapling notes Create CWallet::ListNotes and use it in WalletModel::listAvailableNotes --- src/qt/walletmodel.cpp | 23 ++++++++++------------- src/sapling/saplingscriptpubkeyman.cpp | 14 ++++++++++++++ src/sapling/saplingscriptpubkeyman.h | 2 ++ src/wallet/wallet.cpp | 5 +++++ src/wallet/wallet.h | 5 +++++ 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 232ed49cbd98d..ce681222beaef 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1044,19 +1044,16 @@ void WalletModel::listCoins(std::map>& void WalletModel::listAvailableNotes(std::map>& mapCoins) const { - std::vector notes; - Optional dummy = nullopt; - wallet->GetSaplingScriptPubKeyMan()->GetFilteredNotes(notes, dummy); - for (const auto& note : notes) { - ListCoinsKey key{QString::fromStdString(KeyIO::EncodePaymentAddress(note.address)), false, nullopt}; - ListCoinsValue value{ - note.op.hash, - (int)note.op.n, - (CAmount)note.note.value(), - 0, - note.confirmations - }; - mapCoins[key].emplace_back(value); + for (const auto& it: wallet->ListNotes()) { + const ListCoinsKey key{QString::fromStdString(KeyIO::EncodePaymentAddress(it.first)), false, nullopt}; + + for (const SaplingNoteEntry& note : it.second) { + mapCoins[key].emplace_back(note.op.hash, + (int)note.op.n, + (CAmount)note.note.value(), + 0, + note.confirmations); + } } } diff --git a/src/sapling/saplingscriptpubkeyman.cpp b/src/sapling/saplingscriptpubkeyman.cpp index 769cbd5f8bf63..0965c701de87f 100644 --- a/src/sapling/saplingscriptpubkeyman.cpp +++ b/src/sapling/saplingscriptpubkeyman.cpp @@ -511,6 +511,20 @@ void SaplingScriptPubKeyMan::GetFilteredNotes( } } +/* Return list of available notes grouped by sapling address. */ +std::map> SaplingScriptPubKeyMan::ListNotes() const +{ + std::vector notes; + Optional dummy = nullopt; + GetFilteredNotes(notes, dummy); + + std::map> result; + for (const auto& note : notes) { + result[note.address].emplace_back(std::move(note)); + } + return result; +} + Optional SaplingScriptPubKeyMan::GetAddressFromInputIfPossible(const uint256& txHash, int index) const { diff --git a/src/sapling/saplingscriptpubkeyman.h b/src/sapling/saplingscriptpubkeyman.h index 33d06f78ed2fc..600c8652a6ce6 100644 --- a/src/sapling/saplingscriptpubkeyman.h +++ b/src/sapling/saplingscriptpubkeyman.h @@ -297,6 +297,8 @@ class SaplingScriptPubKeyMan { bool requireSpendingKey=true, bool ignoreLocked=true) const; + /* Return list of available notes grouped by sapling address. */ + std::map> ListNotes() const; //! Return the address from where the shielded spend is taking the funds from (if possible) Optional GetAddressFromInputIfPossible(const CWalletTx* wtx, int index) const; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 65464565990d8..019bd3b66b8c5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2999,6 +2999,11 @@ std::map>, std::vector> CWallet::ListNotes() const +{ + return m_sspk_man->ListNotes(); +} + bool CWallet::CreateBudgetFeeTX(CTransactionRef& tx, const uint256& hash, CReserveKey& keyChange, bool fFinalization) { CScript scriptChange; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 80b5d0bc6244a..6ec22b45eeaa3 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -98,6 +98,7 @@ class CScheduler; class ScriptPubKeyMan; class SaplingScriptPubKeyMan; class SaplingNoteData; +struct SaplingNoteEntry; class CDeterministicMNList; /** (client) version numbers for particular wallet features */ @@ -816,6 +817,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * is reserved for the staker address in case of P2CS. */ std::map>, std::vector> ListCoins() const; + /** + * Return list of available shield notes grouped by sapling address. + */ + std::map> ListNotes() const; /// Get 10000 PIV output and keys which can be used for the Masternode bool GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, From 5c3d73f40dbab17dcc6ccc2bf680d00d09d1b8a3 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 1 Jun 2017 11:48:29 -0400 Subject: [PATCH 13/50] Avoid CWalletTx copies in GetAddressBalances and GetAddressGroupings --- src/wallet/wallet.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 019bd3b66b8c5..fe0f7dc43e66c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3732,8 +3732,8 @@ std::map CWallet::GetAddressBalances() { LOCK(cs_wallet); - for (std::pair walletEntry : mapWallet) { - CWalletTx* pcoin = &walletEntry.second; + for (const auto& walletEntry : mapWallet) { + const CWalletTx* pcoin = &walletEntry.second; if (!IsFinalTx(pcoin->tx, m_last_block_processed_height) || !pcoin->IsTrusted()) continue; @@ -3774,8 +3774,8 @@ std::set > CWallet::GetAddressGroupings() std::set > groupings; std::set grouping; - for (std::pair walletEntry : mapWallet) { - CWalletTx* pcoin = &walletEntry.second; + for (const auto& walletEntry : mapWallet) { + const CWalletTx* pcoin = &walletEntry.second; if (pcoin->tx->vin.size() > 0) { bool any_mine = false; From 41a7335c17ffe824ae42b6d76088e92e15891acb Mon Sep 17 00:00:00 2001 From: practicalswift Date: Sat, 3 Jun 2017 23:54:24 +0200 Subject: [PATCH 14/50] Remove unused variables --- src/wallet/db.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index b64004a45f913..2429a05070489 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -210,7 +210,6 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco if (recoverKVcallback) { CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); - std::string strType, strErr; if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue)) continue; } From 5bd1bd7536ab89a9d52e584d7620a31c34db79f8 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Thu, 13 Jul 2017 13:48:28 -0400 Subject: [PATCH 15/50] Properly forbid -salvagewallet and -zapwallettxes for multi wallet. --- src/wallet/wallet.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fe0f7dc43e66c..9e9adb00d5b46 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -658,12 +658,14 @@ bool CWallet::ParameterInteraction() 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 (gArgs.GetBoolArg("-salvagewallet", false)) { 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__); + if (gArgs.SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } } int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); @@ -673,11 +675,13 @@ bool CWallet::ParameterInteraction() } // -zapwallettxes implies a rescan - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-rescan", true)) { + if (zapwallettxes != 0) { 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 (gArgs.SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); + } } if (is_multiwallet) { From 7dd3916c4f6b063b25802c86364f1d3d9daf5a2f Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 17 Jul 2017 11:56:00 +0200 Subject: [PATCH 16/50] Register wallet endpoint --- src/httprpc.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 24db0d4554140..6c9d6f25ec43b 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -228,7 +228,10 @@ bool StartHTTPRPC() return false; RegisterHTTPHandler("/", true, HTTPReq_JSONRPC); - +#ifdef ENABLE_WALLET + // ifdef can be removed once we switch to better endpoint support and API versioning + RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC); +#endif assert(EventBase()); httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase()); RPCSetTimerInterface(httpRPCTimerInterface); From 6cb2b923a3dbc61f82ee27acc5c7223abf9adc5d Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 13 Jul 2017 17:06:27 +0200 Subject: [PATCH 17/50] Add wallet endpoint support to bitcoin-cli (-usewallet) --- src/httpserver.cpp | 16 ++++++++++++++-- src/httpserver.h | 11 +++++++---- src/pivx-cli.cpp | 21 +++++++++++++++++---- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index ed88665cfe93f..2dbf0b1ce5511 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2015 The Bitcoin Core developers -// Copyright (c) 2018-2020 The PIVX developers +// Copyright (c) 2015-2021 The Bitcoin Core developers +// Copyright (c) 2018-2021 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -679,3 +679,15 @@ void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) pathHandlers.erase(i); } } + +std::string urlDecode(const std::string &urlEncoded) { + std::string res; + if (!urlEncoded.empty()) { + char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, NULL); + if (decoded) { + res = std::string(decoded); + free(decoded); + } + } + return res; +} diff --git a/src/httpserver.h b/src/httpserver.h index ddbe04c142260..6fb3590348ffe 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -1,9 +1,10 @@ -// Copyright (c) 2015 The Bitcoin Core developers +// Copyright (c) 2015-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 BITCOIN_HTTPSERVER_H -#define BITCOIN_HTTPSERVER_H +#ifndef PIVX_HTTPSERVER_H +#define PIVX_HTTPSERVER_H #include #include @@ -148,4 +149,6 @@ class HTTPEvent struct event* ev; }; -#endif // BITCOIN_HTTPSERVER_H +std::string urlDecode(const std::string &urlEncoded); + +#endif // PIVX_HTTPSERVER_H diff --git a/src/pivx-cli.cpp b/src/pivx-cli.cpp index 3da747b0de4ed..cb7a7b821bbf0 100644 --- a/src/pivx-cli.cpp +++ b/src/pivx-cli.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin developers +// Copyright (c) 2009-2021 The Bitcoin developers // Copyright (c) 2009-2015 The Dash developers -// Copyright (c) 2015-2019 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. @@ -42,7 +42,8 @@ std::string HelpMessageCli() strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcclienttimeout=", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); + strUsage += HelpMessageOpt("-rpcclienttimeout=", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); + strUsage += HelpMessageOpt("-usewallet=", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in pivxd directory, required if pivxd/-Qt runs with multiple wallets)")); return strUsage; } @@ -190,7 +191,19 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) assert(output_buffer); evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); - int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/"); + // check if we should use a special wallet endpoint + std::string endpoint = "/"; + std::string walletName = gArgs.GetArg("-usewallet", ""); + if (!walletName.empty()) { + char* encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false); + if (encodedURI) { + endpoint = "/wallet/"+ std::string(encodedURI); + free(encodedURI); + } else { + throw CConnectionFailed("uri-encode failed"); + } + } + int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, endpoint.c_str()); if (r != 0) { evhttp_connection_free(evcon); event_base_free(base); From b0fe92f413c06e4b814a04325eb180401267aff7 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 14:30:16 +0200 Subject: [PATCH 18/50] Fix test_pivx circular dependency issue from bitcoin@32c9710c50b7e9255c81c7083cbfdfddc813b01f --- src/Makefile.test.include | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 58fc2b8b1b2d1..6123d0918fd64 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -176,13 +176,13 @@ test_test_pivx_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(SAPLING_TESTS) test_test_pivx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_FLAGS) test_test_pivx_LDADD = -test_test_pivx_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBBITCOIN_ZEROCOIN) \ - $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(LIBSAPLING) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) - if ENABLE_WALLET test_test_pivx_LDADD += $(LIBBITCOIN_WALLET) endif +test_test_pivx_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBBITCOIN_ZEROCOIN) \ + $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(LIBSAPLING) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) + test_test_pivx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_test_pivx_LDADD += $(LIBRUSTZCASH) $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH_LIBS) From 5683a9c9c5b4a62ad582910769cc079fb54961c4 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 14:37:42 +0200 Subject: [PATCH 19/50] Complete previous commit by moving mn stuff out of libbitcoin_wallet and into libbitcoin_common --- CMakeLists.txt | 28 ++++++++++++++-------------- src/Makefile.am | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ccff3d1251e8..ec1c400b7e006 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,24 +251,10 @@ if(ZMQ_FOUND) endif() set(WALLET_SOURCES - ./src/activemasternode.cpp ./src/bip38.cpp ./src/wallet/db.cpp ./src/addressbook.cpp ./src/crypter.cpp - ./src/budget/budgetdb.cpp - ./src/budget/budgetmanager.cpp - ./src/budget/budgetproposal.cpp - ./src/budget/budgetvote.cpp - ./src/budget/finalizedbudget.cpp - ./src/budget/finalizedbudgetvote.cpp - ./src/masternode.cpp - ./src/masternode-payments.cpp - ./src/masternode-sync.cpp - ./src/tiertwo_networksync.cpp - ./src/masternodeconfig.cpp - ./src/masternodeman.cpp - ./src/messagesigner.cpp ./src/zpiv/mintpool.cpp ./src/wallet/hdchain.cpp ./src/wallet/rpcdump.cpp @@ -353,8 +339,15 @@ add_library(ZEROCOIN_A STATIC ${ZEROCOIN_SOURCES}) target_include_directories(ZEROCOIN_A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) set(COMMON_SOURCES + ./src/activemasternode.cpp ./src/base58.cpp ./src/bip38.cpp + ./src/budget/budgetdb.cpp + ./src/budget/budgetmanager.cpp + ./src/budget/budgetproposal.cpp + ./src/budget/budgetvote.cpp + ./src/budget/finalizedbudget.cpp + ./src/budget/finalizedbudgetvote.cpp ./src/consensus/params.cpp ./src/consensus/upgrades.cpp ./src/chainparams.cpp @@ -377,6 +370,12 @@ set(COMMON_SOURCES ./src/invalid.cpp ./src/key.cpp ./src/keystore.cpp + ./src/masternode.cpp + ./src/masternode-payments.cpp + ./src/masternode-sync.cpp + ./src/masternodeconfig.cpp + ./src/masternodeman.cpp + ./src/messagesigner.cpp ./src/netaddress.cpp ./src/netbase.cpp ./src/policy/feerate.cpp @@ -391,6 +390,7 @@ set(COMMON_SOURCES ./src/script/script_error.cpp ./src/spork.cpp ./src/sporkdb.cpp + ./src/tiertwo_networksync.cpp ./src/warnings.cpp ) add_library(COMMON_A STATIC ${BitcoinHeaders} ${COMMON_SOURCES}) diff --git a/src/Makefile.am b/src/Makefile.am index ad88a3e7e537b..6bfeac8b6f67a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -387,25 +387,11 @@ endif libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ - activemasternode.cpp \ bip38.cpp \ interfaces/wallet.cpp \ addressbook.cpp \ crypter.cpp \ key_io.cpp \ - budget/budgetdb.cpp \ - budget/budgetmanager.cpp \ - budget/budgetproposal.cpp \ - budget/budgetvote.cpp \ - budget/finalizedbudget.cpp \ - budget/finalizedbudgetvote.cpp \ - masternode.cpp \ - masternode-payments.cpp \ - tiertwo_networksync.cpp \ - masternode-sync.cpp \ - masternodeconfig.cpp \ - masternodeman.cpp \ - messagesigner.cpp \ legacy/stakemodifier.cpp \ kernel.cpp \ wallet/db.cpp \ @@ -491,8 +477,15 @@ libzerocoin_libbitcoin_zerocoin_a_SOURCES = \ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ + activemasternode.cpp \ base58.cpp \ bip38.cpp \ + budget/budgetdb.cpp \ + budget/budgetmanager.cpp \ + budget/budgetproposal.cpp \ + budget/budgetvote.cpp \ + budget/finalizedbudget.cpp \ + budget/finalizedbudgetvote.cpp \ chainparams.cpp \ consensus/upgrades.cpp \ coins.cpp \ @@ -507,6 +500,12 @@ libbitcoin_common_a_SOURCES = \ invalid.cpp \ key.cpp \ keystore.cpp \ + masternode.cpp \ + masternode-payments.cpp \ + masternode-sync.cpp \ + masternodeconfig.cpp \ + masternodeman.cpp \ + messagesigner.cpp \ netaddress.cpp \ netbase.cpp \ policy/feerate.cpp \ @@ -518,6 +517,7 @@ libbitcoin_common_a_SOURCES = \ script/script.cpp \ script/sign.cpp \ script/standard.cpp \ + tiertwo_networksync.cpp \ warnings.cpp \ script/script_error.cpp \ spork.cpp \ From a64440b45cab313d87dcd7956a70c99aa5243a0b Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 17 Jul 2017 12:10:23 +0200 Subject: [PATCH 20/50] Select wallet based on the given endpoint --- src/wallet/rpcwallet.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 6dfb0c845ae46..94ab4d2f2716a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -14,6 +14,7 @@ #include "core_io.h" #include "destination_io.h" #include "init.h" +#include "httpserver.h" #include "key_io.h" #include "masternode-sync.h" #include "net.h" @@ -33,11 +34,21 @@ #include +static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; CWallet* GetWalletForJSONRPCRequest(const JSONRPCRequest& request) { - // TODO: Some way to access secondary wallets - return vpwallets.empty() ? nullptr : vpwallets[0]; + if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) { + // wallet endpoint was used + std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size())); + for (CWalletRef pwallet : ::vpwallets) { + if (pwallet->GetName() == requestedWallet) { + return pwallet; + } + } + throw JSONRPCError(RPC_INVALID_PARAMETER, "Requested wallet does not exist or is not loaded"); + } + return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet* const pwallet) From 2e02006f55407cfe507b65bad5670b99518e179f Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Wed, 19 Jul 2017 15:44:20 -0400 Subject: [PATCH 21/50] Rename -usewallet to -rpcwallet --- src/pivx-cli.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pivx-cli.cpp b/src/pivx-cli.cpp index cb7a7b821bbf0..7b2f0d117af05 100644 --- a/src/pivx-cli.cpp +++ b/src/pivx-cli.cpp @@ -43,7 +43,7 @@ std::string HelpMessageCli() strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcclienttimeout=", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); - strUsage += HelpMessageOpt("-usewallet=", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in pivxd directory, required if pivxd/-Qt runs with multiple wallets)")); + strUsage += HelpMessageOpt("-rpcwallet=", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in pivxd directory, required if pivxd/-Qt runs with multiple wallets)")); return strUsage; } @@ -193,7 +193,7 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) // check if we should use a special wallet endpoint std::string endpoint = "/"; - std::string walletName = gArgs.GetArg("-usewallet", ""); + std::string walletName = gArgs.GetArg("-rpcwallet", ""); if (!walletName.empty()) { char* encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false); if (encodedURI) { From f9141f82a6cfcacba601c3fe59bec9a5bf672733 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 28 Apr 2021 16:50:12 +0200 Subject: [PATCH 22/50] [QA] Add wallet_multiwallet.py functional test --- test/functional/wallet_multiwallet.py | 151 ++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100755 test/functional/wallet_multiwallet.py diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py new file mode 100755 index 0000000000000..574cd2202ace9 --- /dev/null +++ b/test/functional/wallet_multiwallet.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 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. +"""Test multiwallet. + +Verify that a pivxd node can load multiple wallet files +""" +import os +import shutil + +from test_framework.test_framework import PivxTestFramework +from test_framework.util import assert_equal, assert_raises_rpc_error + +class MultiWalletTest(PivxTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 2 + self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3', '-wallet=w'], []] + self.supports_cli = True + + def run_test(self): + node = self.nodes[0] + + data_dir = lambda *p: os.path.join(node.datadir, 'regtest', *p) + wallet_dir = lambda *p: data_dir('wallets', *p) + wallet = lambda name: node.get_wallet_rpc(name) + + # !TODO: implement listwallets (bitcoin#11310) + #assert_equal(set(node.listwallets()), {"w1", "w2", "w3", "w"}) + + self.stop_nodes() + + # !TODO: backport bitcoin#12220 + #self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist') + #self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir()) + #self.assert_start_raises_init_error(0, ['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir()) + + # should not initialize if there are duplicate wallets + # !TODO: backport bitcoin#10885 + #self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') + + # should not initialize if wallet file is a directory + # !TODO: backport bitcoin#11476 + bitcoin#11970 + # os.mkdir(wallet_dir('w11')) + # self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') + + # should not initialize if one wallet is a copy of another + # shutil.copyfile(wallet_dir('w2'), wallet_dir('w22')) + # self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') + + # should not initialize if wallet file is a symlink + # !TODO: backport bitcoin#10885 + # os.symlink(wallet_dir('w1'), wallet_dir('w12')) + # self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') + + self.log.info("Do not allow -zapwallettxes with multiwallet") + self.assert_start_raises_init_error(0, ['-zapwallettxes', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.assert_start_raises_init_error(0, ['-zapwallettxes=1', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.assert_start_raises_init_error(0, ['-zapwallettxes=2', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + + self.log.info("Do not allow -salvagewallet with multiwallet") + self.assert_start_raises_init_error(0, ['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") + self.assert_start_raises_init_error(0, ['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") + + self.log.info("Do not allow -upgradewallet with multiwallet") + self.assert_start_raises_init_error(0, ['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") + self.assert_start_raises_init_error(0, ['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") + + # !TODO: backport bitcoin#12220 + ''' + # if wallets/ doesn't exist, datadir should be the default wallet dir + wallet_dir2 = data_dir('walletdir') + os.rename(wallet_dir(), wallet_dir2) + self.start_node(0, ['-wallet=w4', '-wallet=w5']) + assert_equal(set(node.listwallets()), {"w4", "w5"}) + w5 = wallet("w5") + w5.generate(1) + + # now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded + os.rename(wallet_dir2, wallet_dir()) + self.restart_node(0, ['-wallet=w4', '-wallet=w5', '-walletdir=' + data_dir()]) + assert_equal(set(node.listwallets()), {"w4", "w5"}) + w5 = wallet("w5") + w5_info = w5.getwalletinfo() + assert_equal(w5_info['immature_balance'], 50) + + competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir') + os.mkdir(competing_wallet_dir) + self.restart_node(0, ['-walletdir='+competing_wallet_dir]) + self.assert_start_raises_init_error(1, ['-walletdir='+competing_wallet_dir], 'Error initializing wallet database environment') + ''' + self.restart_node(0, self.extra_args[0]) + + w1 = wallet("w1") + w2 = wallet("w2") + w3 = wallet("w3") + w4 = wallet("w") + wallet_bad = wallet("bad") + + w1.generate(1) + + # accessing invalid wallet fails + assert_raises_rpc_error(-8, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) + + # accessing wallet RPC without using wallet endpoint fails + # !TODO: backport bitcoin#11970 + #assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo) + + # !TODO: implement listwallets (bitcoin#11310) and backport bitcoin#11473 + # to un-comment wallet-name checks + + # check w1 wallet balance + w1_info = w1.getwalletinfo() + assert_equal(w1_info['immature_balance'], 250) + #w1_name = w1_info['walletname'] + #assert_equal(w1_name, "w1") + + # check w2 wallet balance + w2_info = w2.getwalletinfo() + assert_equal(w2_info['immature_balance'], 0) + #w2_name = w2_info['walletname'] + #assert_equal(w2_name, "w2") + + #w3_name = w3.getwalletinfo()['walletname'] + #assert_equal(w3_name, "w3") + + #w4_name = w4.getwalletinfo()['walletname'] + #assert_equal(w4_name, "w") + + w1.generate(101) + assert_equal(w1.getbalance(), 500) + assert_equal(w2.getbalance(), 0) + assert_equal(w3.getbalance(), 0) + assert_equal(w4.getbalance(), 0) + + w1.sendtoaddress(w2.getnewaddress(), 1) + w1.sendtoaddress(w3.getnewaddress(), 2) + w1.sendtoaddress(w4.getnewaddress(), 3) + w1.generate(1) + assert_equal(w2.getbalance(), 1) + assert_equal(w3.getbalance(), 2) + assert_equal(w4.getbalance(), 3) + + batch = w1.batch([w1.getblockchaininfo.get_request(), w1.getwalletinfo.get_request()]) + assert_equal(batch[0]["result"]["chain"], "regtest") + #assert_equal(batch[1]["result"]["walletname"], "w1") + +if __name__ == '__main__': + MultiWalletTest().main() \ No newline at end of file From 808fbc306b56b7d372addce412bc96270cc082c5 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 17:18:32 +0200 Subject: [PATCH 23/50] [Bugfix] consider boolean value of -zapwallettxes ParameterInteraction --- src/wallet/wallet.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9e9adb00d5b46..65993993b177b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -668,14 +668,14 @@ bool CWallet::ParameterInteraction() } } - int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); + bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false); // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { + if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) { LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); } // -zapwallettxes implies a rescan - if (zapwallettxes != 0) { + if (zapwallettxes) { if (is_multiwallet) { return UIError(strprintf(_("%s is only allowed with a single wallet file"), "-zapwallettxes")); } From 379255e824d662f8d41e2558819185c20cf77610 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 21:18:02 +0200 Subject: [PATCH 24/50] [Tests][Trivial] Add wallet_multiwallet.py to test_runner --- test/functional/test_runner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 712cb83b5ce1f..b90a8e4826087 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -70,6 +70,7 @@ 'wallet_import_rescan.py', # ~ 204 sec 'p2p_invalid_block.py', # ~ 213 sec 'feature_logging.py', # ~ 195 sec + 'wallet_multiwallet.py', # ~ 190 sec 'wallet_abandonconflict.py', # ~ 188 sec 'feature_blockindexstats.py', # ~ 167 sec 'wallet_importmulti.py', # ~ 157 sec @@ -223,6 +224,7 @@ 'sapling_mempool.py', 'wallet_importmulti.py', 'wallet_import_rescan.py', + 'wallet_multiwallet.py', ] # Place the lists with the longest tests (on average) first From 9fb29cc203aae2ec5d13649853c6cda032a9f09e Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 18:58:53 +0200 Subject: [PATCH 25/50] [Doc] Update multiwallet release notes --- doc/release-notes.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index 1ccd2a81131c9..9dee23c5a0d79 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -44,6 +44,7 @@ Notable Changes (Developers: add your notes here as part of your pull requests whenever possible) + 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)). @@ -60,13 +61,22 @@ Scripts with the old opcode are still accepted on the network (the restriction o 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. +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 +PIVX Core 6.0.0 contains the following changes to the RPC interface and pivx-cli for multi-wallet: + +* When running PIVX Core with a single wallet, there are **no** changes to the RPC interface or `pivx-cli`. All RPC calls and `pivx-cli` commands continue to work as before. +* When running PIVX Core with multi-wallet, all *node-level* RPC methods continue to work as before. HTTP RPC requests should be send to the normal `:` endpoint, and `pivx-cli` commands should be run as before. A *node-level* RPC method is any method which does not require access to the wallet. +* When running PIVX Core with multi-wallet, *wallet-level* RPC methods must specify the wallet for which they're intended in every request. HTTP RPC requests should be send to the `:/wallet/` endpoint, for example `127.0.0.1:8332/wallet/wallet1.dat`. `pivx-cli` commands should be run with a `-rpcwallet` option, for example `pivx-cli -rpcwallet=wallet1.dat getbalance`. + +* A new *node-level* `listwallets` RPC method is added to display which wallets are currently loaded. The names returned by this method are the same as those used in the HTTP endpoint and for the `rpcwallet` argument. + +Note that while multi-wallet is now fully supported, the RPC multi-wallet interface should be considered unstable for version 6.0.0, and there may backwards-incompatible changes in future versions. + GUI changes @@ -125,13 +135,18 @@ A new init option flag '-blocksdir' will allow one to keep the blockfiles extern Low-level RPC changes --------------------- -- The `listunspent` RPC now takes a `query_options` argument (see [PR 2317](https://github.com/PIVX-Project/PIVX/pull/2317)), which is a JSON object +- The `listunspent` RPC now takes a `query_options` argument (see [PR #2317](https://github.com/PIVX-Project/PIVX/pull/2317)), which is a JSON object containing one or more of the following members: - `minimumAmount` - a number specifying the minimum value of each UTXO - `maximumAmount` - a number specifying the maximum value of each UTXO - `maximumCount` - a number specifying the minimum number of UTXOs - `minimumSumAmount` - a number specifying the minimum sum value of all UTXOs +- The `listunspent` RPC also takes an additional boolean argument `include_unsafe` (true by default) to optionally exclude "unsafe" utxos. + An unconfirmed output from outside keys is considered unsafe (see [PR #2351](https://github.com/PIVX-Project/PIVX/pull/2351)). + +- The `listunspent` output also shows whether the utxo is considered safe to spend or not. + - the `stop` RPC no longer accepts the (already deprecated, ignored, and undocumented) optional boolean argument `detach`. From a1c56fd9d0c5790f40656775790474de0ee1f349 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 18:49:18 +0200 Subject: [PATCH 26/50] [Policy] Introduce -dustrelayfee >>> adapted from bitcoin@eb30d1a5b215c6dd3763d7f7948f2dd8cb61f6bf --- src/init.cpp | 10 ++++++++++ src/policy/policy.cpp | 24 +++++++++++++----------- src/policy/policy.h | 16 ++++++++++++---- src/qt/coincontroldialog.cpp | 8 ++++---- src/qt/guiutil.cpp | 2 +- src/sapling/sapling_operation.cpp | 4 ++-- src/sapling/transaction_builder.cpp | 4 ++-- src/wallet/wallet.cpp | 4 ++-- 8 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 91322bfe4b00c..f8437f2040e04 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -561,6 +561,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Fees (in %s/Kb) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(::minRelayTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-printtoconsole", strprintf(_("Send trace/debug info to console instead of debug.log file (default: %u)"), 0)); if (showDebug) { + strUsage += HelpMessageOpt("-dustrelayfee=", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); strUsage += HelpMessageOpt("-printpriority", strprintf(_("Log transaction priority and fee per kB when mining blocks (default: %u)"), DEFAULT_PRINTPRIORITY)); } strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); @@ -1146,6 +1147,15 @@ bool AppInitParameterInteraction() if (!chainparams.IsTestChain() && !fRequireStandard) return UIError(strprintf("%s is not currently supported for %s chain", "-acceptnonstdtxn", chainparams.NetworkIDString())); + // Feerate used to define dust. Shouldn't be changed lightly as old + // implementations may inadvertently create non-standard transactions + if (gArgs.IsArgSet("-dustrelayfee")) { + CAmount n = 0; + if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n) || 0 == n) + return UIError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); + dustRelayFee = CFeeRate(n); + } + #ifdef ENABLE_WALLET strWalletFile = gArgs.GetArg("-wallet", DEFAULT_WALLET_DAT); if (!CWallet::ParameterInteraction()) diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index deae2082bead1..9e6938977e8a2 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -16,7 +16,9 @@ bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; -CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee) +CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE); + +CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) { // "Dust" is defined in terms of dustRelayFee, // which has units satoshis-per-kilobyte. @@ -31,26 +33,26 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee) size_t nSize = GetSerializeSize(txout, SER_DISK, 0); nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above - return 3 * dustRelayFee.GetFee(nSize); + return 3 * dustRelayFeeIn.GetFee(nSize); } -CAmount GetDustThreshold(const CFeeRate& dustRelayFee) +CAmount GetDustThreshold(const CFeeRate& dustRelayFeeIn) { // return the dust threshold for a typical 34 bytes output - return 3 * dustRelayFee.GetFee(182); + return 3 * dustRelayFeeIn.GetFee(182); } -bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee) +bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) { - return (txout.nValue < GetDustThreshold(txout, dustRelayFee)); + return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn)); } -CAmount GetShieldedDustThreshold(const CFeeRate& dustRelayFee) +CAmount GetShieldedDustThreshold(const CFeeRate& dustRelayFeeIn) { unsigned int K = DEFAULT_SHIELDEDTXFEE_K; // Fixed (100) for now - return 3 * K * dustRelayFee.GetFee(SPENDDESCRIPTION_SIZE + - CTXOUT_REGULAR_SIZE + - BINDINGSIG_SIZE); + return 3 * K * dustRelayFeeIn.GetFee(SPENDDESCRIPTION_SIZE + + CTXOUT_REGULAR_SIZE + + BINDINGSIG_SIZE); } /** @@ -174,7 +176,7 @@ bool IsStandardTx(const CTransactionRef& tx, int nBlockHeight, std::string& reas else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { reason = "bare-multisig"; return false; - } else if (IsDust(txout, ::minRelayTxFee)) { + } else if (IsDust(txout, dustRelayFee)) { reason = "dust"; return false; } diff --git a/src/policy/policy.h b/src/policy/policy.h index 58ec491d5e65d..f9b509f17f5c5 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -29,6 +29,12 @@ static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; /** Default for -permitbaremultisig */ static const bool DEFAULT_PERMIT_BAREMULTISIG = true; extern bool fIsBareMultisigStd; +/** Min feerate for defining dust. Historically this has been the same as the + * minRelayTxFee, however changing the dust limit changes which transactions are + * standard and should be done with care and ideally rarely. It makes sense to + * only increase the dust limit after prior releases were already not creating + * outputs below the new threshold */ +static const unsigned int DUST_RELAY_TX_FEE = 10000; /** * Standard script verification flags that standard transactions will comply @@ -50,12 +56,12 @@ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCR BOOST_STATIC_ASSERT(DEFAULT_BLOCK_MAX_SIZE <= MAX_BLOCK_SIZE_CURRENT); BOOST_STATIC_ASSERT(DEFAULT_BLOCK_PRIORITY_SIZE <= DEFAULT_BLOCK_MAX_SIZE); -CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee); +CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn); -bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee); +bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn); -CAmount GetDustThreshold(const CFeeRate& dustRelayFee); -CAmount GetShieldedDustThreshold(const CFeeRate& dustRelayFee); +CAmount GetDustThreshold(const CFeeRate& dustRelayFeeIn); +CAmount GetShieldedDustThreshold(const CFeeRate& dustRelayFeeIn); bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); @@ -71,4 +77,6 @@ bool IsStandardTx(const CTransactionRef& tx, int nBlockHeight, std::string& reas */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); +extern CFeeRate dustRelayFee; + #endif // BITCOIN_POLICY_H diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index a36c811b86b66..726ae31788803 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -535,7 +535,7 @@ TotalAmounts CoinControlDialog::getTotals() const if (shieldedOut) nShieldOuts++; else nTransOuts++; if (a.first > 0 && !t.fDust) { - if (a.first < (shieldedOut ? GetShieldedDustThreshold(minRelayTxFee) : GetDustThreshold(minRelayTxFee))) + if (a.first < (shieldedOut ? GetShieldedDustThreshold(dustRelayFee) : GetDustThreshold(dustRelayFee))) t.fDust = true; } t.nBytes += (shieldedOut ? OUTPUTDESCRIPTION_SIZE @@ -566,8 +566,8 @@ TotalAmounts CoinControlDialog::getTotals() const t.nChange = t.nAmount - t.nPayFee - t.nPayAmount; // Never create dust outputs; if we would, just add the dust to the fee. - CAmount dustThreshold = fSelectTransparent ? GetDustThreshold(minRelayTxFee) : - GetShieldedDustThreshold(minRelayTxFee); + CAmount dustThreshold = fSelectTransparent ? GetDustThreshold(dustRelayFee) + : GetShieldedDustThreshold(dustRelayFee); if (t.nChange > 0 && t.nChange < dustThreshold) { t.nPayFee += t.nChange; t.nChange = 0; @@ -636,7 +636,7 @@ void CoinControlDialog::updateLabels() toolTip1 += tr("Can vary +/- 1 byte per input."); QString toolTip3 = tr("This label turns red, if recipient receives an amount smaller than %1 (transparent) / %2 (shield)." - ).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, GetDustThreshold(minRelayTxFee))).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, GetShieldedDustThreshold(minRelayTxFee))); + ).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, GetDustThreshold(dustRelayFee))).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, GetShieldedDustThreshold(dustRelayFee))); // how many satoshis the estimated fee can vary per byte we guess wrong double dFeeVary; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index ba4a73b4336ad..0401c77bfdf5c 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -250,7 +250,7 @@ bool isDust(const QString& address, const CAmount& amount) CTxDestination dest = DecodeDestination(address.toStdString()); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); - return IsDust(txOut, ::minRelayTxFee); + return IsDust(txOut, dustRelayFee); } QString HtmlEscape(const QString& str, bool fMultiLine) diff --git a/src/sapling/sapling_operation.cpp b/src/sapling/sapling_operation.cpp index 006e905584c61..9d2a43cbcdfb0 100644 --- a/src/sapling/sapling_operation.cpp +++ b/src/sapling/sapling_operation.cpp @@ -313,7 +313,7 @@ OperationResult SaplingOperation::loadUtxos(TxValues& txValues) // Final step, append utxo to the transaction // Get dust threshold - CAmount dustThreshold = GetDustThreshold(minRelayTxFee); + CAmount dustThreshold = GetDustThreshold(dustRelayFee); CAmount dustChange = -1; CAmount selectedUTXOAmount = 0; @@ -445,7 +445,7 @@ OperationResult SaplingOperation::loadUnspentNotes(TxValues& txValues, uint256& std::vector notes; std::vector spendingKeys; txValues.shieldedInTotal = 0; - CAmount dustThreshold = GetShieldedDustThreshold(minRelayTxFee); + CAmount dustThreshold = GetShieldedDustThreshold(dustRelayFee); CAmount dustChange = -1; for (const auto& t : shieldedInputs) { // Get the spending key for the address. diff --git a/src/sapling/transaction_builder.cpp b/src/sapling/transaction_builder.cpp index 80acb95475534..c2a5d0930d0c9 100644 --- a/src/sapling/transaction_builder.cpp +++ b/src/sapling/transaction_builder.cpp @@ -421,8 +421,8 @@ TransactionBuilderResult TransactionBuilder::Build(bool fDummySig) if (change > 0) { // If we get here and the change is dust, add it to the fee - CAmount dustThreshold = (spends.empty() && outputs.empty()) ? GetDustThreshold(minRelayTxFee) : - GetShieldedDustThreshold(minRelayTxFee); + CAmount dustThreshold = (spends.empty() && outputs.empty()) ? GetDustThreshold(dustRelayFee) + : GetShieldedDustThreshold(dustRelayFee); if (change > dustThreshold) { // Send change to the specified change address. If no change address // was set, send change to the first Sapling address given as input diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 65993993b177b..c6da2e890c1ad 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3133,7 +3133,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, // Fill outputs for (const CRecipient& rec : vecSend) { CTxOut txout(rec.nAmount, rec.scriptPubKey); - if (IsDust(txout, ::minRelayTxFee)) { + if (IsDust(txout, dustRelayFee)) { strFailReason = _("Transaction amount too small"); return false; } @@ -3198,7 +3198,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, // Never create dust outputs; if we would, just // add the dust to the fee. - if (IsDust(newTxOut, ::minRelayTxFee)) { + if (IsDust(newTxOut, dustRelayFee)) { nFeeRet += nChange; nChange = 0; reservekey.ReturnKey(); From cf4a90b5420c19fe03f8695e5c401e6f305df325 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 19:24:28 +0200 Subject: [PATCH 27/50] Remove factor of 3 from definition of dust. >>> backports bitcoin@b1385852ef2ba45fd6926d75497646debf79e686 --- src/init.cpp | 2 +- src/policy/policy.cpp | 12 ++++++------ src/policy/policy.h | 4 ++-- src/test/transaction_tests.cpp | 19 +++++++++++++++++-- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index f8437f2040e04..4788c25bdd017 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -561,7 +561,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Fees (in %s/Kb) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(::minRelayTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-printtoconsole", strprintf(_("Send trace/debug info to console instead of debug.log file (default: %u)"), 0)); if (showDebug) { - strUsage += HelpMessageOpt("-dustrelayfee=", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); + strUsage += HelpMessageOpt("-dustrelayfee=", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); strUsage += HelpMessageOpt("-printpriority", strprintf(_("Log transaction priority and fee per kB when mining blocks (default: %u)"), DEFAULT_PRINTPRIORITY)); } strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 9e6938977e8a2..c2dc818175eef 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -22,7 +22,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) { // "Dust" is defined in terms of dustRelayFee, // which has units satoshis-per-kilobyte. - // If you'd pay more than 1/3 in fees + // If you'd pay more in fees than the value of the output // to spend something, then we consider it dust. // A typical spendable txout is 34 bytes big, and will // need a CTxIn of at least 148 bytes to spend: @@ -33,13 +33,13 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) size_t nSize = GetSerializeSize(txout, SER_DISK, 0); nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above - return 3 * dustRelayFeeIn.GetFee(nSize); + return dustRelayFeeIn.GetFee(nSize); } CAmount GetDustThreshold(const CFeeRate& dustRelayFeeIn) { // return the dust threshold for a typical 34 bytes output - return 3 * dustRelayFeeIn.GetFee(182); + return dustRelayFeeIn.GetFee(182); } bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) @@ -50,9 +50,9 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) CAmount GetShieldedDustThreshold(const CFeeRate& dustRelayFeeIn) { unsigned int K = DEFAULT_SHIELDEDTXFEE_K; // Fixed (100) for now - return 3 * K * dustRelayFeeIn.GetFee(SPENDDESCRIPTION_SIZE + - CTXOUT_REGULAR_SIZE + - BINDINGSIG_SIZE); + return K * dustRelayFeeIn.GetFee(SPENDDESCRIPTION_SIZE + + CTXOUT_REGULAR_SIZE + + BINDINGSIG_SIZE); } /** diff --git a/src/policy/policy.h b/src/policy/policy.h index f9b509f17f5c5..46f25b2227bb5 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -29,12 +29,12 @@ static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; /** Default for -permitbaremultisig */ static const bool DEFAULT_PERMIT_BAREMULTISIG = true; extern bool fIsBareMultisigStd; -/** Min feerate for defining dust. Historically this has been the same as the +/** Min feerate for defining dust. Historically this has been based on the * minRelayTxFee, however changing the dust limit changes which transactions are * standard and should be done with care and ideally rarely. It makes sense to * only increase the dust limit after prior releases were already not creating * outputs below the new threshold */ -static const unsigned int DUST_RELAY_TX_FEE = 10000; +static const unsigned int DUST_RELAY_TX_FEE = 30000; /** * Standard script verification flags that standard transactions will comply diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index f38373e92eee8..6838c7ab63b31 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -425,11 +425,26 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) std::string reason; BOOST_CHECK(IsStandardTx(t, 0, reason)); - t.vout[0].nValue = 5011; // dust + // Check dust with default relay fee: + CAmount nDustThreshold = GetDustThreshold(dustRelayFee); + BOOST_CHECK_EQUAL(nDustThreshold, 5460); + // dust: + t.vout[0].nValue = nDustThreshold - 1; BOOST_CHECK(!IsStandardTx(t, 0, reason)); + // not dust: + t.vout[0].nValue = nDustThreshold; + BOOST_CHECK(IsStandardTx(t, 0, reason)); - t.vout[0].nValue = 6011; // not dust + // Check dust with odd relay fee to verify rounding: + // nDustThreshold = 182 * 3702 / 1000 + dustRelayFee = CFeeRate(3702); + // dust: + t.vout[0].nValue = 673 - 1; + BOOST_CHECK(!IsStandardTx(t, 0, reason)); + // not dust: + t.vout[0].nValue = 673; BOOST_CHECK(IsStandardTx(t, 0, reason)); + dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE); t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, 0, reason)); From fdf5da0683bcd380d579713860eb38dfb0889e98 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 15 Jun 2017 13:49:38 -0400 Subject: [PATCH 28/50] [wallet] fix comment for CWallet::Verify() --- src/wallet/wallet.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6ec22b45eeaa3..6af90c096541a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1161,7 +1161,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - //! Verify the wallet database and perform salvage if required + //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. + // This function will perform salvage on the wallet if requested, as long as only one wallet is + // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). static bool Verify(); /* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */ From 15252819d986a581bf768e893e721d1e7df7ad69 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 15 Jun 2017 11:45:08 -0400 Subject: [PATCH 29/50] [wallet] [rpc] print wallet name in getwalletinfo --- src/wallet/rpcwallet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 94ab4d2f2716a..94f3d2add7f5f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4010,6 +4010,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) "\nResult:\n" "{\n" + " \"walletname\": xxxxx, (string) the wallet name\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" " \"balance\": xxxxxxx, (numeric) the total PIV balance of the wallet (cold balance excluded)\n" " \"delegated_balance\": xxxxx, (numeric) the PIV balance held in P2CS (cold staking) contracts\n" @@ -4041,6 +4042,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); UniValue obj(UniValue::VOBJ); + obj.pushKV("walletname", pwallet->GetName()); obj.pushKV("walletversion", pwallet->GetVersion()); obj.pushKV("balance", ValueFromAmount(pwallet->GetAvailableBalance())); obj.pushKV("delegated_balance", ValueFromAmount(pwallet->GetDelegatedBalance())); From 4fd591304dafbbbdc68f81ac5e201440d4c0fcf9 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 27 Jun 2017 09:46:55 -0400 Subject: [PATCH 30/50] [wallet] [rpc] Add listwallets RPC This commit adds a listwallets RPC, which lists the names of the currently loaded wallets. This command intentionally shows no information about the wallet other then the name. Information on individual wallets can be obtained using the getwalletinfo RPC. --- src/wallet/rpcwallet.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 94f3d2add7f5f..e0862bff5b698 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4081,6 +4081,37 @@ UniValue getwalletinfo(const JSONRPCRequest& request) return obj; } +UniValue listwallets(const JSONRPCRequest& request) +{ + if (request.fHelp || !request.params.empty()) + throw std::runtime_error( + "listwallets\n" + "Returns a list of currently loaded wallets.\n" + "For full information on the wallet, use \"getwalletinfo\"\n" + "\nResult:\n" + "[ (json array of strings)\n" + " \"walletname\" (string) the wallet name\n" + " ...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("listwallets", "") + + HelpExampleRpc("listwallets", "") + ); + + UniValue obj(UniValue::VARR); + + for (CWalletRef pwallet : vpwallets) { + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + LOCK(pwallet->cs_wallet); + obj.push_back(pwallet->GetName()); + } + + return obj; +} + UniValue getstakingstatus(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -4515,6 +4546,7 @@ static const CRPCCommand commands[] = { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} }, { "wallet", "listtransactions", &listtransactions, false, {"dummy","count","from","include_watchonly","include_delegated","include_cold"} }, { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","watchonly_config","query_options","include_unsafe" } }, + { "wallet", "listwallets", &listwallets, true, {} }, { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, { "wallet", "rawdelegatestake", &rawdelegatestake, false, {"staking_addr","amount","owner_addr","ext_owner","include_delegated","from_shield","force"} }, { "wallet", "sendmany", &sendmany, false, {"dummy","amounts","minconf","comment","include_delegated"} }, From 3955ee908a33643bd9122d5403522e3047351962 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 23:00:32 +0200 Subject: [PATCH 31/50] [Doc] Update release notes --- doc/release-notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/release-notes.md b/doc/release-notes.md index 9dee23c5a0d79..fa996c5f363a0 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -75,6 +75,8 @@ PIVX Core 6.0.0 contains the following changes to the RPC interface and pivx-cli * A new *node-level* `listwallets` RPC method is added to display which wallets are currently loaded. The names returned by this method are the same as those used in the HTTP endpoint and for the `rpcwallet` argument. +The `getwalletinfo` RPC method returns the name of the wallet used for the query. + Note that while multi-wallet is now fully supported, the RPC multi-wallet interface should be considered unstable for version 6.0.0, and there may backwards-incompatible changes in future versions. From 37089d183900da71cb9a82ac60b2f20947386f0e Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 21:15:14 +0200 Subject: [PATCH 32/50] [tests] Update wallet_multiwallet.py functional test --- test/functional/wallet_multiwallet.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 574cd2202ace9..39c37cf18ba1b 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -27,8 +27,7 @@ def run_test(self): wallet_dir = lambda *p: data_dir('wallets', *p) wallet = lambda name: node.get_wallet_rpc(name) - # !TODO: implement listwallets (bitcoin#11310) - #assert_equal(set(node.listwallets()), {"w1", "w2", "w3", "w"}) + assert_equal(set(node.listwallets()), {"w1", "w2", "w3", "w"}) self.stop_nodes() @@ -105,29 +104,30 @@ def run_test(self): assert_raises_rpc_error(-8, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) # accessing wallet RPC without using wallet endpoint fails + assert_raises_rpc_error(-32601, "Method not found", self.nodes[0].getwalletinfo) # !TODO: backport bitcoin#11970 #assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo) - # !TODO: implement listwallets (bitcoin#11310) and backport bitcoin#11473 + # !TODO: backport bitcoin#11473 # to un-comment wallet-name checks # check w1 wallet balance w1_info = w1.getwalletinfo() assert_equal(w1_info['immature_balance'], 250) - #w1_name = w1_info['walletname'] - #assert_equal(w1_name, "w1") + w1_name = w1_info['walletname'] + assert_equal(w1_name, "w1") # check w2 wallet balance w2_info = w2.getwalletinfo() assert_equal(w2_info['immature_balance'], 0) - #w2_name = w2_info['walletname'] - #assert_equal(w2_name, "w2") + w2_name = w2_info['walletname'] + assert_equal(w2_name, "w2") - #w3_name = w3.getwalletinfo()['walletname'] - #assert_equal(w3_name, "w3") + w3_name = w3.getwalletinfo()['walletname'] + assert_equal(w3_name, "w3") - #w4_name = w4.getwalletinfo()['walletname'] - #assert_equal(w4_name, "w") + w4_name = w4.getwalletinfo()['walletname'] + assert_equal(w4_name, "w") w1.generate(101) assert_equal(w1.getbalance(), 500) From ce35e1e288e7b09550b2f3fbdb2985842e70949b Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 21:32:57 +0200 Subject: [PATCH 33/50] [Qt] Use wallet 0 in rpc console if running with multiple wallets >>> backports bitcoin@97375727b8f3b7d26c7c813630a6139005b5c5c9 --- src/qt/rpcexecutor.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/qt/rpcexecutor.cpp b/src/qt/rpcexecutor.cpp index 305e9bd16ce12..70409b2652b34 100644 --- a/src/qt/rpcexecutor.cpp +++ b/src/qt/rpcexecutor.cpp @@ -7,6 +7,12 @@ #include "rpc/client.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif + +#include + #include QString RPCExecutor::categoryClass(int category) @@ -218,6 +224,14 @@ bool RPCExecutor::ExecuteCommandLine(std::string& strResult, const std::string& JSONRPCRequest req; req.params = RPCConvertValues(stack.back()[0], std::vector(stack.back().begin() + 1, stack.back().end())); req.strMethod = stack.back()[0]; +#ifdef ENABLE_WALLET + // TODO: Move this logic to WalletModel + if (!vpwallets.empty()) { + // in Qt, use always the wallet with index 0 when running with multiple wallets + QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(vpwallets[0]->GetName())); + req.URI = "/wallet/"+std::string(encodedName.constData(), encodedName.length()); + } +#endif lastResult = tableRPC.execute(req); state = STATE_COMMAND_EXECUTED; curarg.clear(); From ee52c2e85bfb6c74eb65c6240e55fda1cd0d8e69 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Jul 2017 05:42:30 -0400 Subject: [PATCH 34/50] Fix misleading "Method not found" multiwallet errors Raise RPC_WALLET_NOT_SPECIFIED instead of RPC_METHOD_NOT_FOUND when a required wallet filename was not specified in an RPC call. Also raise more specific RPC_WALLET_NOT_FOUND error instead of RPC_INVALID_PARAMETER in case an invalid wallet was specified, for consistency. --- src/pivx-cli.cpp | 12 ++++++++++++ src/rpc/protocol.h | 2 ++ src/wallet/rpcwallet.cpp | 15 ++++++++------- test/functional/wallet_multiwallet.py | 9 ++------- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/pivx-cli.cpp b/src/pivx-cli.cpp index 7b2f0d117af05..ce15f3a8629db 100644 --- a/src/pivx-cli.cpp +++ b/src/pivx-cli.cpp @@ -276,6 +276,18 @@ int CommandLineRPC(int argc, char* argv[]) throw CConnectionFailed("server in warmup"); strPrint = "error: " + error.write(); nRet = abs(code); + if (error.isObject()) { + UniValue errCode = find_value(error, "code"); + UniValue errMsg = find_value(error, "message"); + strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n"; + + if (errMsg.isStr()) + strPrint += "error message:\n"+errMsg.get_str(); + + if (errCode.isNum() && errCode.get_int() == RPC_WALLET_NOT_SPECIFIED) { + strPrint += "\nTry adding \"-rpcwallet=\" option to pivx-cli command line."; + } + } } else { // Result if (result.isNull()) diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 84cedade73471..11c6c411aae45 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -76,6 +76,8 @@ enum RPCErrorCode { RPC_WALLET_WRONG_ENC_STATE = -15, //! Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) RPC_WALLET_ENCRYPTION_FAILED = -16, //! Failed to encrypt the wallet RPC_WALLET_ALREADY_UNLOCKED = -17, //! Wallet is already unlocked + RPC_WALLET_NOT_FOUND = -18, //!< Invalid wallet specified + RPC_WALLET_NOT_SPECIFIED = -19, //!< No wallet specified (error when there are multiple wallets loaded) }; UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e0862bff5b698..b1a2ecf7f9ac1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -46,7 +46,7 @@ CWallet* GetWalletForJSONRPCRequest(const JSONRPCRequest& request) return pwallet; } } - throw JSONRPCError(RPC_INVALID_PARAMETER, "Requested wallet does not exist or is not loaded"); + throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); } return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr; } @@ -58,13 +58,14 @@ std::string HelpRequiringPassphrase(CWallet* const pwallet) bool EnsureWalletIsAvailable(CWallet* const pwallet, bool avoidException) { - if (!pwallet) { - if (!avoidException) - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet disabled)"); - else - return false; + if (pwallet) return true; + if (avoidException) return false; + if (::vpwallets.empty()) { + // Wallet RPC methods are disabled if no wallets are loaded. + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); } - return true; + throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED, + "Wallet file not specified (must request wallet RPC through /wallet/ uri-path)."); } void EnsureWalletIsUnlocked(CWallet* const pwallet, bool fAllowAnonOnly) diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 39c37cf18ba1b..317789f5cc1bf 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -101,15 +101,10 @@ def run_test(self): w1.generate(1) # accessing invalid wallet fails - assert_raises_rpc_error(-8, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) + assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) # accessing wallet RPC without using wallet endpoint fails - assert_raises_rpc_error(-32601, "Method not found", self.nodes[0].getwalletinfo) - # !TODO: backport bitcoin#11970 - #assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo) - - # !TODO: backport bitcoin#11473 - # to un-comment wallet-name checks + assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo) # check w1 wallet balance w1_info = w1.getwalletinfo() From a1f4e2a267e83354c5210bc04dc962b22c8b1bbd Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 22:38:18 +0200 Subject: [PATCH 35/50] Reject duplicate wallet filenames >>> backports bitcoin@3ef77a0c1288df524fdf0c90ca70c986f473b787 --- src/wallet/wallet.cpp | 12 +++++++++++- test/functional/wallet_multiwallet.py | 3 +-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c6da2e890c1ad..57273ebbfeda5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2121,13 +2121,23 @@ bool CWallet::Verify() uiInterface.InitMessage(_("Verifying wallet(s)...")); + // Keep track of each wallet absolute path to detect duplicates. + std::set wallet_paths; + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { if (fs::path(walletFile).filename() != walletFile) { return UIError(strprintf(_("%s parameter must only specify a filename (not a path)"), "-wallet")); - } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + } + if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { return UIError(strprintf(_("Invalid characters in %s filename"), "-wallet")); } + fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); + + if (!wallet_paths.insert(wallet_path).second) { + return UIError(strprintf(_("Duplicate %s filename"), "-wallet")); + } + std::string strError; if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { return UIError(strError); diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 317789f5cc1bf..75c751fe64396 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -37,8 +37,7 @@ def run_test(self): #self.assert_start_raises_init_error(0, ['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir()) # should not initialize if there are duplicate wallets - # !TODO: backport bitcoin#10885 - #self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') + self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Duplicate -wallet filename') # should not initialize if wallet file is a directory # !TODO: backport bitcoin#11476 + bitcoin#11970 From 900bbfabfcf22299295d58fbaffa6b91f48b3e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Thu, 27 Jul 2017 00:57:02 +0100 Subject: [PATCH 36/50] Reject invalid wallet files --- src/wallet/wallet.cpp | 4 ++++ test/functional/wallet_multiwallet.py | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 57273ebbfeda5..8d97fec8b41d1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2134,6 +2134,10 @@ bool CWallet::Verify() fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); + if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { + return UIError(_("Invalid -wallet file")); + } + if (!wallet_paths.insert(wallet_path).second) { return UIError(strprintf(_("Duplicate %s filename"), "-wallet")); } diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 75c751fe64396..408d383166202 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -41,15 +41,19 @@ def run_test(self): # should not initialize if wallet file is a directory # !TODO: backport bitcoin#11476 + bitcoin#11970 - # os.mkdir(wallet_dir('w11')) - # self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') + os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11')) + self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Invalid -wallet file') + #os.mkdir(wallet_dir('w11')) + #self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') # should not initialize if one wallet is a copy of another # shutil.copyfile(wallet_dir('w2'), wallet_dir('w22')) # self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') # should not initialize if wallet file is a symlink - # !TODO: backport bitcoin#10885 + os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), + os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12')) + self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Invalid -wallet file') # os.symlink(wallet_dir('w1'), wallet_dir('w12')) # self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') From 8bd979f4d182c74c6c550b54f5a68c21a83dfb62 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 23:14:00 +0200 Subject: [PATCH 37/50] [wallet] Specify wallet name in wallet loading errors >>> backports bitcoin@d84e78ec393049cb067170a4905a1679ff794368 --- src/wallet/wallet.cpp | 8 ++++---- test/functional/wallet_multiwallet.py | 16 +++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 8d97fec8b41d1..0370b1b0eb36b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2126,20 +2126,20 @@ bool CWallet::Verify() for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { if (fs::path(walletFile).filename() != walletFile) { - return UIError(strprintf(_("%s parameter must only specify a filename (not a path)"), "-wallet")); + return UIError(strprintf(_("Error loading wallet %s. %s parameter must only specify a filename (not a path)."), walletFile, "-wallet")); } if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { - return UIError(strprintf(_("Invalid characters in %s filename"), "-wallet")); + return UIError(strprintf(_("Error loading wallet %s. Invalid characters in %s filename."), walletFile, "-wallet")); } fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { - return UIError(_("Invalid -wallet file")); + return UIError(strprintf(_("Error loading wallet %s. %s filename must be a regular file."), walletFile, "-wallet")); } if (!wallet_paths.insert(wallet_path).second) { - return UIError(strprintf(_("Duplicate %s filename"), "-wallet")); + return UIError(strprintf(_("Error loading wallet %s. Duplicate %s filename specified."), walletFile, "-wallet")); } std::string strError; diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 408d383166202..42e9c9c5c48e5 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -37,25 +37,23 @@ def run_test(self): #self.assert_start_raises_init_error(0, ['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir()) # should not initialize if there are duplicate wallets - self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Duplicate -wallet filename') + self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') # should not initialize if wallet file is a directory - # !TODO: backport bitcoin#11476 + bitcoin#11970 os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11')) - self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Invalid -wallet file') #os.mkdir(wallet_dir('w11')) - #self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') + self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') - # should not initialize if one wallet is a copy of another - # shutil.copyfile(wallet_dir('w2'), wallet_dir('w22')) - # self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') + # !TODO: backport bitcoin#11476 + bitcoin#11970 + #should not initialize if one wallet is a copy of another + #shutil.copyfile(wallet_dir('w2'), wallet_dir('w22')) + #self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') # should not initialize if wallet file is a symlink os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12')) - self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Invalid -wallet file') # os.symlink(wallet_dir('w1'), wallet_dir('w12')) - # self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') + self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') self.log.info("Do not allow -zapwallettxes with multiwallet") self.assert_start_raises_init_error(0, ['-zapwallettxes', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") From f49acf7337517971ffba750ebd91d73a7f1f9cdd Mon Sep 17 00:00:00 2001 From: random-zebra Date: Wed, 28 Apr 2021 23:36:26 +0200 Subject: [PATCH 38/50] [wallet] [moveonly] Move CAffectedKeysVisitor >>> backports bitcoin@cab8557e3504c4b93796a7e196b288ffd061b9b4 --- src/wallet/wallet.cpp | 94 +++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0370b1b0eb36b..32400a96a81d6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -72,6 +72,53 @@ std::string COutput::ToString() const return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); } +class CAffectedKeysVisitor : public boost::static_visitor +{ +private: + const CKeyStore& keystore; + std::vector& vKeys; + +public: + CAffectedKeysVisitor(const CKeyStore& keystoreIn, std::vector& vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} + + void Process(const CScript& script) + { + txnouttype type; + std::vector vDest; + int nRequired; + if (ExtractDestinations(script, type, vDest, nRequired)) { + for (const CTxDestination& dest : vDest) + boost::apply_visitor(*this, dest); + } + } + + void operator()(const CKeyID& keyId) + { + if (keystore.HaveKey(keyId)) + vKeys.push_back(keyId); + } + + void operator()(const CScriptID& scriptId) + { + CScript script; + if (keystore.GetCScript(scriptId, script)) + Process(script); + } + + void operator()(const CNoDestination& none) {} +}; + +std::vector CWallet::GetAffectedKeys(const CScript& spk) +{ + std::vector ret; + std::vector vAffected; + CAffectedKeysVisitor(*this, vAffected).Process(spk); + for (const CKeyID& keyid : vAffected) { + ret.emplace_back(keyid); + } + return ret; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3977,53 +4024,6 @@ bool CWallet::SetStakeSplitThreshold(const CAmount sst) /** @} */ // end of Actions -class CAffectedKeysVisitor : public boost::static_visitor -{ -private: - const CKeyStore& keystore; - std::vector& vKeys; - -public: - CAffectedKeysVisitor(const CKeyStore& keystoreIn, std::vector& vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} - - void Process(const CScript& script) - { - txnouttype type; - std::vector vDest; - int nRequired; - if (ExtractDestinations(script, type, vDest, nRequired)) { - for (const CTxDestination& dest : vDest) - boost::apply_visitor(*this, dest); - } - } - - void operator()(const CKeyID& keyId) - { - if (keystore.HaveKey(keyId)) - vKeys.push_back(keyId); - } - - void operator()(const CScriptID& scriptId) - { - CScript script; - if (keystore.GetCScript(scriptId, script)) - Process(script); - } - - void operator()(const CNoDestination& none) {} -}; - -std::vector CWallet::GetAffectedKeys(const CScript& spk) -{ - std::vector ret; - std::vector vAffected; - CAffectedKeysVisitor(*this, vAffected).Process(spk); - for (const CKeyID& keyid : vAffected) { - ret.emplace_back(keyid); - } - return ret; -} - void CWallet::GetKeyBirthTimes(std::map& mapKeyBirth) const { From 2188c3ed6cdae23842a68df681f42653c4466cad Mon Sep 17 00:00:00 2001 From: random-zebra Date: Thu, 29 Apr 2021 00:07:52 +0200 Subject: [PATCH 39/50] Move some static functions out of wallet.h/cpp >>> backports bitcoin@d97fe2016cc7739929aac5c44de5037163b17ad0 This commit just moves a few function declarations and updates callers. Function bodies are moved in two followup MOVEONLY commits. This change is desirable because wallet.h/cpp are monolithic and hard to navigate, so pulling things out and grouping together pieces of related functionality should improve the organization. Another proximate motivation is the wallet process separation work in parameter parsing and fee estimation are still done in the main process rather than the wallet process, and having functions that run in different processes scrambled up throughout wallet.cpp is unnecessarily confusing. --- CMakeLists.txt | 2 ++ src/Makefile.am | 4 ++++ src/guiinterfaceutil.h | 2 ++ src/init.cpp | 12 ++++++------ src/qt/coincontroldialog.cpp | 7 ++++--- src/qt/pivx/sendcustomfeedialog.cpp | 11 ++++++----- src/wallet/fees.cpp | 13 +++++++++++++ src/wallet/fees.h | 27 +++++++++++++++++++++++++++ src/wallet/init.cpp | 14 ++++++++++++++ src/wallet/init.h | 26 ++++++++++++++++++++++++++ src/wallet/rpcwallet.cpp | 3 ++- src/wallet/wallet.cpp | 23 +++++++++++++++-------- src/wallet/wallet.h | 22 ---------------------- 13 files changed, 121 insertions(+), 45 deletions(-) create mode 100644 src/wallet/fees.cpp create mode 100644 src/wallet/fees.h create mode 100644 src/wallet/init.cpp create mode 100644 src/wallet/init.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ec1c400b7e006..0e49af237798a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,6 +259,8 @@ set(WALLET_SOURCES ./src/wallet/hdchain.cpp ./src/wallet/rpcdump.cpp ./src/zpiv/zerocoin.cpp + ./src/wallet/fees.cpp + ./src/wallet/init.cpp ./src/wallet/scriptpubkeyman.cpp ./src/wallet/rpcwallet.cpp ./src/kernel.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 6bfeac8b6f67a..744b4f54b56e5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -297,6 +297,8 @@ BITCOIN_CORE_H = \ wallet/rpcwallet.h \ wallet/scriptpubkeyman.h \ destination_io.h \ + wallet/fees.h \ + wallet/init.h \ wallet/wallet.h \ wallet/walletdb.h \ warnings.h \ @@ -395,6 +397,8 @@ libbitcoin_wallet_a_SOURCES = \ legacy/stakemodifier.cpp \ kernel.cpp \ wallet/db.cpp \ + wallet/fees.cpp \ + wallet/init.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/hdchain.cpp \ diff --git a/src/guiinterfaceutil.h b/src/guiinterfaceutil.h index 9e8c779269f84..2cd03176edc17 100644 --- a/src/guiinterfaceutil.h +++ b/src/guiinterfaceutil.h @@ -6,6 +6,8 @@ #define GUIINTERFACEUTIL_H #include "guiinterface.h" +#include "tinyformat.h" +#include "util/system.h" inline static bool UIError(const std::string &str) { diff --git a/src/init.cpp b/src/init.cpp index 4788c25bdd017..47dd50e99bf26 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -57,13 +57,13 @@ #include "validation.h" #include "validationinterface.h" #include "zpivchain.h" +#include "warnings.h" #ifdef ENABLE_WALLET +#include "wallet/init.h" #include "wallet/wallet.h" #include "wallet/rpcwallet.h" - #endif -#include "warnings.h" #include #include @@ -507,7 +507,7 @@ std::string HelpMessage(HelpMessageMode mode) " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); #if ENABLE_WALLET - strUsage += CWallet::GetWalletHelpString(showDebug); + strUsage += GetWalletHelpString(showDebug); #endif if (mode == HMM_BITCOIN_QT) { @@ -1158,7 +1158,7 @@ bool AppInitParameterInteraction() #ifdef ENABLE_WALLET strWalletFile = gArgs.GetArg("-wallet", DEFAULT_WALLET_DAT); - if (!CWallet::ParameterInteraction()) + if (!WalletParameterInteraction()) return false; #endif // ENABLE_WALLET @@ -1332,7 +1332,7 @@ bool AppInitMain() // ********************************************************* Step 5: Verify wallet database integrity #ifdef ENABLE_WALLET - if (!CWallet::Verify()) { + if (!WalletVerify()) { return false; } #endif @@ -1721,7 +1721,7 @@ bool AppInitMain() // ********************************************************* Step 8: Backup and Load wallet #ifdef ENABLE_WALLET - if (!CWallet::InitLoadWallet()) + if (!InitLoadWallet()) return false; #else LogPrintf("No wallet compiled in!\n"); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 726ae31788803..a362df2b387a9 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -15,6 +15,7 @@ #include "optionsmodel.h" #include "policy/policy.h" #include "txmempool.h" +#include "wallet/fees.h" #include "wallet/wallet.h" #include "walletmodel.h" @@ -632,7 +633,7 @@ void CoinControlDialog::updateLabels() // tool tips QString toolTip1 = tr("This label turns red, if the transaction size is greater than 1000 bytes.") + "

"; - toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::GetRequiredFee(1000))) + "

"; + toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, GetRequiredFee(1000))) + "

"; toolTip1 += tr("Can vary +/- 1 byte per input."); QString toolTip3 = tr("This label turns red, if recipient receives an amount smaller than %1 (transparent) / %2 (shield)." @@ -641,9 +642,9 @@ void CoinControlDialog::updateLabels() // how many satoshis the estimated fee can vary per byte we guess wrong double dFeeVary; if (payTxFee.GetFeePerK() > 0) - dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000; + dFeeVary = (double)std::max(GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000; else - dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / 1000; + dFeeVary = (double)std::max(GetRequiredFee(1000), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / 1000; QString toolTip4 = tr("Can vary +/- %1 u%2 per input.").arg(dFeeVary).arg(CURRENCY_UNIT.c_str()); ui->labelCoinControlFee->setToolTip(toolTip4); diff --git a/src/qt/pivx/sendcustomfeedialog.cpp b/src/qt/pivx/sendcustomfeedialog.cpp index 9712b8d224dfa..f7a139333c5e6 100644 --- a/src/qt/pivx/sendcustomfeedialog.cpp +++ b/src/qt/pivx/sendcustomfeedialog.cpp @@ -5,9 +5,10 @@ #include "qt/pivx/sendcustomfeedialog.h" #include "qt/pivx/forms/ui_sendcustomfeedialog.h" #include "qt/pivx/qtutils.h" -#include "walletmodel.h" +#include "qt/walletmodel.h" #include "optionsmodel.h" #include "guiutil.h" +#include "wallet/fees.h" #include #include @@ -123,19 +124,19 @@ void SendCustomFeeDialog::accept() // Check insane fee const CAmount insaneFee = ::minRelayTxFee.GetFeePerK() * 10000; if (customFee >= insaneFee) { - ui->lineEditCustomFee->setText(BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), insaneFee - CWallet::GetRequiredFee(1000))); + ui->lineEditCustomFee->setText(BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), insaneFee - GetRequiredFee(1000))); inform(tr("Fee too high. Must be below: %1").arg( BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), insaneFee))); - } else if (customFee < CWallet::GetRequiredFee(1000)) { + } else if (customFee < GetRequiredFee(1000)) { CAmount nFee = 0; if (walletModel->hasWalletCustomFee()) { walletModel->getWalletCustomFee(nFee); } else { - nFee = CWallet::GetRequiredFee(1000); + nFee = GetRequiredFee(1000); } ui->lineEditCustomFee->setText(BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), nFee)); inform(tr("Fee too low. Must be at least: %1").arg( - BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), CWallet::GetRequiredFee(1000)))); + BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), GetRequiredFee(1000)))); } else { walletModel->setWalletCustomFee(fUseCustomFee, customFee); QDialog::accept(); diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp new file mode 100644 index 0000000000000..fb851c36520ab --- /dev/null +++ b/src/wallet/fees.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 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. + +#include "wallet/fees.h" + +#include "policy/policy.h" +#include "txmempool.h" +#include "util/system.h" +#include "validation.h" +#include "wallet/wallet.h" diff --git a/src/wallet/fees.h b/src/wallet/fees.h new file mode 100644 index 0000000000000..144a15b3f0704 --- /dev/null +++ b/src/wallet/fees.h @@ -0,0 +1,27 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 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_FEES_H +#define PIVX_WALLET_FEES_H + +#include "amount.h" + +class CTxMemPool; + +/** + * Return the minimum required fee taking into account the + * floating relay fee and user set minimum transaction fee + */ +CAmount GetRequiredFee(unsigned int nTxBytes); + +/** + * Estimate the minimum fee considering user set parameters + * and the required fee + */ +CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool); + + +#endif // PIVX_WALLET_FEES_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp new file mode 100644 index 0000000000000..626da1e86a0e8 --- /dev/null +++ b/src/wallet/init.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 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. + +#include "wallet/init.h" + +#include "guiinterfaceutil.h" +#include "net.h" +#include "util/system.h" +#include "utilmoneystr.h" +#include "validation.h" +#include "wallet/wallet.h" diff --git a/src/wallet/init.h b/src/wallet/init.h new file mode 100644 index 0000000000000..f63844b99da26 --- /dev/null +++ b/src/wallet/init.h @@ -0,0 +1,26 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 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_INIT_H +#define PIVX_WALLET_INIT_H + +#include + +//! Return the wallets help message. +std::string GetWalletHelpString(bool showDebug); + +//! Wallets parameter interaction +bool WalletParameterInteraction(); + +//! Responsible for reading and validating the -wallet arguments and verifying the wallet database. +// This function will perform salvage on the wallet if requested, as long as only one wallet is +// being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). +bool WalletVerify(); + +//! Load wallet databases. +bool InitLoadWallet(); + +#endif // PIVX_WALLET_INIT_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b1a2ecf7f9ac1..47ae55b568cc1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -13,7 +13,6 @@ #include "coincontrol.h" #include "core_io.h" #include "destination_io.h" -#include "init.h" #include "httpserver.h" #include "key_io.h" #include "masternode-sync.h" @@ -30,6 +29,8 @@ #include "wallet/walletdb.h" #include "zpivchain.h" +#include // for StartShutdown + #include #include diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 32400a96a81d6..37eac7d5a8346 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -9,8 +9,11 @@ #include "budget/budgetmanager.h" #include "coincontrol.h" +<<<<<<< Upstream, based on master #include "evo/deterministicmns.h" #include "init.h" +======= +>>>>>>> 834e2bb Move some static functions out of wallet.h/cpp #include "guiinterfaceutil.h" #include "masternode.h" #include "masternode-payments.h" @@ -21,8 +24,12 @@ #include "spork.h" #include "util/system.h" #include "utilmoneystr.h" +#include "wallet/init.h" +#include "wallet/fees.h" #include "zpivchain.h" +#include // for StartShutdown/ShutdownRequested + #include #include @@ -692,7 +699,7 @@ void CWallet::SyncMetaData(std::pair::iterator, typename ///////// Init //////////////// -bool CWallet::ParameterInteraction() +bool WalletParameterInteraction() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; @@ -2160,7 +2167,7 @@ void CWallet::Flush(bool shutdown) bitdb.Flush(shutdown); } -bool CWallet::Verify() +bool WalletVerify() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; @@ -3591,12 +3598,12 @@ CWallet::CommitResult CWallet::CommitTransaction(CTransactionRef tx, CReserveKey return res; } -CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) +CAmount GetRequiredFee(unsigned int nTxBytes) { - return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); + return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); } -CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) +CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) { // payTxFee is user-set "I want to pay this much" CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); @@ -4259,7 +4266,7 @@ void CWallet::LockIfMyCollateral(const CTransactionRef& ptx) } } -std::string CWallet::GetWalletHelpString(bool showDebug) +std::string GetWalletHelpString(bool showDebug) { std::string strUsage = HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageOpt("-backuppath=", _("Specify custom backup path to add a copy of any wallet backup. If set as dir, every backup generates a timestamped file. If set as file, will rewrite to that file every backup.")); @@ -4490,7 +4497,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) return walletInstance; } -bool CWallet::InitLoadWallet() +bool InitLoadWallet() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); @@ -4509,7 +4516,7 @@ bool CWallet::InitLoadWallet() } } - CWallet * const pwallet = CreateWalletFromFile(walletFile); + CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile); if (!pwallet) { return false; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6af90c096541a..3b354245dfd8d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1078,16 +1078,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface CAmount GetUnconfirmedShieldedBalance() const; static CFeeRate minTxFee; - /** - * Estimate the minimum fee considering user set parameters - * and the required fee - */ - static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool); - /** - * Return the minimum required fee taking into account the - * floating relay fee and user set minimum transaction fee - */ - static CAmount GetRequiredFee(unsigned int nTxBytes); size_t KeypoolCountExternalKeys(); bool TopUpKeyPool(unsigned int kpSize = 0); @@ -1161,23 +1151,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. - // This function will perform salvage on the wallet if requested, as long as only one wallet is - // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). - static bool Verify(); - /* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */ bool AbandonTransaction(const uint256& hashTx); - /* Returns the wallets help message */ - static std::string GetWalletHelpString(bool showDebug); - /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ static CWallet* CreateWalletFromFile(const std::string walletFile); - static bool InitLoadWallet(); - - /* Wallets parameter interaction */ - static bool ParameterInteraction(); /** * Wallet post-init setup From e947eec3273cc598017452529481e1e8c2ef3d25 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Thu, 29 Apr 2021 00:09:52 +0200 Subject: [PATCH 40/50] MOVEONLY: Fee functions wallet/wallet.cpp -> wallet/fees.cpp >>> backports bitcoin@e7fe3208a83c170e50407d703525f1b4cbf337a3 --- src/wallet/fees.cpp | 27 +++++++++++++++++++++++++++ src/wallet/wallet.cpp | 27 --------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index fb851c36520ab..fb83aa7025835 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -11,3 +11,30 @@ #include "util/system.h" #include "validation.h" #include "wallet/wallet.h" + +CAmount GetRequiredFee(unsigned int nTxBytes) +{ + return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); +} + +CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) +{ + // payTxFee is user-set "I want to pay this much" + CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); + // User didn't set: use -txconfirmtarget to estimate... + if (nFeeNeeded == 0) { + int estimateFoundTarget = (int) nConfirmTarget; + nFeeNeeded = pool.estimateSmartFee((int) nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes); + // ... unless we don't have enough mempool data for our desired target + // so we make sure we're paying at least minTxFee + if (nFeeNeeded == 0 || (unsigned int) estimateFoundTarget > nConfirmTarget) + nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); + } + // prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee + if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes)) + nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes); + // But always obey the maximum + if (nFeeNeeded > maxTxFee) + nFeeNeeded = maxTxFee; + return nFeeNeeded; +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 37eac7d5a8346..97f56b1ae6be2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3598,33 +3598,6 @@ CWallet::CommitResult CWallet::CommitTransaction(CTransactionRef tx, CReserveKey return res; } -CAmount GetRequiredFee(unsigned int nTxBytes) -{ - return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); -} - -CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) -{ - // payTxFee is user-set "I want to pay this much" - CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); - // User didn't set: use -txconfirmtarget to estimate... - if (nFeeNeeded == 0) { - int estimateFoundTarget = (int) nConfirmTarget; - nFeeNeeded = pool.estimateSmartFee((int) nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes); - // ... unless we don't have enough mempool data for our desired target - // so we make sure we're paying at least minTxFee - if (nFeeNeeded == 0 || (unsigned int) estimateFoundTarget > nConfirmTarget) - nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); - } - // prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee - if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes)) - nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes); - // But always obey the maximum - if (nFeeNeeded > maxTxFee) - nFeeNeeded = maxTxFee; - return nFeeNeeded; -} - DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { LOCK2(cs_main, cs_wallet); From e067235fb4e8e14e88865191cc3914766f4f0e83 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Thu, 29 Apr 2021 00:12:59 +0200 Subject: [PATCH 41/50] MOVEONLY: Init functions wallet/wallet.cpp -> wallet/init.cpp >>> backports bitcoin@f01103c1e0a204fc7f40a06755f6c3adb5480cf8 --- src/wallet/init.cpp | 218 +++++++++++++++++++++++++++++++++++++++++ src/wallet/wallet.cpp | 219 ------------------------------------------ 2 files changed, 218 insertions(+), 219 deletions(-) diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 626da1e86a0e8..98dc0417094b4 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -12,3 +12,221 @@ #include "utilmoneystr.h" #include "validation.h" #include "wallet/wallet.h" + +std::string GetWalletHelpString(bool showDebug) +{ + std::string strUsage = HelpMessageGroup(_("Wallet options:")); + strUsage += HelpMessageOpt("-backuppath=", _("Specify custom backup path to add a copy of any wallet backup. If set as dir, every backup generates a timestamped file. If set as file, will rewrite to that file every backup.")); + strUsage += HelpMessageOpt("-createwalletbackups=", strprintf(_("Number of automatic wallet backups (default: %d)"), DEFAULT_CREATEWALLETBACKUPS)); + strUsage += HelpMessageOpt("-custombackupthreshold=", strprintf(_("Number of custom location backups to retain (default: %d)"), DEFAULT_CUSTOMBACKUPTHRESHOLD)); + strUsage += HelpMessageOpt("-disablewallet", strprintf(_("Do not load the wallet and disable wallet RPC calls (default: %u)"), DEFAULT_DISABLE_WALLET)); + strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); + strUsage += HelpMessageOpt("-legacywallet", _("On first run, create a legacy wallet instead of a HD wallet")); + strUsage += HelpMessageOpt("-maxtxfee=", strprintf(_("Maximum total fees to use in a single wallet transaction, setting too low may abort large transactions (default: %s)"), FormatMoney(maxTxFee))); + strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(CWallet::minTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions") + " " + _("on startup")); + strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet file") + " " + _("on startup")); + strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); + strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), 1)); + strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format") + " " + _("on startup")); + strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); + strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + + " " + _("(1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)")); + strUsage += HelpMessageGroup(_("Mining/Staking options:")); + strUsage += HelpMessageOpt("-coldstaking=", strprintf(_("Enable cold staking functionality (0-1, default: %u). Disabled if staking=0"), DEFAULT_COLDSTAKING)); + strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), DEFAULT_GENERATE)); + strUsage += HelpMessageOpt("-genproclimit=", strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), DEFAULT_GENERATE_PROCLIMIT)); + strUsage += HelpMessageOpt("-minstakesplit=", strprintf(_("Minimum positive amount (in PIV) allowed by GUI and RPC for the stake split threshold (default: %s)"), FormatMoney(DEFAULT_MIN_STAKE_SPLIT_THRESHOLD))); + strUsage += HelpMessageOpt("-staking=", strprintf(_("Enable staking functionality (0-1, default: %u)"), DEFAULT_STAKING)); + if (showDebug) { + strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); + strUsage += HelpMessageOpt("-dblogsize=", strprintf(_("Flush database activity from memory pool to disk log every megabytes (default: %u)"), DEFAULT_WALLET_DBLOGSIZE)); + strUsage += HelpMessageOpt("-flushwallet", strprintf(_("Run a thread to flush wallet periodically (default: %u)"), DEFAULT_FLUSHWALLET)); + strUsage += HelpMessageOpt("-printcoinstake", _("Display verbose coin stake messages in the debug.log file.")); + strUsage += HelpMessageOpt("-privdb", strprintf(_("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)"), DEFAULT_WALLET_PRIVDB)); + } + + return strUsage; +} + +bool WalletParameterInteraction() +{ + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + return true; + } + + if (gArgs.GetBoolArg("-sysperms", false)) { + return UIError(strprintf(_("%s is not allowed in combination with enabled wallet functionality"), "-sysperms")); + } + + gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + + if (gArgs.GetBoolArg("-salvagewallet", false)) { + if (is_multiwallet) { + return UIError(strprintf(_("%s is only allowed with a single wallet file"), "-salvagewallet")); + } + // Rewrite just private keys: rescan to find transactions + if (gArgs.SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } + } + + bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false); + // -zapwallettxes implies dropping the mempool on startup + if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); + } + + // -zapwallettxes implies a rescan + if (zapwallettxes) { + if (is_multiwallet) { + return UIError(strprintf(_("%s is only allowed with a single wallet file"), "-zapwallettxes")); + } + if (gArgs.SoftSetBoolArg("-rescan", true)) { + 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) + CWallet::minTxFee = CFeeRate(n); + else + return UIError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); + } + if (gArgs.IsArgSet("-paytxfee")) { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) + return UIError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); + if (nFeePerK > nHighTransactionFeeWarning) + UIWarning(strprintf(_("Warning: %s is set very high! This is the transaction fee you will pay if you send a transaction."), "-paytxfee")); + payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < ::minRelayTxFee) { + return UIError(strprintf(_("Invalid amount for %s: '%s' (must be at least %s)"), "-paytxfee", + gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + } + } + if (gArgs.IsArgSet("-maxtxfee")) { + CAmount nMaxFee = 0; + if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) + return UIError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); + if (nMaxFee > nHighTransactionMaxFeeWarning) + UIWarning(strprintf(_("Warning: %s is set very high! Fees this large could be paid on a single transaction."), "-maxtxfee")); + maxTxFee = nMaxFee; + if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) { + return UIError(strprintf(_("Invalid amount for %s: '%s' (must be at least the minimum relay fee of %s to prevent stuck transactions)"), + "-maxtxfee", gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); + } + } + if (gArgs.IsArgSet("-minstakesplit")) { + CAmount n = 0; + if (ParseMoney(gArgs.GetArg("-minstakesplit", ""), n) && n > 0) + CWallet::minStakeSplitThreshold = n; + else + return UIError(AmountErrMsg("minstakesplit", gArgs.GetArg("-minstakesplit", ""))); + } + nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", 1); + bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + bdisableSystemnotifications = gArgs.GetBoolArg("-disablesystemnotifications", false); + + return true; +} + +bool WalletVerify() +{ + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + return true; + } + + uiInterface.InitMessage(_("Verifying wallet(s)...")); + + // Keep track of each wallet absolute path to detect duplicates. + std::set wallet_paths; + + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + if (fs::path(walletFile).filename() != walletFile) { + return UIError(strprintf(_("Error loading wallet %s. %s parameter must only specify a filename (not a path)."), walletFile, "-wallet")); + } + if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + return UIError(strprintf(_("Error loading wallet %s. Invalid characters in %s filename."), walletFile, "-wallet")); + } + + fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); + + if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { + return UIError(strprintf(_("Error loading wallet %s. %s filename must be a regular file."), walletFile, "-wallet")); + } + + if (!wallet_paths.insert(wallet_path).second) { + return UIError(strprintf(_("Error loading wallet %s. Duplicate %s filename specified."), walletFile, "-wallet")); + } + + std::string strError; + if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { + return UIError(strError); + } + + 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; +} + +bool InitLoadWallet() +{ + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + LogPrintf("Wallet disabled!\n"); + return true; + } + + 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 = CWallet::CreateWalletFromFile(walletFile); + if (!pwallet) { + return false; + } + vpwallets.emplace_back(pwallet); + } + + return true; +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 97f56b1ae6be2..4ebc3487f931a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -9,11 +9,7 @@ #include "budget/budgetmanager.h" #include "coincontrol.h" -<<<<<<< Upstream, based on master #include "evo/deterministicmns.h" -#include "init.h" -======= ->>>>>>> 834e2bb Move some static functions out of wallet.h/cpp #include "guiinterfaceutil.h" #include "masternode.h" #include "masternode-payments.h" @@ -699,95 +695,7 @@ void CWallet::SyncMetaData(std::pair::iterator, typename ///////// Init //////////////// -bool WalletParameterInteraction() -{ - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - return true; - } - - if (gArgs.GetBoolArg("-sysperms", false)) { - return UIError(strprintf(_("%s is not allowed in combination with enabled wallet functionality"), "-sysperms")); - } - gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); - const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; - - if (gArgs.GetBoolArg("-salvagewallet", false)) { - if (is_multiwallet) { - return UIError(strprintf(_("%s is only allowed with a single wallet file"), "-salvagewallet")); - } - // Rewrite just private keys: rescan to find transactions - if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); - } - } - - bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false); - // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); - } - - // -zapwallettxes implies a rescan - if (zapwallettxes) { - if (is_multiwallet) { - return UIError(strprintf(_("%s is only allowed with a single wallet file"), "-zapwallettxes")); - } - if (gArgs.SoftSetBoolArg("-rescan", true)) { - 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) - CWallet::minTxFee = CFeeRate(n); - else - return UIError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); - } - if (gArgs.IsArgSet("-paytxfee")) { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) - return UIError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); - if (nFeePerK > nHighTransactionFeeWarning) - UIWarning(strprintf(_("Warning: %s is set very high! This is the transaction fee you will pay if you send a transaction."), "-paytxfee")); - payTxFee = CFeeRate(nFeePerK, 1000); - if (payTxFee < ::minRelayTxFee) { - return UIError(strprintf(_("Invalid amount for %s: '%s' (must be at least %s)"), "-paytxfee", - gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); - } - } - if (gArgs.IsArgSet("-maxtxfee")) { - CAmount nMaxFee = 0; - if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) - return UIError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); - if (nMaxFee > nHighTransactionMaxFeeWarning) - UIWarning(strprintf(_("Warning: %s is set very high! Fees this large could be paid on a single transaction."), "-maxtxfee")); - maxTxFee = nMaxFee; - if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) { - return UIError(strprintf(_("Invalid amount for %s: '%s' (must be at least the minimum relay fee of %s to prevent stuck transactions)"), - "-maxtxfee", gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); - } - } - if (gArgs.IsArgSet("-minstakesplit")) { - CAmount n = 0; - if (ParseMoney(gArgs.GetArg("-minstakesplit", ""), n) && n > 0) - CWallet::minStakeSplitThreshold = n; - else - return UIError(AmountErrMsg("minstakesplit", gArgs.GetArg("-minstakesplit", ""))); - } - nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", 1); - bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - bdisableSystemnotifications = gArgs.GetBoolArg("-disablesystemnotifications", false); - - return true; -} //////// End Init //////////// @@ -2167,67 +2075,6 @@ void CWallet::Flush(bool shutdown) bitdb.Flush(shutdown); } -bool WalletVerify() -{ - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - return true; - } - - uiInterface.InitMessage(_("Verifying wallet(s)...")); - - // Keep track of each wallet absolute path to detect duplicates. - std::set wallet_paths; - - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - if (fs::path(walletFile).filename() != walletFile) { - return UIError(strprintf(_("Error loading wallet %s. %s parameter must only specify a filename (not a path)."), walletFile, "-wallet")); - } - if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { - return UIError(strprintf(_("Error loading wallet %s. Invalid characters in %s filename."), walletFile, "-wallet")); - } - - fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); - - if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { - return UIError(strprintf(_("Error loading wallet %s. %s filename must be a regular file."), walletFile, "-wallet")); - } - - if (!wallet_paths.insert(wallet_path).second) { - return UIError(strprintf(_("Error loading wallet %s. Duplicate %s filename specified."), walletFile, "-wallet")); - } - - std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { - return UIError(strError); - } - - 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 @@ -4239,44 +4086,6 @@ void CWallet::LockIfMyCollateral(const CTransactionRef& ptx) } } -std::string GetWalletHelpString(bool showDebug) -{ - std::string strUsage = HelpMessageGroup(_("Wallet options:")); - strUsage += HelpMessageOpt("-backuppath=", _("Specify custom backup path to add a copy of any wallet backup. If set as dir, every backup generates a timestamped file. If set as file, will rewrite to that file every backup.")); - strUsage += HelpMessageOpt("-createwalletbackups=", strprintf(_("Number of automatic wallet backups (default: %d)"), DEFAULT_CREATEWALLETBACKUPS)); - strUsage += HelpMessageOpt("-custombackupthreshold=", strprintf(_("Number of custom location backups to retain (default: %d)"), DEFAULT_CUSTOMBACKUPTHRESHOLD)); - strUsage += HelpMessageOpt("-disablewallet", strprintf(_("Do not load the wallet and disable wallet RPC calls (default: %u)"), DEFAULT_DISABLE_WALLET)); - strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); - strUsage += HelpMessageOpt("-legacywallet", _("On first run, create a legacy wallet instead of a HD wallet")); - strUsage += HelpMessageOpt("-maxtxfee=", strprintf(_("Maximum total fees to use in a single wallet transaction, setting too low may abort large transactions (default: %s)"), FormatMoney(maxTxFee))); - strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(CWallet::minTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions") + " " + _("on startup")); - strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet file") + " " + _("on startup")); - strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); - strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), 1)); - strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format") + " " + _("on startup")); - strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); - strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); - strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + - " " + _("(1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)")); - strUsage += HelpMessageGroup(_("Mining/Staking options:")); - strUsage += HelpMessageOpt("-coldstaking=", strprintf(_("Enable cold staking functionality (0-1, default: %u). Disabled if staking=0"), DEFAULT_COLDSTAKING)); - strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), DEFAULT_GENERATE)); - strUsage += HelpMessageOpt("-genproclimit=", strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), DEFAULT_GENERATE_PROCLIMIT)); - strUsage += HelpMessageOpt("-minstakesplit=", strprintf(_("Minimum positive amount (in PIV) allowed by GUI and RPC for the stake split threshold (default: %s)"), FormatMoney(DEFAULT_MIN_STAKE_SPLIT_THRESHOLD))); - strUsage += HelpMessageOpt("-staking=", strprintf(_("Enable staking functionality (0-1, default: %u)"), DEFAULT_STAKING)); - if (showDebug) { - strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); - strUsage += HelpMessageOpt("-dblogsize=", strprintf(_("Flush database activity from memory pool to disk log every megabytes (default: %u)"), DEFAULT_WALLET_DBLOGSIZE)); - strUsage += HelpMessageOpt("-flushwallet", strprintf(_("Run a thread to flush wallet periodically (default: %u)"), DEFAULT_FLUSHWALLET)); - strUsage += HelpMessageOpt("-printcoinstake", _("Display verbose coin stake messages in the debug.log file.")); - strUsage += HelpMessageOpt("-privdb", strprintf(_("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)"), DEFAULT_WALLET_PRIVDB)); - } - - return strUsage; -} - CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) { // needed to restore wallet transaction meta data after -zapwallettxes @@ -4470,34 +4279,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) return walletInstance; } -bool InitLoadWallet() -{ - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - LogPrintf("Wallet disabled!\n"); - return true; - } - - 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 = CWallet::CreateWalletFromFile(walletFile); - if (!pwallet) { - return false; - } - vpwallets.emplace_back(pwallet); - } - - return true; -} std::atomic CWallet::fFlushScheduled(false); From ffcd7814374c97118608f1be361ecf9b08e4fb98 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Thu, 29 Apr 2021 00:15:52 +0200 Subject: [PATCH 42/50] [Trivial] Cleanup after MOVE-ONLY commits --- src/wallet/wallet.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4ebc3487f931a..3e084d7eda065 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -20,7 +20,6 @@ #include "spork.h" #include "util/system.h" #include "utilmoneystr.h" -#include "wallet/init.h" #include "wallet/fees.h" #include "zpivchain.h" @@ -693,12 +692,6 @@ void CWallet::SyncMetaData(std::pair::iterator, typename } } -///////// Init //////////////// - - - -//////// End Init //////////// - const CKeyingMaterial& CWallet::GetEncryptionKey() const { return vMasterKey; From 1d966ce73e06d0d01e24ba15eb99b418be853f1d Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 17 Jul 2017 15:43:12 -0700 Subject: [PATCH 43/50] Add warnings field to getblockchaininfo --- src/rpc/blockchain.cpp | 4 +++- test/functional/rpc_blockchain.py | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index cc3d93f59ab45..2a9e16fe509c0 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -24,6 +24,7 @@ #include "hash.h" #include "validationinterface.h" #include "wallet/wallet.h" +#include "warnings.h" #include #include @@ -1010,6 +1011,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " \"info\": \"xxxx\", (string) additional information about upgrade\n" " }, ...\n" "}\n" + " \"warnings\" : \"...\", (string) any network and blockchain errors.\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "")); @@ -1039,7 +1041,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) NetworkUpgradeDescPushBack(upgrades, consensusParams, Consensus::UpgradeIndex(i), nTipHeight); } obj.pushKV("upgrades", upgrades); - + obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index be9a0717f329b..f3fef36d1cd25 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -38,7 +38,7 @@ def set_test_params(self): self.num_nodes = 1 def run_test(self): - #self._test_getblockchaininfo() + self._test_getblockchaininfo() self._test_gettxoutsetinfo() self._test_getblockheader() #self._test_getdifficulty() @@ -54,6 +54,10 @@ def _test_getblockchaininfo(self): 'chainwork', 'difficulty', 'headers', + 'initial_block_downloading', + 'shield_pool_value', + 'softforks', + 'upgrades', 'verificationprogress', 'warnings', ] From c04390baa4729f4c8796900c6bba2ef6764f1b15 Mon Sep 17 00:00:00 2001 From: random-zebra Date: Thu, 29 Apr 2021 00:55:41 +0200 Subject: [PATCH 44/50] Unify help text for GetWarnings output in get*info RPCs >>> adapted from bitcoin@8502b2085288bcf5b5ff96b77236a3b04c65f082 --- src/rpc/blockchain.cpp | 2 +- src/rpc/mining.cpp | 4 ++-- src/rpc/net.cpp | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 2a9e16fe509c0..d1d1ef12eaaa4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1011,7 +1011,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " \"info\": \"xxxx\", (string) additional information about upgrade\n" " }, ...\n" "}\n" - " \"warnings\" : \"...\", (string) any network and blockchain errors.\n" + " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "")); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 575c2e5bfe15c..913439b3671bc 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -366,13 +366,13 @@ UniValue getmininginfo(const JSONRPCRequest& request) " \"currentblocksize\": nnn, (numeric) The last block size\n" " \"currentblocktx\": nnn, (numeric) The last block transaction\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"errors\": \"...\" (string) Current errors\n" " \"generate\": true|false (boolean) If the generation is on or off (see getgenerate or setgenerate calls)\n" " \"genproclimit\": n (numeric) The processor limit for generation. -1 if no generation. (see getgenerate or setgenerate calls)\n" " \"hashespersec\": n (numeric) The hashes per second of the generation, or 0 if no generation.\n" " \"pooledtx\": n (numeric) The size of the mem pool\n" " \"testnet\": true|false (boolean) If using testnet or not\n" " \"chain\": \"xxxx\", (string) current network name (main, test, regtest)\n" + " \"errors\": \"...\" (string) (string) any network and blockchain warnings\n" "}\n" "\nExamples:\n" + @@ -385,12 +385,12 @@ UniValue getmininginfo(const JSONRPCRequest& request) obj.pushKV("currentblocksize", (uint64_t)nLastBlockSize); obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx); obj.pushKV("difficulty", (double)GetDifficulty()); - obj.pushKV("errors", GetWarnings("statusbar")); obj.pushKV("genproclimit", (int)gArgs.GetArg("-genproclimit", -1)); obj.pushKV("networkhashps", getnetworkhashps(request)); obj.pushKV("pooledtx", (uint64_t)mempool.size()); obj.pushKV("testnet", Params().IsTestnet()); obj.pushKV("chain", Params().NetworkIDString()); + obj.pushKV("errors", GetWarnings("statusbar")); #ifdef ENABLE_WALLET obj.pushKV("generate", getgenerate(request)); obj.pushKV("hashespersec", gethashespersec(request)); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index e7e99732bb600..2dd5bfd965ae0 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -17,6 +17,7 @@ #include "util/system.h" #include "version.h" #include "validation.h" +#include "warnings.h" #include @@ -391,6 +392,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) " }\n" " ,...\n" " ]\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" "}\n" "\nExamples:\n" + @@ -420,6 +422,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) } } obj.pushKV("localaddresses", localAddresses); + obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } From f15aeea222910ccd9e391b2838c04f16df0d8fda Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 26 Sep 2017 12:03:34 -0400 Subject: [PATCH 45/50] Change getmininginfo errors field to warnings Changes the errors field to warnings. To maintain compatibility, the errors field is deprecated and enabled by starting bitcoind with -deprecatedrpc=getmininginfo --- src/rpc/mining.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 913439b3671bc..24d3611bbe749 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -372,7 +372,8 @@ UniValue getmininginfo(const JSONRPCRequest& request) " \"pooledtx\": n (numeric) The size of the mem pool\n" " \"testnet\": true|false (boolean) If using testnet or not\n" " \"chain\": \"xxxx\", (string) current network name (main, test, regtest)\n" - " \"errors\": \"...\" (string) (string) any network and blockchain warnings\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" + " \"errors\": \"...\" (string) DEPRECATED. Same as warnings. Only shown when bitcoind is started with -deprecatedrpc=getmininginfo\n" "}\n" "\nExamples:\n" + @@ -391,6 +392,11 @@ UniValue getmininginfo(const JSONRPCRequest& request) obj.pushKV("testnet", Params().IsTestnet()); obj.pushKV("chain", Params().NetworkIDString()); obj.pushKV("errors", GetWarnings("statusbar")); + if (IsDeprecatedRPCEnabled("getmininginfo")) { + obj.pushKV("errors", GetWarnings("statusbar")); + } else { + obj.pushKV("warnings", GetWarnings("statusbar")); + } #ifdef ENABLE_WALLET obj.pushKV("generate", getgenerate(request)); obj.pushKV("hashespersec", gethashespersec(request)); From e411b702d19356f002a089df6c7ffd983fb7ec90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Thu, 12 Oct 2017 23:14:46 +0100 Subject: [PATCH 46/50] [wallet] Fix leak in CDB constructor Now using a std::unique_ptr, the Db instance is correctly released when CDB initialization fails. The internal CDB state and mapFileUseCount are only mutated when the CDB initialization succeeds. --- src/wallet/db.cpp | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 2429a05070489..62dfb4846d60e 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -352,9 +352,8 @@ void CDBEnv::CheckpointLSN(const std::string& strFile) } -CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL) +CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr) { - int ret; fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); fFlushOnClose = fFlushOnCloseIn; env = dbw.env; @@ -363,7 +362,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb } const std::string &strFilename = dbw.strFile; - bool fCreate = strchr(pszMode, 'c') != NULL; + bool fCreate = strchr(pszMode, 'c') != nullptr; unsigned int nFlags = DB_THREAD; if (fCreate) nFlags |= DB_CREATE; @@ -373,45 +372,43 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb if (!env->Open(GetDataDir())) throw std::runtime_error("CDB: Failed to open database environment."); - strFile = strFilename; - ++env->mapFileUseCount[strFile]; - pdb = env->mapDb[strFile]; - if (pdb == NULL) { - pdb = new Db(env->dbenv, 0); + pdb = env->mapDb[strFilename]; + if (pdb == nullptr) { + int ret; + std::unique_ptr pdb_temp(new Db(env->dbenv, 0)); bool fMockDb = env->IsMock(); if (fMockDb) { - DbMpoolFile* mpf = pdb->get_mpf(); + DbMpoolFile* mpf = pdb_temp->get_mpf(); ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); - if (ret != 0) - throw std::runtime_error(strprintf("CDB : Failed to configure for no temp file backing for database %s", strFile)); + if (ret != 0) { + throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFilename)); + } } - ret = pdb->open(NULL, // Txn pointer - fMockDb ? NULL : strFile.c_str(), // Filename - fMockDb ? strFile.c_str() : "main", // Logical db name - DB_BTREE, // Database type - nFlags, // Flags - 0); + ret = pdb_temp->open(nullptr, // Txn pointer + fMockDb ? nullptr : strFilename.c_str(), // Filename + fMockDb ? strFilename.c_str() : "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags + 0); if (ret != 0) { - delete pdb; - pdb = NULL; - --env->mapFileUseCount[strFile]; - std::string tempCopy(strFile); - strFile = ""; - throw std::runtime_error(strprintf("CDB : Error %d, can't open database %s", ret, tempCopy)); + throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); } + pdb = pdb_temp.release(); + env->mapDb[strFilename] = pdb; + if (fCreate && !Exists(std::string("version"))) { bool fTmp = fReadOnly; fReadOnly = false; WriteVersion(CLIENT_VERSION); fReadOnly = fTmp; } - - env->mapDb[strFile] = pdb; } + ++env->mapFileUseCount[strFilename]; + strFile = strFilename; } } From 20c269bcbf49429765b7026a607cb7db5976d308 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 10 Oct 2017 15:27:26 -0400 Subject: [PATCH 47/50] Avoid opening copied wallet databases simultaneously Make sure wallet databases have unique fileids. If they don't, throw an error. BDB caches do not work properly when more than one open database has the same fileid, because values written to one database may show up in reads to other databases. Bitcoin will never create different databases with the same fileid, but users can create them by manually copying database files. BDB caching bug was reported by Chris Moore Fixes bitcoin issue 11429 --- src/wallet/db.cpp | 34 +++++++++++++++++++++++++++ src/wallet/db.h | 2 +- test/functional/wallet_multiwallet.py | 9 +++---- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 62dfb4846d60e..9f1d0c095ead0 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -22,6 +22,39 @@ #include +namespace { +//! Make sure database has a unique fileid within the environment. If it +//! doesn't, throw an error. BDB caches do not work properly when more than one +//! open database has the same fileid (values written to one database may show +//! up in reads to other databases). +//! +//! BerkeleyDB generates unique fileids by default +//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html), +//! so bitcoin should never create different databases with the same fileid, but +//! this error can be triggered if users manually copy database files. +void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) +{ + if (env.IsMock()) return; + + u_int8_t fileid[DB_FILE_ID_LEN]; + int ret = db.get_mpf()->get_fileid(fileid); + if (ret != 0) { + throw std::runtime_error(strprintf("CDB: Can't open database %s (get_fileid failed with %d)", filename, ret)); + } + + for (const auto& item : env.mapDb) { + u_int8_t item_fileid[DB_FILE_ID_LEN]; + if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 && + memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { + const char* item_filename = nullptr; + item.second->get_dbname(&item_filename, nullptr); + throw std::runtime_error(strprintf("CDB: Can't open database %s (duplicates fileid %s from %s)", filename, + HexStr(std::begin(item_fileid), std::end(item_fileid)), + item_filename ? item_filename : "(unknown database)")); + } + } +} +} // namespace // // CDB @@ -396,6 +429,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb if (ret != 0) { throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); } + CheckUniqueFileid(*env, strFilename, *pdb_temp); pdb = pdb_temp.release(); env->mapDb[strFilename] = pdb; diff --git a/src/wallet/db.h b/src/wallet/db.h index f39e60f2c0c15..413e98dd7b66b 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -46,7 +46,7 @@ class CDBEnv void Reset(); void MakeMock(); - bool IsMock() { return fMockDb; } + bool IsMock() const { return fMockDb; } /** * Verify that database file strFile is OK. If it is not, diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 42e9c9c5c48e5..703dbd3a62370 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -44,15 +44,16 @@ def run_test(self): #os.mkdir(wallet_dir('w11')) self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') - # !TODO: backport bitcoin#11476 + bitcoin#11970 #should not initialize if one wallet is a copy of another - #shutil.copyfile(wallet_dir('w2'), wallet_dir('w22')) - #self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') + #shutil.copyfile(wallet_dir('w2'), wallet_dir('w22')) # !TODO: backport bitcoin#11970 + shutil.copyfile(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w2'), + os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w22')) + self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') # should not initialize if wallet file is a symlink os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12')) - # os.symlink(wallet_dir('w1'), wallet_dir('w12')) + # os.symlink(wallet_dir('w1'), wallet_dir('w12')) # !TODO: backport bitcoin#11970 self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') self.log.info("Do not allow -zapwallettxes with multiwallet") From dbda8741d9997dce911dbf2c47399d5622c424c1 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 31 Oct 2017 20:22:41 -1000 Subject: [PATCH 48/50] [Wallet] always show help-line of wallet encryption calls --- src/wallet/rpcwallet.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 47ae55b568cc1..2687bd028cfbc 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3375,7 +3375,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request) if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - if (pwallet->IsCrypted() && (request.fHelp || request.params.size() < 2 || request.params.size() > 3)) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { throw std::runtime_error( "walletpassphrase \"passphrase\" timeout ( staking_only )\n" "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" @@ -3399,6 +3399,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request) HelpExampleCli("walletlock", "") + "\nAs json rpc call\n" + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")); + } LOCK2(cs_main, pwallet->cs_wallet); @@ -3454,7 +3455,7 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request) if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) + if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" @@ -3465,6 +3466,7 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request) "\nExamples:\n" + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")); + } LOCK2(cs_main, pwallet->cs_wallet); @@ -3502,7 +3504,7 @@ UniValue walletlock(const JSONRPCRequest& request) if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) + if (request.fHelp || !request.params.empty()) { throw std::runtime_error( "walletlock\n" "\nRemoves the wallet encryption key from memory, locking the wallet.\n" @@ -3518,6 +3520,7 @@ UniValue walletlock(const JSONRPCRequest& request) HelpExampleCli("walletlock", "") + "\nAs json rpc call\n" + HelpExampleRpc("walletlock", "")); + } // 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 @@ -3544,7 +3547,7 @@ UniValue encryptwallet(const JSONRPCRequest& request) if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; - if (!pwallet->IsCrypted() && (request.fHelp || request.params.size() != 1)) + if (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" @@ -3568,6 +3571,7 @@ UniValue encryptwallet(const JSONRPCRequest& request) HelpExampleCli("walletlock", "") + "\nAs a json rpc call\n" + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")); + } LOCK2(cs_main, pwallet->cs_wallet); From 3711c6a4d5c3d4ca2f1ff768aa0a043769cea224 Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Sat, 9 Sep 2017 00:19:25 +1200 Subject: [PATCH 49/50] Add wallet backup text to import*, add* and dumpwallet RPCs --- src/wallet/rpcdump.cpp | 17 +++++++---------- src/wallet/rpcwallet.cpp | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 6265a8f41586e..94fb109ecc327 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -86,9 +86,8 @@ UniValue importprivkey(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "importprivkey \"privkey\" ( \"label\" rescan is_staking_address )\n" - "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" + + "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n" + HelpRequiringPassphrase(pwallet) + "\n" - "\nArguments:\n" "1. \"privkey\" (string, required) The private key (see dumpprivkey)\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" @@ -215,8 +214,7 @@ UniValue importaddress(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "importaddress \"script\" ( \"label\" rescan )\n" - "\nAdds a script (in hex), or address, that can be watched as if it were in your wallet but cannot be used to spend.\n" - + "\nAdds a script (in hex), or address, that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n" "\nArguments:\n" "1. \"script\" (string, required) hex-encoded script (or address)\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" @@ -282,7 +280,7 @@ UniValue importpubkey(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "importpubkey \"pubkey\" ( \"label\" rescan )\n" - "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n" + "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n" "\nArguments:\n" "1. \"pubkey\" (string, required) The hex-encoded public key\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" @@ -339,9 +337,8 @@ UniValue importwallet(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "importwallet \"filename\"\n" - "\nImports keys from a wallet dump file (see dumpwallet).\n" + + "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup.\n" + HelpRequiringPassphrase(pwallet) + "\n" - "\nArguments:\n" "1. \"filename\" (string, required) The wallet file\n" @@ -506,9 +503,9 @@ UniValue dumpwallet(const JSONRPCRequest& request) 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" + + "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n" + "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n" + HelpRequiringPassphrase(pwallet) + "\n" - "\nArguments:\n" "1. \"filename\" (string, required) The filename\n" @@ -967,7 +964,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) 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" + + "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 2687bd028cfbc..f9eeeb32496de 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2408,7 +2408,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw std::runtime_error( "addmultisigaddress nrequired [\"key\",...] ( \"label\" )\n" - "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" + "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n" "Each key is a PIVX address or hex-encoded public key.\n" "If 'label' is specified, assign address to that label.\n" From d5526bdd67784c3a6064e8709f4db84b62b7f76f Mon Sep 17 00:00:00 2001 From: MeshCollider Date: Thu, 19 Oct 2017 22:01:30 +1300 Subject: [PATCH 50/50] Wrap dumpwallet warning and note scripts aren't dumped --- src/wallet/rpcdump.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 94fb109ecc327..e8f7de0b9e712 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -504,7 +504,9 @@ UniValue dumpwallet(const JSONRPCRequest& request) 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" - "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n" + + "Imported scripts are not currently included in wallet dumps, these must be backed up separately.\n" + "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n" + "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename\n"