From 48564f02c4769f26295bedfc04b796246ad0d0bc Mon Sep 17 00:00:00 2001 From: fsb4000 Date: Wed, 31 Dec 2014 08:58:04 +0600 Subject: [PATCH] Add removeaddress RPC call --- src/bitcoinrpc.cpp | 1 + src/bitcoinrpc.h | 1 + src/keystore.cpp | 14 +++++++++++++ src/keystore.h | 4 +++ src/qt/overviewpage.cpp | 11 ++++++++++ src/qt/overviewpage.h | 1 + src/qt/transactionrecord.cpp | 2 +- src/qt/walletmodel.cpp | 24 ++++++++++++++++++++++- src/qt/walletmodel.h | 8 ++++++- src/rpcdump.cpp | 43 +++++++++++++++++++++++++++++++++++++++-- src/wallet.cpp | 30 +++++++++++++++++++++++++--- src/wallet.h | 37 +++++++++++++++++++++++++++-------- src/walletdb.h | 6 +++++ 13 files changed, 163 insertions(+), 19 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 899c381..86c062d 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -295,6 +295,7 @@ static const CRPCCommand vRPCCommands[] = { "importwallet", &importwallet, false, false }, { "importprivkey", &importprivkey, false, false }, { "importaddress", &importaddress, false, true }, + { "removeaddress", &removeaddress, false, true }, { "listunspent", &listunspent, false, false }, { "getrawtransaction", &getrawtransaction, false, false }, { "createrawtransaction", &createrawtransaction, false, false }, diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 8da6bbb..72f7128 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -153,6 +153,7 @@ extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fH extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value removeaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendalert(const json_spirit::Array& params, bool fHelp); diff --git a/src/keystore.cpp b/src/keystore.cpp index b4783ec..5f07bb1 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -79,12 +79,26 @@ bool CBasicKeyStore::AddWatchOnly(const CScript &dest) return true; } + +bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest) +{ + LOCK(cs_KeyStore); + setWatchOnly.erase(dest); + return true; +} + bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const { LOCK(cs_KeyStore); return setWatchOnly.count(dest) > 0; } +bool CBasicKeyStore::HaveWatchOnly() const +{ + LOCK(cs_KeyStore); + return (!setWatchOnly.empty()); +} + bool CCryptoKeyStore::SetCrypted() { { diff --git a/src/keystore.h b/src/keystore.h index 34d94ef..9573e3f 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -52,7 +52,9 @@ public: // Support for Watch-only addresses virtual bool AddWatchOnly(const CScript &dest) =0; + virtual bool RemoveWatchOnly(const CScript &dest) =0; virtual bool HaveWatchOnly(const CScript &dest) const =0; + virtual bool HaveWatchOnly() const =0; virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const { @@ -119,7 +121,9 @@ public: virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; virtual bool AddWatchOnly(const CScript &dest); + virtual bool RemoveWatchOnly(const CScript &dest); virtual bool HaveWatchOnly(const CScript &dest) const; + virtual bool HaveWatchOnly() const; }; typedef std::map > > CryptedKeyMap; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 59cb0ac..21cdba0 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -158,6 +158,14 @@ void OverviewPage::setBalance(qint64 total, qint64 watchOnly, qint64 stake, qint bool showWatchOnly = watchOnly != 0; ui->labelBalanceWatchOnly->setVisible(showWatchOnly); ui->labelBalanceWatchOnlyText->setVisible(showWatchOnly); + +} + +// show/hide watch-only labels +void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly) +{ + ui->labelBalanceWatchOnly->setVisible(showWatchOnly); + ui->labelBalanceWatchOnlyText->setVisible(showWatchOnly); } void OverviewPage::setNumTransactions(int count) @@ -190,6 +198,9 @@ void OverviewPage::setModel(WalletModel *model) connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + updateWatchOnlyLabels(model->haveWatchOnly()); + connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); } // update the display unit, to not use the default ("BTC") diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 10fa79a..fe3c19e 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -48,6 +48,7 @@ private: private slots: void updateDisplayUnit(); void handleTransactionClicked(const QModelIndex &index); + void updateWatchOnlyLabels(bool showWatchOnly); }; #endif // OVERVIEWPAGE_H diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 5df2c3d..34d3edf 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -25,7 +25,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * { QList parts; int64_t nTime = wtx.GetTxTime(); - int64_t nCredit = wtx.GetCredit(true); + int64_t nCredit = wtx.GetCredit(MINE_ALL); int64_t nDebit = wtx.GetDebit(MINE_ALL); int64_t nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(), hashPrev = 0; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 54f806a..baa8e7e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -21,6 +21,8 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { + fHaveWatchOnly = wallet->HaveWatchOnly(); + addressTableModel = new AddressTableModel(wallet, this); mintingTableModel = new MintingTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); @@ -38,6 +40,11 @@ WalletModel::~WalletModel() unsubscribeFromCoreSignals(); } +bool WalletModel::haveWatchOnly() const +{ + return fHaveWatchOnly; +} + qint64 WalletModel::getBalance() const { return wallet->GetBalance(); @@ -130,6 +137,12 @@ void WalletModel::updateAddressBook(const QString &address, const QString &label addressTableModel->updateEntry(address, label, isMine, status); } +void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) +{ + fHaveWatchOnly = fHaveWatchonly; + emit notifyWatchonlyChanged(fHaveWatchonly); +} + bool WalletModel::validateAddress(const QString &address) { CBitcoinAddress addressParsed(address.toStdString()); @@ -369,12 +382,19 @@ static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, Q_ARG(int, status)); } +static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly) +{ + QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection, + Q_ARG(bool, fHaveWatchonly)); +} + void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1)); } void WalletModel::unsubscribeFromCoreSignals() @@ -383,6 +403,7 @@ void WalletModel::unsubscribeFromCoreSignals() wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); + wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1)); } // WalletModel::UnlockContext implementation @@ -457,7 +478,8 @@ void WalletModel::listCoins(std::map >& mapCoins) { if (!wallet->mapWallet.count(outpoint.hash)) continue; COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain(), true); - vCoins.push_back(out); + if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == MINE_SPENDABLE) + vCoins.push_back(out); } BOOST_FOREACH(const COutput& out, vCoins) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 99648d1..f00ce70 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -65,6 +65,7 @@ public: MintingTableModel *getMintingTableModel(); TransactionTableModel *getTransactionTableModel(); + bool haveWatchOnly() const; qint64 getBalance() const; qint64 getBalanceWatchOnly() const; qint64 getStake() const; @@ -139,6 +140,7 @@ public: private: CWallet *wallet; + bool fHaveWatchOnly; // Wallet has an options model for wallet-specific options // (transaction fee, for example) @@ -163,7 +165,6 @@ private: void unsubscribeFromCoreSignals(); void checkBalanceChanged(); - public slots: /* Wallet status might have changed */ void updateStatus(); @@ -171,6 +172,8 @@ public slots: void updateTransaction(const QString &hash, int status); /* New, updated or removed address book entry */ void updateAddressBook(const QString &address, const QString &label, bool isMine, int status); + /* Watchonly added */ + void updateWatchOnlyFlag(bool fHaveWatchonly); /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ void pollBalanceChanged(); @@ -191,6 +194,9 @@ signals: // Asynchronous error notification void error(const QString &title, const QString &message, bool modal); + + // Watch-only address added + void notifyWatchonlyChanged(bool fHaveWatchonly); }; diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index d42c45b..b734eb0 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -56,8 +56,6 @@ Value importprivkey(const Array& params, bool fHelp) key.SetSecret(secret, fCompressed); CKeyID vchAddress = key.GetPubKey().GetID(); { - LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->MarkDirty(); pwalletMain->SetAddressBookName(vchAddress, strLabel); @@ -99,7 +97,8 @@ Value importaddress(const Array& params, bool fHelp) fRescan = params[2].get_bool(); { - LOCK2(cs_main, pwalletMain->cs_wallet); + if (::IsMine(*pwalletMain, script) == MINE_SPENDABLE) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); // Don't throw error in case an address is already there if (pwalletMain->HaveWatchOnly(script)) @@ -123,6 +122,44 @@ Value importaddress(const Array& params, bool fHelp) return Value::null; } +Value removeaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "removeaddress 'address'\n" + "\nRemoves watch-only address or script (in hex) added by importaddress.\n" + "\nArguments:\n" + "1. 'address' (string, required) The address\n" + "\nExamples:\n" + "\nremoveaddress 4EqHMPgEAf56CQmU6ZWS8Ug4d7N3gsQVQA\n" + "\nRemove watch-only address 4EqHMPgEAf56CQmU6ZWS8Ug4d7N3gsQVQA\n"); + + CScript script; + + CBitcoinAddress address(params[0].get_str()); + if (address.IsValid()) { + script.SetDestination(address.Get()); + } else if (IsHex(params[0].get_str())) { + std::vector data(ParseHex(params[0].get_str())); + script = CScript(data.begin(), data.end()); + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script"); + } + + if (::IsMine(*pwalletMain, script) == MINE_SPENDABLE) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet contains the private key for this address or script - can't remove it"); + + if (!pwalletMain->HaveWatchOnly(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet does not contain this address or script"); + + pwalletMain->MarkDirty(); + + if (!pwalletMain->RemoveWatchOnly(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error removing address from wallet"); + + return Value::null; +} + Value importwallet(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) diff --git a/src/wallet.cpp b/src/wallet.cpp index 3ad87cb..f0a980a 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -76,6 +76,13 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector NotifyTransactionChanged; + + /** Watch-only address added */ + boost::signals2::signal NotifyWatchonlyChanged; }; /** A key allocated from the key pool. */ @@ -647,22 +651,37 @@ public: return nDebit; } - int64_t GetCredit(bool fUseCache=true) const + int64_t GetCredit(const isminefilter& filter) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) return 0; - // GetBalance can assume transactions in mapWallet won't change - if (fUseCache) { + int64_t credit = 0; + if (filter & MINE_SPENDABLE) + { + // GetBalance can assume transactions in mapWallet won't change if (fCreditCached) - return nCreditCached; + credit += nCreditCached; + else + { + nCreditCached = pwallet->GetCredit(*this, MINE_SPENDABLE); + fCreditCached = true; + credit += nCreditCached; + } } - - nCreditCached = pwallet->GetCredit(*this, MINE_ALL); - fCreditCached = true; - - return nCreditCached; + if (filter & MINE_WATCH_ONLY) + { + if (fWatchCreditCached) + credit += nWatchCreditCached; + else + { + nWatchCreditCached = pwallet->GetCredit(*this, MINE_WATCH_ONLY); + fWatchCreditCached = true; + credit += nWatchCreditCached; + } + } + return credit; } int64_t GetImmatureCredit(bool fUseCache=true) const diff --git a/src/walletdb.h b/src/walletdb.h index 9d57c93..c65d776 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -129,6 +129,12 @@ public: return Write(std::make_pair(std::string("watchs"), dest), '1'); } + bool CWalletDB::EraseWatchOnly(const CScript &dest) + { + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("watchs"), dest)); + } + bool WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; -- 1.7.1