diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index b5dbd9fd5..4efcb35a0 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -1,306 +1,306 @@ // Copyright (c) 2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_CONFIG_H) #include #endif #ifdef ENABLE_WALLET #include #include #define CHECK_WALLET(x) x #else #define CHECK_WALLET(x) \ throw std::logic_error("Wallet function called in non-wallet build.") #endif #include #include #include class HTTPRPCRequestProcessor; namespace interfaces { namespace { class NodeImpl : public Node { void parseParameters(int argc, const char *const argv[]) override { gArgs.ParseParameters(argc, argv); } void readConfigFiles() override { gArgs.ReadConfigFiles(); } bool softSetArg(const std::string &arg, const std::string &value) override { return gArgs.SoftSetArg(arg, value); } bool softSetBoolArg(const std::string &arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string &network) override { SelectParams(network); } std::string getNetwork() override { return Params().NetworkIDString(); } void initLogging() override { InitLogging(); } void initParameterInteraction() override { InitParameterInteraction(); } std::string getWarnings(const std::string &type) override { return GetWarnings(type); } bool baseInitialize(Config &config) override { return AppInitBasicSetup() && AppInitParameterInteraction(config) && AppInitSanityChecks() && AppInitLockDataDirectory(); } bool appInitMain(Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor) override { return AppInitMain(config, rpcServer, httpRPCRequestProcessor); } void appShutdown() override { Interrupt(); Shutdown(); } void startShutdown() override { StartShutdown(); } bool shutdownRequested() override { return ShutdownRequested(); } void mapPort(bool use_upnp) override { if (use_upnp) { StartMapPort(); } else { InterruptMapPort(); StopMapPort(); } } void setupServerArgs() override { return SetupServerArgs(); } bool getProxy(Network net, proxyType &proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { return g_connman ? g_connman->GetNodeCount(flags) : 0; } bool getNodesStats(NodesStats &stats) override { stats.clear(); if (g_connman) { std::vector stats_temp; g_connman->GetNodeStats(stats_temp); stats.reserve(stats_temp.size()); for (auto &node_stats_temp : stats_temp) { stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats()); } // Try to retrieve the CNodeStateStats for each node. TRY_LOCK(::cs_main, lockMain); if (lockMain) { for (auto &node_stats : stats) { std::get<1>(node_stats) = GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats)); } } return true; } return false; } bool getBanned(banmap_t &banmap) override { if (g_connman) { g_connman->GetBanned(banmap); return true; } return false; } bool ban(const CNetAddr &net_addr, BanReason reason, int64_t ban_time_offset) override { if (g_connman) { g_connman->Ban(net_addr, reason, ban_time_offset); return true; } return false; } bool unban(const CSubNet &ip) override { if (g_connman) { g_connman->Unban(ip); return true; } return false; } bool disconnect(NodeId id) override { if (g_connman) { return g_connman->DisconnectNode(id); } return false; } int64_t getTotalBytesRecv() override { return g_connman ? g_connman->GetTotalBytesRecv() : 0; } int64_t getTotalBytesSent() override { return g_connman ? g_connman->GetTotalBytesSent() : 0; } size_t getMempoolSize() override { return g_mempool.size(); } size_t getMempoolDynamicUsage() override { return g_mempool.DynamicMemoryUsage(); } bool getHeaderTip(int &height, int64_t &block_time) override { LOCK(::cs_main); if (::pindexBestHeader) { height = ::pindexBestHeader->nHeight; block_time = ::pindexBestHeader->GetBlockTime(); return true; } return false; } int getNumBlocks() override { LOCK(::cs_main); return ::chainActive.Height(); } int64_t getLastBlockTime() override { LOCK(::cs_main); if (::chainActive.Tip()) { return ::chainActive.Tip()->GetBlockTime(); } // Genesis block's time of current network return Params().GenesisBlock().GetBlockTime(); } double getVerificationProgress() override { const CBlockIndex *tip; { LOCK(::cs_main); tip = ::chainActive.Tip(); } return GuessVerificationProgress(Params().TxData(), tip); } bool isInitialBlockDownload() override { return IsInitialBlockDownload(); } bool getReindex() override { return ::fReindex; } bool getImporting() override { return ::fImporting; } void setNetworkActive(bool active) override { if (g_connman) { g_connman->SetNetworkActive(active); } } bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); } Amount getMaxTxFee() override { return ::maxTxFee; } CFeeRate estimateSmartFee() override { return g_mempool.estimateFee(); } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } UniValue executeRpc(Config &config, const std::string &command, const UniValue ¶ms, const std::string &uri) override { JSONRPCRequest req; req.params = params; req.strMethod = command; req.URI = uri; return ::tableRPC.execute(config, req); } std::vector listRpcCommands() override { return ::tableRPC.listCommands(); } void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) override { RPCSetTimerInterfaceIfUnset(iface); } void rpcUnsetTimerInterface(RPCTimerInterface *iface) override { RPCUnsetTimerInterface(iface); } bool getUnspentOutput(const COutPoint &output, Coin &coin) override { LOCK(::cs_main); return ::pcoinsTip->GetCoin(output, coin); } std::vector> getWallets() override { #ifdef ENABLE_WALLET std::vector> wallets; - for (CWallet *wallet : ::vpwallets) { + for (CWallet *wallet : GetWallets()) { wallets.emplace_back(MakeWallet(*wallet)); } return wallets; #else throw std::logic_error( "Node::getWallets() called in non-wallet build."); #endif } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeHandler(::uiInterface.InitMessage.connect(fn)); } std::unique_ptr handleMessageBox(MessageBoxFn fn) override { return MakeHandler(::uiInterface.ThreadSafeMessageBox.connect(fn)); } std::unique_ptr handleQuestion(QuestionFn fn) override { return MakeHandler(::uiInterface.ThreadSafeQuestion.connect(fn)); } std::unique_ptr handleShowProgress(ShowProgressFn fn) override { return MakeHandler(::uiInterface.ShowProgress.connect(fn)); } std::unique_ptr handleLoadWallet(LoadWalletFn fn) override { CHECK_WALLET(return MakeHandler(::uiInterface.LoadWallet.connect( [fn](CWallet *wallet) { fn(MakeWallet(*wallet)); }))); } std::unique_ptr handleNotifyNumConnectionsChanged( NotifyNumConnectionsChangedFn fn) override { return MakeHandler( ::uiInterface.NotifyNumConnectionsChanged.connect(fn)); } std::unique_ptr handleNotifyNetworkActiveChanged( NotifyNetworkActiveChangedFn fn) override { return MakeHandler( ::uiInterface.NotifyNetworkActiveChanged.connect(fn)); } std::unique_ptr handleNotifyAlertChanged(NotifyAlertChangedFn fn) override { return MakeHandler(::uiInterface.NotifyAlertChanged.connect(fn)); } std::unique_ptr handleBannedListChanged(BannedListChangedFn fn) override { return MakeHandler(::uiInterface.BannedListChanged.connect(fn)); } std::unique_ptr handleNotifyBlockTip(NotifyBlockTipFn fn) override { return MakeHandler(::uiInterface.NotifyBlockTip.connect( [fn](bool initial_download, const CBlockIndex *block) { fn(initial_download, block->nHeight, block->GetBlockTime(), GuessVerificationProgress(Params().TxData(), block)); })); } std::unique_ptr handleNotifyHeaderTip(NotifyHeaderTipFn fn) override { return MakeHandler(::uiInterface.NotifyHeaderTip.connect( [fn](bool initial_download, const CBlockIndex *block) { fn(initial_download, block->nHeight, block->GetBlockTime(), GuessVerificationProgress(Params().TxData(), block)); })); } }; } // namespace std::unique_ptr MakeNode() { return std::make_unique(); } } // namespace interfaces diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index b625d25a7..d5358ca33 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -1,251 +1,251 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { //! Press "Yes" or "Cancel" buttons in modal send confirmation dialog. void ConfirmSend(QString *text = nullptr, bool cancel = false) { QTimer::singleShot(0, Qt::PreciseTimer, [text, cancel]() { for (QWidget *widget : QApplication::topLevelWidgets()) { if (widget->inherits("SendConfirmationDialog")) { SendConfirmationDialog *dialog = qobject_cast(widget); if (text) { *text = dialog->text(); } QAbstractButton *button = dialog->button( cancel ? QMessageBox::Cancel : QMessageBox::Yes); button->setEnabled(true); button->click(); } } }); } //! Send coins to address and return txid. uint256 SendCoins(CWallet &wallet, SendCoinsDialog &sendCoinsDialog, const CTxDestination &address, Amount amount) { QVBoxLayout *entries = sendCoinsDialog.findChild("entries"); SendCoinsEntry *entry = qobject_cast(entries->itemAt(0)->widget()); entry->findChild("payTo")->setText( QString::fromStdString(EncodeDestination(address))); entry->findChild("payAmount")->setValue(amount); uint256 txid; boost::signals2::scoped_connection c = wallet.NotifyTransactionChanged.connect( [&txid](CWallet *, const uint256 &hash, ChangeType status) { if (status == CT_NEW) { txid = hash; } }); ConfirmSend(); QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked"); return txid; } //! Find index of txid in transaction list. QModelIndex FindTx(const QAbstractItemModel &model, const uint256 &txid) { QString hash = QString::fromStdString(txid.ToString()); int rows = model.rowCount({}); for (int row = 0; row < rows; ++row) { QModelIndex index = model.index(row, 0, {}); if (model.data(index, TransactionTableModel::TxHashRole) == hash) { return index; } } return {}; } //! Simple qt wallet tests. // // Test widgets can be debugged interactively calling show() on them and // manually running the event loop, e.g.: // // sendCoinsDialog.show(); // QEventLoop().exec(); // // This also requires overriding the default minimal Qt platform: // // src/qt/test/test_bitcoin-qt -platform xcb # Linux // src/qt/test/test_bitcoin-qt -platform windows # Windows // src/qt/test/test_bitcoin-qt -platform cocoa # macOS void TestGUI() { #ifdef Q_OS_MAC if (QApplication::platformName() == "minimal") { // Disable for mac on "minimal" platform to avoid crashes inside the Qt // framework when it tries to look up unimplemented cocoa functions, // and fails to handle returned nulls // (https://bugreports.qt.io/browse/QTBUG-49686). QWARN("Skipping WalletTests on mac build with 'minimal' platform set " "due to Qt bugs. To run AppTests, invoke " "with 'test_bitcoin-qt -platform cocoa' on mac, or else use a " "linux or windows build."); return; } #endif g_address_type = OutputType::LEGACY; g_change_type = OutputType::LEGACY; // Set up wallet and chain with 105 blocks (5 mature blocks for spending). TestChain100Setup test; for (int i = 0; i < 5; ++i) { test.CreateAndProcessBlock( {}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } CWallet wallet(Params(), "mock", CWalletDBWrapper::CreateMock()); bool firstRun; wallet.LoadWallet(firstRun); { LOCK(wallet.cs_wallet); wallet.SetAddressBook( GetDestinationForKey(test.coinbaseKey.GetPubKey(), g_address_type), "", "receive"); wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); } { LOCK(cs_main); WalletRescanReserver reserver(&wallet); reserver.reserve(); wallet.ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); } wallet.SetBroadcastTransactions(true); // Create widgets for sending coins and listing transactions. std::unique_ptr platformStyle( PlatformStyle::instantiate("other")); SendCoinsDialog sendCoinsDialog(platformStyle.get()); auto node = interfaces::MakeNode(); OptionsModel optionsModel(*node); - vpwallets.insert(vpwallets.begin(), &wallet); - WalletModel walletModel(std::move(node->getWallets()[0]), *node, + AddWallet(&wallet); + WalletModel walletModel(std::move(node->getWallets().back()), *node, platformStyle.get(), &optionsModel); - vpwallets.erase(vpwallets.begin()); + RemoveWallet(&wallet); sendCoinsDialog.setModel(&walletModel); // Send two transactions, and verify they are added to transaction list. TransactionTableModel *transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 105); uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CTxDestination(CKeyID()), 5 * COIN); uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CTxDestination(CKeyID()), 10 * COIN); QCOMPARE(transactionTableModel->rowCount({}), 107); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); // Check current balance on OverviewPage OverviewPage overviewPage(platformStyle.get()); overviewPage.setWalletModel(&walletModel); QLabel *balanceLabel = overviewPage.findChild("labelBalance"); QString balanceText = balanceLabel->text(); int unit = walletModel.getOptionsModel()->getDisplayUnit(); Amount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit( unit, balance, false, BitcoinUnits::separatorAlways); QCOMPARE(balanceText, balanceComparison); // Check Request Payment button const Config &config = GetConfig(); ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get(), &config); receiveCoinsDialog.setModel(&walletModel); RecentRequestsTableModel *requestTableModel = walletModel.getRecentRequestsTableModel(); // Label input QLineEdit *labelInput = receiveCoinsDialog.findChild("reqLabel"); labelInput->setText("TEST_LABEL_1"); // Amount input BitcoinAmountField *amountInput = receiveCoinsDialog.findChild("reqAmount"); amountInput->setValue(1 * SATOSHI); // Message input QLineEdit *messageInput = receiveCoinsDialog.findChild("reqMessage"); messageInput->setText("TEST_MESSAGE_1"); int initialRowCount = requestTableModel->rowCount({}); QPushButton *requestPaymentButton = receiveCoinsDialog.findChild("receiveButton"); requestPaymentButton->click(); for (QWidget *widget : QApplication::topLevelWidgets()) { if (widget->inherits("ReceiveRequestDialog")) { ReceiveRequestDialog *receiveRequestDialog = qobject_cast(widget); QTextEdit *rlist = receiveRequestDialog->QObject::findChild("outUri"); QString paymentText = rlist->toPlainText(); QStringList paymentTextList = paymentText.split('\n'); QCOMPARE(paymentTextList.at(0), QString("Payment information")); QVERIFY(paymentTextList.at(1).indexOf( QString("URI: bitcoincash:")) != -1); QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1); QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT)); QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1")); QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1")); } } // Clear button QPushButton *clearButton = receiveCoinsDialog.findChild("clearButton"); clearButton->click(); QCOMPARE(labelInput->text(), QString("")); QCOMPARE(amountInput->value(), Amount::zero()); QCOMPARE(messageInput->text(), QString("")); // Check addition to history int currentRowCount = requestTableModel->rowCount({}); QCOMPARE(currentRowCount, initialRowCount + 1); // Check Remove button QTableView *table = receiveCoinsDialog.findChild("recentRequestsView"); table->selectRow(currentRowCount - 1); QPushButton *removeRequestButton = receiveCoinsDialog.findChild("removeRequestButton"); removeRequestButton->click(); QCOMPARE(requestTableModel->rowCount({}), currentRowCount - 1); } } void WalletTests::walletTests() { TestGUI(); } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 7621b5d7f..c8936fbd8 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1,492 +1,492 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_WALLET #include #include #include #endif #include #include #include #ifdef HAVE_MALLOC_INFO #include #endif static UniValue validateaddress(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "validateaddress \"address\"\n" "\nReturn information about the given bitcoin address.\n" "DEPRECATION WARNING: Parts of this command have been deprecated " "and moved to getaddressinfo. Clients must\n" "transition to using getaddressinfo to access this information " "before upgrading to v0.20. The following deprecated\n" "fields have moved to getaddressinfo and will only be shown here " "with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n" "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, " "iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin " "address to validate\n" "\nResult:\n" "{\n" " \"isvalid\" : true|false, (boolean) If the address is " "valid or not. If not, this is the only property returned.\n" " \"address\" : \"address\", (string) The bitcoin address " "validated\n" " \"scriptPubKey\" : \"hex\", (string) The hex encoded " "scriptPubKey generated by the address\n" " \"isscript\" : true|false, (boolean) If the key is a " "script\n" "}\n" "\nExamples:\n" + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")); } CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); bool isValid = IsValidDestination(dest); UniValue ret(UniValue::VOBJ); ret.pushKV("isvalid", isValid); if (isValid) { #ifdef ENABLE_WALLET - if (!::vpwallets.empty() && + if (!GetWallets().empty() && IsDeprecatedRPCEnabled(gArgs, "validateaddress")) { ret.pushKVs(getaddressinfo(config, request)); } #endif if (ret["address"].isNull()) { std::string currentAddress = EncodeDestination(dest); ret.pushKV("address", currentAddress); CScript scriptPubKey = GetScriptForDestination(dest); ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); UniValue detail = DescribeAddress(dest); ret.pushKVs(detail); } } return ret; } // Needed even with !ENABLE_WALLET, to pass (ignored) pointers around class CWallet; static UniValue createmultisig(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) { std::string msg = "createmultisig nrequired [\"key\",...]\n" "\nCreates a multi-signature address with n signature of m keys " "required.\n" "It returns a json object with the address and redeemScript.\n" "DEPRECATION WARNING: Using addresses with createmultisig is " "deprecated. Clients must\n" "transition to using addmultisigaddress to create multisig " "addresses with addresses known\n" "to the wallet before upgrading to v0.20. To use the deprecated " "functionality, start bitcoind with -deprecatedrpc=createmultisig\n" "\nArguments:\n" "1. nrequired (numeric, required) The number of required " "signatures out of the n keys or addresses.\n" "2. \"keys\" (string, required) A json array of hex-encoded " "public keys\n" " [\n" " \"key\" (string) The hex-encoded public key\n" " ,...\n" " ]\n" "\nResult:\n" "{\n" " \"address\":\"multisigaddress\", (string) The value of the new " "multisig address.\n" " \"redeemScript\":\"script\" (string) The string value of " "the hex-encoded redemption script.\n" "}\n" "\nExamples:\n" "\nCreate a multisig address from 2 public keys\n" + HelpExampleCli("createmultisig", "2 " "\"[" "\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3" "42cf11ae157a7ace5fd\\\"," "\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1" "7e107ef3f6aa5a61626\\\"]\"") + "\nAs a json rpc call\n" + HelpExampleRpc("createmultisig", "2, " "\"[" "\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3" "42cf11ae157a7ace5fd\\\"," "\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1" "7e107ef3f6aa5a61626\\\"]\""); throw std::runtime_error(msg); } int required = request.params[0].get_int(); // Get the public keys const UniValue &keys = request.params[1].get_array(); std::vector pubkeys; for (size_t i = 0; i < keys.size(); ++i) { if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) { pubkeys.push_back(HexToPubKey(keys[i].get_str())); } else { #ifdef ENABLE_WALLET CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (IsDeprecatedRPCEnabled(gArgs, "createmultisig") && EnsureWalletIsAvailable(pwallet, false)) { pubkeys.push_back(AddrToPubKey(config.GetChainParams(), pwallet, keys[i].get_str())); } else #endif throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\nNote that from v0.19.6, " "createmultisig no longer accepts addresses." " Clients must transition to using " "addmultisigaddress to create multisig addresses " "with addresses known to the wallet before " "upgrading to v0.20." " To use the deprecated functionality, start " "bitcoind with -deprecatedrpc=createmultisig", keys[i].get_str())); } } // Construct using pay-to-script-hash: CScript inner = CreateMultisigRedeemscript(required, pubkeys); CScriptID innerID(inner); UniValue result(UniValue::VOBJ); result.pushKV("address", EncodeDestination(innerID)); result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); return result; } static UniValue verifymessage(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 3) { throw std::runtime_error( "verifymessage \"address\" \"signature\" \"message\"\n" "\nVerify a signed message\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "use for the signature.\n" "2. \"signature\" (string, required) The signature provided " "by the signer in base 64 encoding (see signmessage).\n" "3. \"message\" (string, required) The message that was " "signed.\n" "\nResult:\n" "true|false (boolean) If the signature is verified or not.\n" "\nExamples:\n" "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" + HelpExampleCli( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\" \"signature\" \"my " "message\"") + "\nAs json rpc\n" + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\", \"signature\", \"my " "message\"")); } LOCK(cs_main); std::string strAddress = request.params[0].get_str(); std::string strSign = request.params[1].get_str(); std::string strMessage = request.params[2].get_str(); CTxDestination destination = DecodeDestination(strAddress, config.GetChainParams()); if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } const CKeyID *keyID = boost::get(&destination); if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } bool fInvalid = false; std::vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); if (fInvalid) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; CPubKey pubkey; if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) { return false; } return (pubkey.GetID() == *keyID); } static UniValue signmessagewithprivkey(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "signmessagewithprivkey \"privkey\" \"message\"\n" "\nSign a message with the private key of an address\n" "\nArguments:\n" "1. \"privkey\" (string, required) The private key to sign " "the message with.\n" "2. \"message\" (string, required) The message to create a " "signature of.\n" "\nResult:\n" "\"signature\" (string) The signature of the message " "encoded in base 64\n" "\nExamples:\n" "\nCreate the signature\n" + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\" \"signature\" \"my " "message\"") + "\nAs json rpc\n" + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")); } std::string strPrivkey = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(strPrivkey); if (!fGood) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); } CKey key = vchSecret.GetKey(); if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; std::vector vchSig; if (!key.SignCompact(ss.GetHash(), vchSig)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); } return EncodeBase64(&vchSig[0], vchSig.size()); } static UniValue setmocktime(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "setmocktime timestamp\n" "\nSet the local time to given timestamp (-regtest only)\n" "\nArguments:\n" "1. timestamp (integer, required) Unix seconds-since-epoch " "timestamp\n" " Pass 0 to go back to using the system time."); } if (!config.GetChainParams().MineBlocksOnDemand()) { throw std::runtime_error( "setmocktime for regression testing (-regtest mode) only"); } // For now, don't change mocktime if we're in the middle of validation, as // this could have an effect on mempool time-based eviction, as well as // IsInitialBlockDownload(). // TODO: figure out the right way to synchronize around mocktime, and // ensure all call sites of GetTime() are accessing this safely. LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VNUM}); SetMockTime(request.params[0].get_int64()); return NullUniValue; } static UniValue RPCLockedMemoryInfo() { LockedPool::Stats stats = LockedPoolManager::Instance().stats(); UniValue obj(UniValue::VOBJ); obj.pushKV("used", uint64_t(stats.used)); obj.pushKV("free", uint64_t(stats.free)); obj.pushKV("total", uint64_t(stats.total)); obj.pushKV("locked", uint64_t(stats.locked)); obj.pushKV("chunks_used", uint64_t(stats.chunks_used)); obj.pushKV("chunks_free", uint64_t(stats.chunks_free)); return obj; } #ifdef HAVE_MALLOC_INFO static std::string RPCMallocInfo() { char *ptr = nullptr; size_t size = 0; FILE *f = open_memstream(&ptr, &size); if (f) { malloc_info(0, f); fclose(f); if (ptr) { std::string rv(ptr, size); free(ptr); return rv; } } return ""; } #endif static UniValue getmemoryinfo(const Config &config, const JSONRPCRequest &request) { /* Please, avoid using the word "pool" here in the RPC interface or help, * as users will undoubtedly confuse it with the other "memory pool" */ if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getmemoryinfo (\"mode\")\n" "Returns an object containing information about memory usage.\n" "Arguments:\n" "1. \"mode\" determines what kind of information is returned. This " "argument is optional, the default mode is \"stats\".\n" " - \"stats\" returns general statistics about memory usage in " "the daemon.\n" " - \"mallocinfo\" returns an XML string describing low-level " "heap state (only available if compiled with glibc 2.10+).\n" "\nResult (mode \"stats\"):\n" "{\n" " \"locked\": { (json object) Information about " "locked memory manager\n" " \"used\": xxxxx, (numeric) Number of bytes used\n" " \"free\": xxxxx, (numeric) Number of bytes available " "in current arenas\n" " \"total\": xxxxxxx, (numeric) Total number of bytes " "managed\n" " \"locked\": xxxxxx, (numeric) Amount of bytes that " "succeeded locking. If this number is smaller than total, locking " "pages failed at some point and key data could be swapped to " "disk.\n" " \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n" " \"chunks_free\": xxxxx, (numeric) Number unused chunks\n" " }\n" "}\n" "\nResult (mode \"mallocinfo\"):\n" "\"...\"\n" "\nExamples:\n" + HelpExampleCli("getmemoryinfo", "") + HelpExampleRpc("getmemoryinfo", "")); } std::string mode = (request.params.size() < 1 || request.params[0].isNull()) ? "stats" : request.params[0].get_str(); if (mode == "stats") { UniValue obj(UniValue::VOBJ); obj.pushKV("locked", RPCLockedMemoryInfo()); return obj; } else if (mode == "mallocinfo") { #ifdef HAVE_MALLOC_INFO return RPCMallocInfo(); #else throw JSONRPCError( RPC_INVALID_PARAMETER, "mallocinfo is only available when compiled with glibc 2.10+"); #endif } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode); } } static UniValue echo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp) { throw std::runtime_error( "echo|echojson \"message\" ...\n" "\nSimply echo back the input arguments. This command is for " "testing.\n" "\nThe difference between echo and echojson is that echojson has " "argument conversion enabled in the client-side table in" "bitcoin-cli and the GUI. There is no server-side difference."); } return request.params; } static UniValue getinfo_deprecated(const Config &config, const JSONRPCRequest &request) { throw JSONRPCError(RPC_METHOD_NOT_FOUND, "getinfo\n" "\nThis call was removed in version 0.19.8. Use the " "appropriate fields from:\n" "- getblockchaininfo: blocks, difficulty, chain\n" "- getnetworkinfo: version, protocolversion, " "timeoffset, connections, proxy, relayfee, warnings\n" "- getwalletinfo: balance, keypoololdest, keypoolsize, " "paytxfee, unlocked_until, walletversion\n" "\nbitcoin-cli has the option -getinfo to collect and " "format these in the old format."); } // clang-format off static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- { "control", "getmemoryinfo", getmemoryinfo, {"mode"} }, { "util", "validateaddress", validateaddress, {"address"} }, /* uses wallet if enabled */ { "util", "createmultisig", createmultisig, {"nrequired","keys"} }, { "util", "verifymessage", verifymessage, {"address","signature","message"} }, { "util", "signmessagewithprivkey", signmessagewithprivkey, {"privkey","message"} }, /* Not shown in help */ { "hidden", "setmocktime", setmocktime, {"timestamp"}}, { "hidden", "echo", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "echojson", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "getinfo", getinfo_deprecated, {}}, }; // clang-format on void RegisterMiscRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); } } diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index bc531a90a..417ff48ea 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -1,415 +1,415 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2017 The Bitcoin Core developers // Copyright (c) 2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include class WalletInit : public WalletInitInterface { public: //! Return the wallets help message. void AddWalletOptions() const override; //! Wallets parameter interaction bool ParameterInteraction() const override; //! Register wallet RPCs. void RegisterRPC(CRPCTable &tableRPC) const override; //! 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 (WalletParameterInteraction forbids // -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). bool Verify(const CChainParams &chainParams) const override; //! Load wallet databases. bool Open(const CChainParams &chainParams) const override; //! Complete startup of wallets. void Start(CScheduler &scheduler) const override; //! Flush all wallets in preparation for shutdown. void Flush() const override; //! Stop all wallets. Wallets will be flushed first. void Stop() const override; //! Close all wallets. void Close() const override; }; const WalletInitInterface &g_wallet_init_interface = WalletInit(); void WalletInit::AddWalletOptions() const { gArgs.AddArg( "-avoidpartialspends", strprintf(_("Group outputs by address, selecting all or none, instead " "of selecting on a per-output basis. Privacy is improved " "as an address is only used once (unless someone sends to " "it after spending from it), but may result in slightly " "higher fees as suboptimal coin selection may result due " "to the added limitation (default: %u)"), DEFAULT_AVOIDPARTIALSPENDS), false, OptionsCategory::WALLET); gArgs.AddArg("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"), false, OptionsCategory::WALLET); gArgs.AddArg("-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee " "estimation has insufficient data (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET); gArgs.AddArg("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET); gArgs.AddArg( "-maxtxfee=", strprintf(_("Maximum total fees (in %s) to use in a single wallet " "transaction or raw transaction; setting this too low may " "abort large transactions (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered " "zero fee for transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE_PER_KB)), false, OptionsCategory::WALLET); gArgs.AddArg( "-paytxfee=", strprintf( _("Fee (in %s/kB) to add to transactions you send (default: %s)"), CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), false, OptionsCategory::WALLET); gArgs.AddArg( "-rescan", _("Rescan the block chain for missing wallet transactions on startup"), false, OptionsCategory::WALLET); gArgs.AddArg( "-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup"), false, OptionsCategory::WALLET); gArgs.AddArg("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending " "transactions (default: %d)"), DEFAULT_SPEND_ZEROCONF_CHANGE), false, OptionsCategory::WALLET); gArgs.AddArg("-upgradewallet", _("Upgrade wallet to latest format on startup"), false, OptionsCategory::WALLET); gArgs.AddArg( "-wallet=", _("Specify wallet database path. Can be specified multiple times to " "load multiple wallets. Path is interpreted relative to " "if it is not absolute, and will be created if it does not exist (as " "a directory containing a wallet.dat file and log files). For " "backwards compatibility this will also accept names of existing " "data files in .)"), false, OptionsCategory::WALLET); gArgs.AddArg("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %d)"), DEFAULT_WALLETBROADCAST), false, OptionsCategory::WALLET); gArgs.AddArg("-walletdir=", _("Specify directory to hold wallets (default: " "/wallets if it exists, otherwise )"), false, OptionsCategory::WALLET); gArgs.AddArg("-walletnotify=", _("Execute command when a wallet transaction changes (%s in " "cmd is replaced by TxID)"), false, OptionsCategory::WALLET); gArgs.AddArg("-zapwallettxes=", _("Delete all wallet transactions and only recover those " "parts of the blockchain through -rescan on startup") + " " + _("(1 = keep tx meta data e.g. account owner and payment " "request information, 2 = drop tx meta data)"), false, OptionsCategory::WALLET); gArgs.AddArg("-dblogsize=", strprintf("Flush wallet database activity from memory to disk " "log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST); gArgs.AddArg( "-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %d)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST); gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db " "environment (default: %d)", DEFAULT_WALLET_PRIVDB), true, OptionsCategory::WALLET_DEBUG_TEST); gArgs.AddArg("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate " "mempool chain limits (default: %d)"), DEFAULT_WALLET_REJECT_LONG_CHAINS), true, OptionsCategory::WALLET_DEBUG_TEST); } bool WalletInit::ParameterInteraction() const { gArgs.SoftSetArg("-wallet", ""); const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; } if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting " "-walletbroadcast=0\n", __func__); } if (gArgs.GetBoolArg("-salvagewallet", false) && gArgs.SoftSetBoolArg("-rescan", true)) { if (is_multiwallet) { return InitError( strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); } // Rewrite just private keys: rescan to find transactions LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting " "-rescan=1\n", __func__); } int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); // -zapwallettxes implies dropping the mempool on startup if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting " "-persistmempool=0\n", __func__, zapwallettxes); } // -zapwallettxes implies a rescan if (zapwallettxes != 0) { if (is_multiwallet) { return InitError( strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); } if (gArgs.SoftSetBoolArg("-rescan", true)) { LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting " "-rescan=1\n", __func__, zapwallettxes); } LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting " "-rescan=1\n", __func__); } if (is_multiwallet) { if (gArgs.GetBoolArg("-upgradewallet", false)) { return InitError( strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); } } if (gArgs.GetBoolArg("-sysperms", false)) { return InitError("-sysperms is not allowed in combination with enabled " "wallet functionality"); } if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false)) { return InitError( _("Rescans are not possible in pruned mode. You will need to use " "-reindex which will download the whole blockchain again.")); } if (minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) { InitWarning( AmountHighWarn("-minrelaytxfee") + " " + _("The wallet will avoid paying less than the minimum relay fee.")); } if (gArgs.IsArgSet("-maxtxfee")) { Amount nMaxFee = Amount::zero(); if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) { return InitError( AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); } if (nMaxFee > HIGH_MAX_TX_FEE) { InitWarning(_("-maxtxfee is set very high! Fees this large could " "be paid on a single transaction.")); } maxTxFee = nMaxFee; if (CFeeRate(maxTxFee, 1000) < minRelayTxFee) { return InitError(strprintf( _("Invalid amount for -maxtxfee=: '%s' (must " "be at least the minrelay fee of %s to prevent " "stuck transactions)"), gArgs.GetArg("-maxtxfee", ""), minRelayTxFee.ToString())); } } g_address_type = OutputType::DEFAULT; g_change_type = OutputType::DEFAULT; return true; } void WalletInit::RegisterRPC(CRPCTable &t) const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return; } RegisterWalletRPCCommands(t); RegisterDumpRPCCommands(t); } bool WalletInit::Verify(const CChainParams &chainParams) const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; } if (gArgs.IsArgSet("-walletdir")) { fs::path wallet_dir = gArgs.GetArg("-walletdir", ""); if (!fs::exists(wallet_dir)) { return InitError( strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string())); } else if (!fs::is_directory(wallet_dir)) { return InitError( strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string())); } else if (!wallet_dir.is_absolute()) { return InitError( strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string())); } } LogPrintf("Using wallet directory %s\n", GetWalletDir().string()); 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")) { // Do some checking on wallet path. It should be either a: // // 1. Path where a directory can be created. // 2. Path to an existing directory. // 3. Path to a symlink to a directory. // 4. For backwards compatibility, the name of a data file in // -walletdir. fs::path wallet_path = fs::absolute(walletFile, GetWalletDir()); fs::file_type path_type = fs::symlink_status(wallet_path).type(); if (!(path_type == fs::file_not_found || path_type == fs::directory_file || (path_type == fs::symlink_file && fs::is_directory(wallet_path)) || (path_type == fs::regular_file && fs::path(walletFile).filename() == walletFile))) { return InitError(strprintf( _("Invalid -wallet path '%s'. -wallet path should point to a " "directory where wallet.dat and database/log.?????????? " "files can be stored, a location where such a directory " "could be created, or (for backwards compatibility) the name " "of an existing data file in -walletdir (%s)"), walletFile, GetWalletDir())); } if (!wallet_paths.insert(wallet_path).second) { return InitError(strprintf(_("Error loading wallet %s. Duplicate " "-wallet filename specified."), walletFile)); } std::string strError; if (!CWalletDB::VerifyEnvironment(wallet_path, strError)) { return InitError(strError); } if (gArgs.GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: CWallet dummyWallet(chainParams, "dummy", CWalletDBWrapper::CreateDummy()); std::string backup_filename; if (!CWalletDB::Recover(wallet_path, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { return false; } } std::string strWarning; bool dbV = CWalletDB::VerifyDatabaseFile(wallet_path, strWarning, strError); if (!strWarning.empty()) { InitWarning(strWarning); } if (!dbV) { InitError(strError); return false; } } return true; } bool WalletInit::Open(const CChainParams &chainParams) const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); return true; } for (const std::string &walletFile : gArgs.GetArgs("-wallet")) { CWallet *const pwallet = CWallet::CreateWalletFromFile( chainParams, walletFile, fs::absolute(walletFile, GetWalletDir())); if (!pwallet) { return false; } - vpwallets.push_back(pwallet); + AddWallet(pwallet); } return true; } void WalletInit::Start(CScheduler &scheduler) const { - for (CWallet *pwallet : vpwallets) { + for (CWallet *pwallet : GetWallets()) { pwallet->postInitProcess(scheduler); } } void WalletInit::Flush() const { - for (CWallet *pwallet : vpwallets) { + for (CWallet *pwallet : GetWallets()) { pwallet->Flush(false); } } void WalletInit::Stop() const { - for (CWallet *pwallet : vpwallets) { + for (CWallet *pwallet : GetWallets()) { pwallet->Flush(true); } } void WalletInit::Close() const { - for (CWallet *pwallet : vpwallets) { + for (CWallet *pwallet : GetWallets()) { + RemoveWallet(pwallet); delete pwallet; } - vpwallets.clear(); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 80de142a1..61d573009 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1,4296 +1,4296 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include // for GetConsensus. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Input src/init.h (not wallet/init.h) for StartShutdown #include #include #include #include static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; static std::string urlDecode(const std::string &urlEncoded) { std::string res; if (!urlEncoded.empty()) { char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr); if (decoded) { res = std::string(decoded); free(decoded); } } return res; } CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest &request) { 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 (CWallet *pwallet : ::vpwallets) { - if (pwallet->GetName() == requestedWallet) { - return pwallet; - } + CWallet *pwallet = GetWallet(requestedWallet); + if (!pwallet) { + throw JSONRPCError( + RPC_WALLET_NOT_FOUND, + "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 pwallet; } - return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) - ? ::vpwallets[0] + + std::vector wallets = GetWallets(); + return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) + ? wallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet *const pwallet) { return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to " "be set with walletpassphrase " "call." : ""; } bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException) { if (pwallet) { return true; } - if (avoidException) { return false; } - - if (::vpwallets.empty()) { + if (GetWallets().empty()) { // Note: It isn't currently possible to trigger this error because // wallet RPC methods aren't registered unless a wallet is loaded. But // this error is being kept as a precaution, because it's possible in // the future that wallet RPC methods might get or remain registered // when no wallets are loaded. throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet " "method is disabled because " "no wallet is loaded)"); } throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED, "Wallet file not specified (must request wallet RPC " "through /wallet/ uri-path)."); } void EnsureWalletIsUnlocked(CWallet *const pwallet) { if (pwallet->IsLocked()) { throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the " "wallet passphrase with " "walletpassphrase first."); } } void WalletTxToJSON(const CWalletTx &wtx, UniValue &entry) { int confirms = wtx.GetDepthInMainChain(); entry.pushKV("confirmations", confirms); if (wtx.IsCoinBase()) { entry.pushKV("generated", true); } if (confirms > 0) { entry.pushKV("blockhash", wtx.hashBlock.GetHex()); entry.pushKV("blockindex", wtx.nIndex); entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); } else { entry.pushKV("trusted", wtx.IsTrusted()); } uint256 hash = wtx.GetId(); entry.pushKV("txid", hash.GetHex()); UniValue conflicts(UniValue::VARR); for (const uint256 &conflict : wtx.GetConflicts()) { conflicts.push_back(conflict.GetHex()); } entry.pushKV("walletconflicts", conflicts); entry.pushKV("time", wtx.GetTxTime()); entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived); for (const std::pair &item : wtx.mapValue) { entry.pushKV(item.first, item.second); } } std::string LabelFromValue(const UniValue &value) { std::string label = value.get_str(); if (label == "*") { throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name"); } return label; } static UniValue getnewaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "getnewaddress ( \"label\" )\n" "\nReturns a new Bitcoin address for receiving payments.\n" "If 'label' is specified, it is added to the address book \n" "so payments received with the address will be associated with " "'label'.\n" "\nArguments:\n" "1. \"label\" (string, optional) The label name for the " "address to be linked to. If not provided, the default label \"\" " "is used. It can also be set to the empty string \"\" to represent " "the default label. The label does not need to exist, it will be " "created if there is no label by the given name.\n" "\nResult:\n" "\"address\" (string) The new bitcoin address\n" "\nExamples:\n" + HelpExampleRpc("getnewaddress", "")); } LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label; if (!request.params[0].isNull()) { label = LabelFromValue(request.params[0]); } OutputType output_type = g_address_type; if (!request.params[1].isNull()) { output_type = ParseOutputType(request.params[1].get_str(), g_address_type); if (output_type == OutputType::NONE) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str())); } } if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); } // Generate a new key that is added to wallet CPubKey newKey; if (!pwallet->GetKeyFromPool(newKey)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } pwallet->LearnRelatedScripts(newKey, output_type); CTxDestination dest = GetDestinationForKey(newKey, output_type); pwallet->SetAddressBook(dest, label, "receive"); return EncodeDestination(dest); } CTxDestination GetLabelDestination(CWallet *const pwallet, const std::string &label, bool bForceNew = false) { CTxDestination dest; if (!pwallet->GetLabelDestination(dest, label, bForceNew)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } return dest; } UniValue getlabeladdress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getlabeladdress \"label\"\n" "\nReturns the current Bitcoin address for receiving payments to " "this label.\n" "\nArguments:\n" "1. \"label\" (string, required) The label name for the " "address. It can also be set to the empty string \"\" to represent " "the default label. The label does not need to exist, it will be " "created and a new address created if there is no label by the " "given name.\n" "\nResult:\n" "\"address\" (string) The label bitcoin address\n" "\nExamples:\n" + HelpExampleCli("getlabeladdress", "") + HelpExampleCli("getlabeladdress", "\"\"") + HelpExampleCli("getlabeladdress", "\"mylabel\"") + HelpExampleRpc("getlabeladdress", "\"mylabel\"")); } LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label = LabelFromValue(request.params[0]); UniValue ret(UniValue::VSTR); ret = EncodeDestination(GetLabelDestination(pwallet, label)); return ret; } static UniValue getrawchangeaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getrawchangeaddress\n" "\nReturns a new Bitcoin address, for receiving change.\n" "This is for use with raw transactions, NOT normal use.\n" "\nResult:\n" "\"address\" (string) The address\n" "\nExamples:\n" + HelpExampleCli("getrawchangeaddress", "") + HelpExampleRpc("getrawchangeaddress", "")); } LOCK2(cs_main, pwallet->cs_wallet); if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); } OutputType output_type = g_change_type; if (!request.params[0].isNull()) { output_type = ParseOutputType(request.params[0].get_str(), g_change_type); if (output_type == OutputType::NONE) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str())); } } CReserveKey reservekey(pwallet); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey, true)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } reservekey.KeepKey(); pwallet->LearnRelatedScripts(vchPubKey, output_type); CTxDestination dest = GetDestinationForKey(vchPubKey, output_type); return EncodeDestination(dest); } UniValue setlabel(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "setlabel \"address\" \"label\"\n" "\nSets the label associated with the given address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "be associated with a label.\n" "2. \"label\" (string, required) The label to assign the " "address to.\n" "\nExamples:\n" + HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + HelpExampleRpc( "setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string label; if (!request.params[1].isNull()) { label = LabelFromValue(request.params[1]); } // Only add the label if the address is yours. if (IsMine(*pwallet, dest)) { // Detect when changing the label of an address that is the 'unused // current key' of another label: if (pwallet->mapAddressBook.count(dest)) { std::string old_label = pwallet->mapAddressBook[dest].name; if (dest == GetLabelDestination(pwallet, old_label)) { GetLabelDestination(pwallet, old_label, true); } } pwallet->SetAddressBook(dest, label, "receive"); } else { throw JSONRPCError(RPC_MISC_ERROR, "setlabel can only be used with own address"); } return NullUniValue; } static UniValue getaccount(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaccount \"address\"\n" "\nDEPRECATED. Returns the account associated with the given " "address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for " "account lookup.\n" "\nResult:\n" "\"accountname\" (string) the account address\n" "\nExamples:\n" + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"")); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string strAccount; std::map::iterator mi = pwallet->mapAddressBook.find(dest); if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; } return strAccount; } static UniValue getaddressesbyaccount(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaddressesbyaccount \"account\"\n" "\nDEPRECATED. Returns the list of addresses for the given " "account.\n" "\nArguments:\n" "1. \"account\" (string, required) The account name.\n" "\nResult:\n" "[ (json array of string)\n" " \"address\" (string) a bitcoin address associated with " "the given account\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"")); } LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = LabelFromValue(request.params[0]); // Find all addresses that have the given account UniValue ret(UniValue::VARR); for (const std::pair &item : pwallet->mapAddressBook) { const CTxDestination &dest = item.first; const std::string &strName = item.second.name; if (strName == strAccount) { ret.push_back(EncodeDestination(dest)); } } return ret; } static CTransactionRef SendMoney(CWallet *const pwallet, const CTxDestination &address, Amount nValue, bool fSubtractFeeFromAmount, mapValue_t mapValue, std::string fromAccount) { Amount curBalance = pwallet->GetBalance(); // Check amount if (nValue <= Amount::zero()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); } if (nValue > curBalance) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); } if (pwallet->GetBroadcastTransactions() && !g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } // Parse Bitcoin address CScript scriptPubKey = GetScriptForDestination(address); // Create and send the transaction CReserveKey reservekey(pwallet); Amount nFeeRequired; std::string strError; std::vector vecSend; int nChangePosRet = -1; CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; vecSend.push_back(recipient); CCoinControl coinControl; CTransactionRef tx; if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coinControl)) { if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) { strError = strprintf("Error: This transaction requires a " "transaction fee of at least %s", FormatMoney(nFeeRequired)); } throw JSONRPCError(RPC_WALLET_ERROR, strError); } CValidationState state; if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, std::move(fromAccount), reservekey, g_connman.get(), state)) { strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); throw JSONRPCError(RPC_WALLET_ERROR, strError); } return tx; } static UniValue sendtoaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) { throw std::runtime_error( "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" " "subtractfeefromamount )\n" "\nSend an amount to a given address.\n" + HelpRequiringPassphrase(pwallet) + "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address " "to send to.\n" "2. \"amount\" (numeric or string, required) The " "amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" "3. \"comment\" (string, optional) A comment used to " "store what the transaction is for. \n" " This is not part of the transaction, " "just kept in your wallet.\n" "4. \"comment_to\" (string, optional) A comment to store " "the name of the person or organization \n" " to which you're sending the " "transaction. This is not part of the \n" " transaction, just kept in your " "wallet.\n" "5. subtractfeefromamount (boolean, optional, default=false) The " "fee will be deducted from the amount being sent.\n" " The recipient will receive less " "bitcoins than you enter in the amount field.\n" "\nResult:\n" "\"txid\" (string) The transaction id.\n" "\nExamples:\n" + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay" "dd\" 0.1 \"donation\" \"seans " "outpost\"") + HelpExampleCli( "sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay" "dd\", 0.1, \"donation\", \"seans " "outpost\"")); } // 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 pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } // Amount Amount nAmount = AmountFromValue(request.params[1]); if (nAmount <= Amount::zero()) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); } // Wallet comments mapValue_t mapValue; if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) { mapValue["comment"] = request.params[2].get_str(); } if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) { mapValue["to"] = request.params[3].get_str(); } bool fSubtractFeeFromAmount = false; if (request.params.size() > 4) { fSubtractFeeFromAmount = request.params[4].get_bool(); } EnsureWalletIsUnlocked(pwallet); CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, std::move(mapValue), {} /* fromAccount */); return tx->GetId().GetHex(); } static UniValue listaddressgroupings(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "listaddressgroupings\n" "\nLists groups of addresses which have had their common " "ownership\n" "made public by common use as inputs or as the resulting change\n" "in past transactions\n" "\nResult:\n" "[\n" " [\n" " [\n" " \"address\", (string) The bitcoin address\n" " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"label\" (string, optional) The label\n" " ]\n" " ,...\n" " ]\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("listaddressgroupings", "") + HelpExampleRpc("listaddressgroupings", "")); } // 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 pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); std::map balances = pwallet->GetAddressBalances(); for (const std::set &grouping : pwallet->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); for (const CTxDestination &address : grouping) { UniValue addressInfo(UniValue::VARR); addressInfo.push_back(EncodeDestination(address)); addressInfo.push_back(ValueFromAmount(balances[address])); if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) { addressInfo.push_back( pwallet->mapAddressBook.find(address)->second.name); } jsonGrouping.push_back(addressInfo); } jsonGroupings.push_back(jsonGrouping); } return jsonGroupings; } static UniValue signmessage(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "signmessage \"address\" \"message\"\n" "\nSign a message with the private key of an address" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "use for the private key.\n" "2. \"message\" (string, required) The message to create a " "signature of.\n" "\nResult:\n" "\"signature\" (string) The signature of the message " "encoded in base 64\n" "\nExamples:\n" "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" + HelpExampleCli( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\" \"signature\" \"my " "message\"") + "\nAs json rpc\n" + HelpExampleRpc( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")); } LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); CTxDestination dest = DecodeDestination(strAddress, config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } const CKeyID *keyID = boost::get(&dest); if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } CKey key; if (!pwallet->GetKey(*keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; std::vector vchSig; if (!key.SignCompact(ss.GetHash(), vchSig)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); } return EncodeBase64(&vchSig[0], vchSig.size()); } static UniValue getreceivedbyaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getreceivedbyaddress \"address\" ( minconf )\n" "\nReturns the total amount received by the given address in " "transactions with at least minconf confirmations.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for " "transactions.\n" "2. minconf (numeric, optional, default=1) Only " "include transactions confirmed at least this many times.\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" "\nExamples:\n" "\nThe amount from transactions with at least 1 confirmation\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + "\nThe amount including unconfirmed transactions, zero " "confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") + "\nThe amount with at least 6 confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + "\nAs a json rpc call\n" + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")); } // 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 pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwallet, scriptPubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet"); } // Minimum confirmations int nMinDepth = 1; if (!request.params[1].isNull()) { nMinDepth = request.params[1].get_int(); } // Tally Amount nAmount = Amount::zero(); for (const std::pair &pairWtx : pwallet->mapWallet) { const CWalletTx &wtx = pairWtx.second; CValidationState state; if (wtx.IsCoinBase() || !ContextualCheckTransactionForCurrentBlock( config, *wtx.tx, state)) { continue; } for (const CTxOut &txout : wtx.tx->vout) { if (txout.scriptPubKey == scriptPubKey) { if (wtx.GetDepthInMainChain() >= nMinDepth) { nAmount += txout.nValue; } } } } return ValueFromAmount(nAmount); } UniValue getreceivedbylabel(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getreceivedbylabel \"label\" ( minconf )\n" "\nReturns the total amount received by addresses with