{ "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 },
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);
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()
{
{
// 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
{
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<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap;
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)
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")
private slots:
void updateDisplayUnit();
void handleTransactionClicked(const QModelIndex &index);
+ void updateWatchOnlyLabels(bool showWatchOnly);
};
#endif // OVERVIEWPAGE_H
{
QList<TransactionRecord> 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;
cachedEncryptionStatus(Unencrypted),
cachedNumBlocks(0)
{
+ fHaveWatchOnly = wallet->HaveWatchOnly();
+
addressTableModel = new AddressTableModel(wallet, this);
mintingTableModel = new MintingTableModel(wallet, this);
transactionTableModel = new TransactionTableModel(wallet, this);
unsubscribeFromCoreSignals();
}
+bool WalletModel::haveWatchOnly() const
+{
+ return fHaveWatchOnly;
+}
+
qint64 WalletModel::getBalance() const
{
return wallet->GetBalance();
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());
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()
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
{
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)
MintingTableModel *getMintingTableModel();
TransactionTableModel *getTransactionTableModel();
+ bool haveWatchOnly() const;
qint64 getBalance() const;
qint64 getBalanceWatchOnly() const;
qint64 getStake() const;
private:
CWallet *wallet;
+ bool fHaveWatchOnly;
// Wallet has an options model for wallet-specific options
// (transaction fee, for example)
void unsubscribeFromCoreSignals();
void checkBalanceChanged();
-
public slots:
/* Wallet status might have changed */
void updateStatus();
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();
// Asynchronous error notification
void error(const QString &title, const QString &message, bool modal);
+
+ // Watch-only address added
+ void notifyWatchonlyChanged(bool fHaveWatchonly);
};
key.SetSecret(secret, fCompressed);
CKeyID vchAddress = key.GetPubKey().GetID();
{
- LOCK2(cs_main, pwalletMain->cs_wallet);
-
pwalletMain->MarkDirty();
pwalletMain->SetAddressBookName(vchAddress, strLabel);
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))
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<unsigned char> 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)
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
+
+ // check if we need to remove from watch-only
+ CScript script;
+ script.SetDestination(vchPubKey.GetID());
+ if (HaveWatchOnly(script))
+ RemoveWatchOnly(script);
+
if (!fFileBacked)
return true;
{
if (!CCryptoKeyStore::AddWatchOnly(dest))
return false;
nTimeFirstKey = 1; // No birthday information for watch-only keys.
+ NotifyWatchonlyChanged(true);
if (!fFileBacked)
return true;
return CWalletDB(strWalletFile).WriteWatchOnly(dest);
}
+bool CWallet::RemoveWatchOnly(const CScript &dest)
+{
+ LOCK(cs_wallet);
+ if (!CCryptoKeyStore::RemoveWatchOnly(dest))
+ return false;
+ if (!HaveWatchOnly())
+ NotifyWatchonlyChanged(false);
+ if (fFileBacked)
+ if (!CWalletDB(strWalletFile).EraseWatchOnly(dest))
+ return false;
+
+ return true;
+}
+
bool CWallet::LoadWatchOnly(const CScript &dest)
{
return CCryptoKeyStore::AddWatchOnly(dest);
printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str());
else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
{
- printf("WalletUpdateSpent found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ printf("WalletUpdateSpent found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit(MINE_ALL)).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkSpent(txin.prevout.n);
wtx.WriteToDisk();
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
}
if (fUpdated)
{
- printf("ReacceptWalletTransactions found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ printf("ReacceptWalletTransactions found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit(MINE_ALL)).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkDirty();
wtx.WriteToDisk();
}
if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash()))
{
CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()];
- printf(" mine: %d %d %" PRId64 "", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
+ printf(" mine: %d %d %" PRId64 "", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit(MINE_ALL));
}
if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash()))
{
CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()];
- printf(" stake: %d %d %" PRId64 "", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
+ printf(" stake: %d %d %" PRId64 "", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit(MINE_ALL));
}
}
// Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript &dest);
+ bool RemoveWatchOnly(const CScript &dest);
// Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
* @note called with lock cs_wallet held.
*/
boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx, ChangeType status)> NotifyTransactionChanged;
+
+ /** Watch-only address added */
+ boost::signals2::signal<void (bool fHaveWatchOnly)> NotifyWatchonlyChanged;
};
/** A key allocated from the key pool. */
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
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++;