]> Git Repo - VerusCoin.git/blame - src/qt/walletmodel.cpp
Merge pull request #3430 from ldenman/patch-1
[VerusCoin.git] / src / qt / walletmodel.cpp
CommitLineData
e592d43f
WL
1// Copyright (c) 2011-2013 The Bitcoin developers
2// Distributed under the MIT/X11 software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
ef079e18 5#include "walletmodel.h"
51ed9ec9 6
ef079e18 7#include "addresstablemodel.h"
51ed9ec9 8#include "guiconstants.h"
ef079e18 9#include "transactiontablemodel.h"
666893b1 10#include "recentrequeststablemodel.h"
ef079e18 11
51ed9ec9
BD
12#include "base58.h"
13#include "db.h"
14#include "keystore.h"
15#include "main.h"
16#include "sync.h"
6b6aaa16 17#include "ui_interface.h"
51ed9ec9 18#include "wallet.h"
9eace6b1 19#include "walletdb.h" // for BackupWallet
ef079e18 20
51ed9ec9
BD
21#include <stdint.h>
22
23#include <QDebug>
a5e6d723 24#include <QSet>
6c83a841 25#include <QTimer>
ef079e18 26
ee014e5b
WL
27WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) :
28 QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0),
5df0b03c 29 transactionTableModel(0),
666893b1 30 recentRequestsTableModel(0),
8fdb7e10 31 cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
32 cachedNumTransactions(0),
6c83a841
SE
33 cachedEncryptionStatus(Unencrypted),
34 cachedNumBlocks(0)
ef079e18 35{
ef079e18
WL
36 addressTableModel = new AddressTableModel(wallet, this);
37 transactionTableModel = new TransactionTableModel(wallet, this);
666893b1 38 recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
ab1b288f 39
2e00b8fb 40 // This timer will be fired repeatedly to update the balance
6c83a841 41 pollTimer = new QTimer(this);
6c83a841 42 connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollBalanceChanged()));
2e00b8fb 43 pollTimer->start(MODEL_UPDATE_DELAY);
6c83a841 44
ab1b288f
WL
45 subscribeToCoreSignals();
46}
47
48WalletModel::~WalletModel()
49{
50 unsubscribeFromCoreSignals();
ef079e18
WL
51}
52
6a86c24d 53qint64 WalletModel::getBalance(const CCoinControl *coinControl) const
ef079e18 54{
6a86c24d
CL
55 if (coinControl)
56 {
57 qint64 nBalance = 0;
58 std::vector<COutput> vCoins;
59 wallet->AvailableCoins(vCoins, true, coinControl);
60 BOOST_FOREACH(const COutput& out, vCoins)
61 nBalance += out.tx->vout[out.i].nValue;
62
63 return nBalance;
64 }
65
ef079e18
WL
66 return wallet->GetBalance();
67}
68
df5ccbd2
WL
69qint64 WalletModel::getUnconfirmedBalance() const
70{
71 return wallet->GetUnconfirmedBalance();
72}
73
8fdb7e10 74qint64 WalletModel::getImmatureBalance() const
75{
76 return wallet->GetImmatureBalance();
77}
78
ef079e18
WL
79int WalletModel::getNumTransactions() const
80{
81 int numTransactions = 0;
ef079e18 82 {
f8dcd5ca 83 LOCK(wallet->cs_wallet);
abf11f79
PK
84 // the size of mapWallet contains the number of unique transaction IDs
85 // (e.g. payments to yourself generate 2 transactions, but both share the same transaction ID)
ef079e18
WL
86 numTransactions = wallet->mapWallet.size();
87 }
88 return numTransactions;
89}
90
fe4a6550 91void WalletModel::updateStatus()
ef079e18 92{
fe4a6550
WL
93 EncryptionStatus newEncryptionStatus = getEncryptionStatus();
94
95 if(cachedEncryptionStatus != newEncryptionStatus)
96 emit encryptionStatusChanged(newEncryptionStatus);
97}
98
6c83a841 99void WalletModel::pollBalanceChanged()
fe4a6550 100{
4c6d41b8 101 if(chainActive.Height() != cachedNumBlocks)
2e00b8fb
WL
102 {
103 // Balance and number of transactions might have changed
4c6d41b8 104 cachedNumBlocks = chainActive.Height();
6c83a841
SE
105 checkBalanceChanged();
106 }
6c83a841
SE
107}
108
109void WalletModel::checkBalanceChanged()
110{
5df0b03c
WL
111 qint64 newBalance = getBalance();
112 qint64 newUnconfirmedBalance = getUnconfirmedBalance();
8fdb7e10 113 qint64 newImmatureBalance = getImmatureBalance();
5df0b03c 114
2e00b8fb
WL
115 if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
116 {
6c83a841
SE
117 cachedBalance = newBalance;
118 cachedUnconfirmedBalance = newUnconfirmedBalance;
119 cachedImmatureBalance = newImmatureBalance;
8fdb7e10 120 emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance);
6c83a841
SE
121 }
122}
8fdb7e10 123
6c83a841
SE
124void WalletModel::updateTransaction(const QString &hash, int status)
125{
126 if(transactionTableModel)
127 transactionTableModel->updateTransaction(hash, status);
5df0b03c 128
6c83a841
SE
129 // Balance and number of transactions might have changed
130 checkBalanceChanged();
131
6c83a841 132 int newNumTransactions = getNumTransactions();
2e00b8fb
WL
133 if(cachedNumTransactions != newNumTransactions)
134 {
6c83a841 135 cachedNumTransactions = newNumTransactions;
2e00b8fb 136 emit numTransactionsChanged(newNumTransactions);
6c83a841 137 }
ef079e18
WL
138}
139
42018eff 140void WalletModel::updateAddressBook(const QString &address, const QString &label,
dcd0b077 141 bool isMine, const QString &purpose, int status)
98e61758 142{
fe4a6550 143 if(addressTableModel)
dcd0b077 144 addressTableModel->updateEntry(address, label, isMine, purpose, status);
98e61758
WL
145}
146
a5e6d723 147bool WalletModel::validateAddress(const QString &address)
ef079e18 148{
491ad6db
WL
149 CBitcoinAddress addressParsed(address.toStdString());
150 return addressParsed.IsValid();
a5e6d723
WL
151}
152
6a86c24d 153WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl)
a5e6d723
WL
154{
155 qint64 total = 0;
9e8904f6 156 QList<SendCoinsRecipient> recipients = transaction.getRecipients();
51ed9ec9 157 std::vector<std::pair<CScript, int64_t> > vecSend;
a5e6d723
WL
158
159 if(recipients.empty())
ef079e18 160 {
a5e6d723 161 return OK;
ef079e18
WL
162 }
163
a41d5fe0
GA
164 QSet<QString> setAddress; // Used to detect duplicates
165 int nAddresses = 0;
166
a5e6d723
WL
167 // Pre-check input data for validity
168 foreach(const SendCoinsRecipient &rcp, recipients)
ef079e18 169 {
a41d5fe0 170 if (rcp.paymentRequest.IsInitialized())
d5f0ef54 171 { // PaymentRequest...
51ed9ec9 172 int64_t subtotal = 0;
a41d5fe0
GA
173 const payments::PaymentDetails& details = rcp.paymentRequest.getDetails();
174 for (int i = 0; i < details.outputs_size(); i++)
175 {
176 const payments::Output& out = details.outputs(i);
177 if (out.amount() <= 0) continue;
178 subtotal += out.amount();
179 const unsigned char* scriptStr = (const unsigned char*)out.script().data();
180 CScript scriptPubKey(scriptStr, scriptStr+out.script().size());
51ed9ec9 181 vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, out.amount()));
a41d5fe0
GA
182 }
183 if (subtotal <= 0)
184 {
185 return InvalidAmount;
186 }
187 total += subtotal;
a5e6d723 188 }
a41d5fe0
GA
189 else
190 { // User-entered bitcoin address / amount:
191 if(!validateAddress(rcp.address))
192 {
193 return InvalidAddress;
194 }
195 if(rcp.amount <= 0)
196 {
197 return InvalidAmount;
198 }
199 setAddress.insert(rcp.address);
200 ++nAddresses;
a5e6d723 201
a41d5fe0
GA
202 CScript scriptPubKey;
203 scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
51ed9ec9 204 vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount));
a41d5fe0
GA
205
206 total += rcp.amount;
a5e6d723 207 }
ef079e18 208 }
a41d5fe0 209 if(setAddress.size() != nAddresses)
a5e6d723
WL
210 {
211 return DuplicateAddress;
212 }
213
6a86c24d
CL
214 qint64 nBalance = getBalance(coinControl);
215
216 if(total > nBalance)
ef079e18
WL
217 {
218 return AmountExceedsBalance;
219 }
220
6a86c24d 221 if((total + nTransactionFee) > nBalance)
ef079e18 222 {
9e8904f6
JS
223 transaction.setTransactionFee(nTransactionFee);
224 return SendCoinsReturn(AmountWithFeeExceedsBalance);
ef079e18
WL
225 }
226
ef079e18 227 {
f8dcd5ca
PW
228 LOCK2(cs_main, wallet->cs_wallet);
229
9e8904f6 230 transaction.newPossibleKeyChange(wallet);
51ed9ec9 231 int64_t nFeeRequired = 0;
1f00f4e9 232 std::string strFailReason;
9e8904f6
JS
233
234 CWalletTx *newTx = transaction.getTransaction();
235 CReserveKey *keyChange = transaction.getPossibleKeyChange();
6a86c24d 236 bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl);
9e8904f6 237 transaction.setTransactionFee(nFeeRequired);
ef079e18 238
a5e6d723 239 if(!fCreated)
ef079e18 240 {
6a86c24d 241 if((total + nFeeRequired) > nBalance)
a5e6d723 242 {
9e8904f6 243 return SendCoinsReturn(AmountWithFeeExceedsBalance);
a5e6d723 244 }
1f00f4e9
GA
245 emit message(tr("Send Coins"), QString::fromStdString(strFailReason),
246 CClientUIInterface::MSG_ERROR);
a5e6d723 247 return TransactionCreationFailed;
ef079e18 248 }
9e8904f6
JS
249 }
250
251 return SendCoinsReturn(OK);
252}
253
254WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction)
255{
256 QByteArray transaction_array; /* store serialized transaction */
257
258 {
259 LOCK2(cs_main, wallet->cs_wallet);
260 CWalletTx *newTx = transaction.getTransaction();
261
a41d5fe0 262 // Store PaymentRequests in wtx.vOrderForm in wallet.
9e8904f6 263 foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
a41d5fe0
GA
264 {
265 if (rcp.paymentRequest.IsInitialized())
266 {
267 std::string key("PaymentRequest");
268 std::string value;
269 rcp.paymentRequest.SerializeToString(&value);
9e8904f6 270 newTx->vOrderForm.push_back(make_pair(key, value));
a41d5fe0 271 }
ef079e18 272 }
9e8904f6
JS
273
274 CReserveKey *keyChange = transaction.getPossibleKeyChange();
275 if(!wallet->CommitTransaction(*newTx, *keyChange))
a5e6d723 276 return TransactionCommitFailed;
a41d5fe0 277
9e8904f6 278 CTransaction* t = (CTransaction*)newTx;
a41d5fe0
GA
279 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
280 ssTx << *t;
9e8904f6 281 transaction_array.append(&(ssTx[0]), ssTx.size());
ef079e18
WL
282 }
283
a41d5fe0 284 // Add addresses / update labels that we've sent to to the address book,
9e8904f6
JS
285 // and emit coinsSent signal for each recipient
286 foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
ef079e18 287 {
c6c97e0f
PK
288 // Don't touch the address book when we have a payment request
289 if (!rcp.paymentRequest.IsInitialized())
a5e6d723 290 {
48c01148
PK
291 std::string strAddress = rcp.address.toStdString();
292 CTxDestination dest = CBitcoinAddress(strAddress).Get();
293 std::string strLabel = rcp.label.toStdString();
dab7acdf 294 {
48c01148
PK
295 LOCK(wallet->cs_wallet);
296
297 std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest);
298
299 // Check if we have a new address or an updated label
300 if (mi == wallet->mapAddressBook.end())
301 {
302 wallet->SetAddressBook(dest, strLabel, "send");
303 }
304 else if (mi->second.name != strLabel)
305 {
306 wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
307 }
dab7acdf 308 }
a5e6d723 309 }
9e8904f6 310 emit coinsSent(wallet, rcp, transaction_array);
ef079e18 311 }
a5e13258 312
9e8904f6 313 return SendCoinsReturn(OK);
ef079e18
WL
314}
315
316OptionsModel *WalletModel::getOptionsModel()
317{
318 return optionsModel;
319}
320
321AddressTableModel *WalletModel::getAddressTableModel()
322{
323 return addressTableModel;
324}
325
326TransactionTableModel *WalletModel::getTransactionTableModel()
327{
328 return transactionTableModel;
329}
ebff5c40 330
666893b1
WL
331RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel()
332{
333 return recentRequestsTableModel;
334}
335
ae8adeb9
WL
336WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
337{
338 if(!wallet->IsCrypted())
339 {
340 return Unencrypted;
341 }
342 else if(wallet->IsLocked())
343 {
344 return Locked;
345 }
346 else
347 {
348 return Unlocked;
349 }
350}
b7bcaf94 351
94f778bd 352bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
b7bcaf94
WL
353{
354 if(encrypted)
355 {
356 // Encrypt
357 return wallet->EncryptWallet(passphrase);
358 }
359 else
360 {
361 // Decrypt -- TODO; not supported yet
362 return false;
363 }
364}
365
94f778bd 366bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
b7bcaf94
WL
367{
368 if(locked)
369 {
370 // Lock
371 return wallet->Lock();
372 }
373 else
374 {
375 // Unlock
376 return wallet->Unlock(passPhrase);
377 }
378}
379
94f778bd 380bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
b7bcaf94
WL
381{
382 bool retval;
b7bcaf94 383 {
f8dcd5ca 384 LOCK(wallet->cs_wallet);
b7bcaf94
WL
385 wallet->Lock(); // Make sure wallet is locked before attempting pass change
386 retval = wallet->ChangeWalletPassphrase(oldPass, newPass);
387 }
388 return retval;
389}
390
4efbda3f 391bool WalletModel::backupWallet(const QString &filename)
392{
393 return BackupWallet(*wallet, filename.toLocal8Bit().data());
394}
395
ab1b288f
WL
396// Handlers for core signals
397static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet)
398{
42018eff 399 qDebug() << "NotifyKeyStoreStatusChanged";
ab1b288f
WL
400 QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
401}
402
dcd0b077
WL
403static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
404 const CTxDestination &address, const std::string &label, bool isMine,
405 const std::string &purpose, ChangeType status)
ab1b288f 406{
42018eff
PK
407 QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString());
408 QString strLabel = QString::fromStdString(label);
409 QString strPurpose = QString::fromStdString(purpose);
410
411 qDebug() << "NotifyAddressBookChanged : " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
ab1b288f 412 QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
42018eff
PK
413 Q_ARG(QString, strAddress),
414 Q_ARG(QString, strLabel),
0832c0d1 415 Q_ARG(bool, isMine),
42018eff 416 Q_ARG(QString, strPurpose),
ab1b288f
WL
417 Q_ARG(int, status));
418}
419
420static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status)
421{
42018eff
PK
422 QString strHash = QString::fromStdString(hash.GetHex());
423
424 qDebug() << "NotifyTransactionChanged : " + strHash + " status= " + QString::number(status);
ab1b288f 425 QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection,
42018eff 426 Q_ARG(QString, strHash),
ab1b288f
WL
427 Q_ARG(int, status));
428}
429
430void WalletModel::subscribeToCoreSignals()
431{
432 // Connect signals to wallet
433 wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
dcd0b077 434 wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
ab1b288f
WL
435 wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
436}
437
438void WalletModel::unsubscribeFromCoreSignals()
439{
440 // Disconnect signals from wallet
441 wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
dcd0b077 442 wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
ab1b288f
WL
443 wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
444}
445
b7bcaf94
WL
446// WalletModel::UnlockContext implementation
447WalletModel::UnlockContext WalletModel::requestUnlock()
448{
449 bool was_locked = getEncryptionStatus() == Locked;
450 if(was_locked)
451 {
452 // Request UI to unlock wallet
453 emit requireUnlock();
454 }
455 // If wallet is still locked, unlock was failed or cancelled, mark context as invalid
456 bool valid = getEncryptionStatus() != Locked;
457
458 return UnlockContext(this, valid, was_locked);
459}
460
461WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock):
462 wallet(wallet),
463 valid(valid),
464 relock(relock)
465{
466}
467
468WalletModel::UnlockContext::~UnlockContext()
469{
470 if(valid && relock)
471 {
472 wallet->setWalletLocked(true);
473 }
474}
475
476void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
477{
478 // Transfer context; old object no longer relocks wallet
479 *this = rhs;
480 rhs.relock = false;
481}
6a86c24d
CL
482
483bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
484{
485 return wallet->GetPubKey(address, vchPubKeyOut);
486}
487
488// returns a list of COutputs from COutPoints
489void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
490{
491 BOOST_FOREACH(const COutPoint& outpoint, vOutpoints)
492 {
493 if (!wallet->mapWallet.count(outpoint.hash)) continue;
494 COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain());
495 vOutputs.push_back(out);
496 }
497}
498
499// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address)
500void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
501{
502 std::vector<COutput> vCoins;
503 wallet->AvailableCoins(vCoins);
504
505 std::vector<COutPoint> vLockedCoins;
506 wallet->ListLockedCoins(vLockedCoins);
507
508 // add locked coins
509 BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins)
510 {
511 if (!wallet->mapWallet.count(outpoint.hash)) continue;
512 COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain());
513 vCoins.push_back(out);
514 }
515
516 BOOST_FOREACH(const COutput& out, vCoins)
517 {
518 COutput cout = out;
519
520 while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0]))
521 {
522 if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break;
523 cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0);
524 }
525
526 CTxDestination address;
527 if(!ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) continue;
528 mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out);
529 }
530}
531
532bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const
533{
534 return wallet->IsLockedCoin(hash, n);
535}
536
537void WalletModel::lockCoin(COutPoint& output)
538{
539 wallet->LockCoin(output);
540}
541
542void WalletModel::unlockCoin(COutPoint& output)
543{
544 wallet->UnlockCoin(output);
545}
546
547void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
548{
549 wallet->ListLockedCoins(vOutpts);
550}
This page took 0.228425 seconds and 4 git commands to generate.