Merge commit '293f264' into 0.6.0.x
[novacoin.git] / src / qt / walletmodel.cpp
1 #include "walletmodel.h"
2 #include "guiconstants.h"
3 #include "optionsmodel.h"
4 #include "addresstablemodel.h"
5 #include "transactiontablemodel.h"
6
7 #include "headers.h"
8 #include "db.h" // for BackupWallet
9
10 #include <QTimer>
11 #include <QSet>
12
13 WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) :
14     QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0),
15     transactionTableModel(0),
16     cachedBalance(0), cachedUnconfirmedBalance(0), cachedNumTransactions(0),
17     cachedEncryptionStatus(Unencrypted)
18 {
19     // Until signal notifications is built into the bitcoin core,
20     //  simply update everything after polling using a timer.
21     QTimer *timer = new QTimer(this);
22     connect(timer, SIGNAL(timeout()), this, SLOT(update()));
23     timer->start(MODEL_UPDATE_DELAY);
24
25     addressTableModel = new AddressTableModel(wallet, this);
26     transactionTableModel = new TransactionTableModel(wallet, this);
27 }
28
29 qint64 WalletModel::getBalance() const
30 {
31     return wallet->GetBalance();
32 }
33
34 qint64 WalletModel::getUnconfirmedBalance() const
35 {
36     return wallet->GetUnconfirmedBalance();
37 }
38
39 int WalletModel::getNumTransactions() const
40 {
41     int numTransactions = 0;
42     CRITICAL_BLOCK(wallet->cs_wallet)
43     {
44         numTransactions = wallet->mapWallet.size();
45     }
46     return numTransactions;
47 }
48
49 void WalletModel::update()
50 {
51     qint64 newBalance = getBalance();
52     qint64 newUnconfirmedBalance = getUnconfirmedBalance();
53     int newNumTransactions = getNumTransactions();
54     EncryptionStatus newEncryptionStatus = getEncryptionStatus();
55
56     if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance)
57         emit balanceChanged(newBalance, newUnconfirmedBalance);
58
59     if(cachedNumTransactions != newNumTransactions)
60         emit numTransactionsChanged(newNumTransactions);
61
62     if(cachedEncryptionStatus != newEncryptionStatus)
63         emit encryptionStatusChanged(newEncryptionStatus);
64
65     cachedBalance = newBalance;
66     cachedUnconfirmedBalance = newUnconfirmedBalance;
67     cachedNumTransactions = newNumTransactions;
68
69     addressTableModel->update();
70 }
71
72 bool WalletModel::validateAddress(const QString &address)
73 {
74     CBitcoinAddress addressParsed(address.toStdString());
75     return addressParsed.IsValid();
76 }
77
78 WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients)
79 {
80     qint64 total = 0;
81     QSet<QString> setAddress;
82     QString hex;
83
84     if(recipients.empty())
85     {
86         return OK;
87     }
88
89     // Pre-check input data for validity
90     foreach(const SendCoinsRecipient &rcp, recipients)
91     {
92         if(!validateAddress(rcp.address))
93         {
94             return InvalidAddress;
95         }
96         setAddress.insert(rcp.address);
97
98         if(rcp.amount <= 0)
99         {
100             return InvalidAmount;
101         }
102         total += rcp.amount;
103     }
104
105     if(recipients.size() > setAddress.size())
106     {
107         return DuplicateAddress;
108     }
109
110     if(total > getBalance())
111     {
112         return AmountExceedsBalance;
113     }
114
115     if((total + nTransactionFee) > getBalance())
116     {
117         return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee);
118     }
119
120     CRITICAL_BLOCK(cs_main)
121     CRITICAL_BLOCK(wallet->cs_wallet)
122     {
123         // Sendmany
124         std::vector<std::pair<CScript, int64> > vecSend;
125         foreach(const SendCoinsRecipient &rcp, recipients)
126         {
127             CScript scriptPubKey;
128             scriptPubKey.SetBitcoinAddress(rcp.address.toStdString());
129             vecSend.push_back(make_pair(scriptPubKey, rcp.amount));
130         }
131
132         CWalletTx wtx;
133         CReserveKey keyChange(wallet);
134         int64 nFeeRequired = 0;
135         bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
136
137         if(!fCreated)
138         {
139             if((total + nFeeRequired) > wallet->GetBalance())
140             {
141                 return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired);
142             }
143             return TransactionCreationFailed;
144         }
145         if(!ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString(), NULL))
146         {
147             return Aborted;
148         }
149         if(!wallet->CommitTransaction(wtx, keyChange))
150         {
151             return TransactionCommitFailed;
152         }
153         hex = QString::fromStdString(wtx.GetHash().GetHex());
154     }
155
156     // Add addresses / update labels that we've sent to to the address book
157     foreach(const SendCoinsRecipient &rcp, recipients)
158     {
159         std::string strAddress = rcp.address.toStdString();
160         std::string strLabel = rcp.label.toStdString();
161         CRITICAL_BLOCK(wallet->cs_wallet)
162         {
163             std::map<CBitcoinAddress, std::string>::iterator mi = wallet->mapAddressBook.find(strAddress);
164
165             // Check if we have a new address or an updated label
166             if (mi == wallet->mapAddressBook.end() || mi->second != strLabel)
167             {
168                 wallet->SetAddressBookName(strAddress, strLabel);
169             }
170         }
171     }
172
173     // Update our model of the address table
174     addressTableModel->updateList();
175
176     return SendCoinsReturn(OK, 0, hex);
177 }
178
179 OptionsModel *WalletModel::getOptionsModel()
180 {
181     return optionsModel;
182 }
183
184 AddressTableModel *WalletModel::getAddressTableModel()
185 {
186     return addressTableModel;
187 }
188
189 TransactionTableModel *WalletModel::getTransactionTableModel()
190 {
191     return transactionTableModel;
192 }
193
194 WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
195 {
196     if(!wallet->IsCrypted())
197     {
198         return Unencrypted;
199     }
200     else if(wallet->IsLocked())
201     {
202         return Locked;
203     }
204     else
205     {
206         return Unlocked;
207     }
208 }
209
210 bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
211 {
212     if(encrypted)
213     {
214         // Encrypt
215         return wallet->EncryptWallet(passphrase);
216     }
217     else
218     {
219         // Decrypt -- TODO; not supported yet
220         return false;
221     }
222 }
223
224 bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
225 {
226     if(locked)
227     {
228         // Lock
229         return wallet->Lock();
230     }
231     else
232     {
233         // Unlock
234         return wallet->Unlock(passPhrase);
235     }
236 }
237
238 bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
239 {
240     bool retval;
241     CRITICAL_BLOCK(wallet->cs_wallet)
242     {
243         wallet->Lock(); // Make sure wallet is locked before attempting pass change
244         retval = wallet->ChangeWalletPassphrase(oldPass, newPass);
245     }
246     return retval;
247 }
248
249 bool WalletModel::backupWallet(const QString &filename)
250 {
251     return BackupWallet(*wallet, filename.toLocal8Bit().data());
252 }
253
254 // WalletModel::UnlockContext implementation
255 WalletModel::UnlockContext WalletModel::requestUnlock()
256 {
257     bool was_locked = getEncryptionStatus() == Locked;
258     if(was_locked)
259     {
260         // Request UI to unlock wallet
261         emit requireUnlock();
262     }
263     // If wallet is still locked, unlock was failed or cancelled, mark context as invalid
264     bool valid = getEncryptionStatus() != Locked;
265
266     return UnlockContext(this, valid, was_locked);
267 }
268
269 WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock):
270         wallet(wallet),
271         valid(valid),
272         relock(relock)
273 {
274 }
275
276 WalletModel::UnlockContext::~UnlockContext()
277 {
278     if(valid && relock)
279     {
280         wallet->setWalletLocked(true);
281     }
282 }
283
284 void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
285 {
286     // Transfer context; old object no longer relocks wallet
287     *this = rhs;
288     rhs.relock = false;
289 }