Added 'Backup Wallet' menu option
[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 that we've sent to to the address book
157     foreach(const SendCoinsRecipient &rcp, recipients)
158     {
159         std::string strAddress = rcp.address.toStdString();
160         CRITICAL_BLOCK(wallet->cs_wallet)
161         {
162             if (!wallet->mapAddressBook.count(strAddress))
163                 wallet->SetAddressBookName(strAddress, rcp.label.toStdString());
164         }
165     }
166
167     // Update our model of the address table
168     addressTableModel->updateList();
169
170     return SendCoinsReturn(OK, 0, hex);
171 }
172
173 OptionsModel *WalletModel::getOptionsModel()
174 {
175     return optionsModel;
176 }
177
178 AddressTableModel *WalletModel::getAddressTableModel()
179 {
180     return addressTableModel;
181 }
182
183 TransactionTableModel *WalletModel::getTransactionTableModel()
184 {
185     return transactionTableModel;
186 }
187
188 WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
189 {
190     if(!wallet->IsCrypted())
191     {
192         return Unencrypted;
193     }
194     else if(wallet->IsLocked())
195     {
196         return Locked;
197     }
198     else
199     {
200         return Unlocked;
201     }
202 }
203
204 bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
205 {
206     if(encrypted)
207     {
208         // Encrypt
209         return wallet->EncryptWallet(passphrase);
210     }
211     else
212     {
213         // Decrypt -- TODO; not supported yet
214         return false;
215     }
216 }
217
218 bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
219 {
220     if(locked)
221     {
222         // Lock
223         return wallet->Lock();
224     }
225     else
226     {
227         // Unlock
228         return wallet->Unlock(passPhrase);
229     }
230 }
231
232 bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
233 {
234     bool retval;
235     CRITICAL_BLOCK(wallet->cs_wallet)
236     {
237         wallet->Lock(); // Make sure wallet is locked before attempting pass change
238         retval = wallet->ChangeWalletPassphrase(oldPass, newPass);
239     }
240     return retval;
241 }
242
243 bool WalletModel::backupWallet(const QString &filename)
244 {
245     return BackupWallet(*wallet, filename.toLocal8Bit().data());
246 }
247
248 // WalletModel::UnlockContext implementation
249 WalletModel::UnlockContext WalletModel::requestUnlock()
250 {
251     bool was_locked = getEncryptionStatus() == Locked;
252     if(was_locked)
253     {
254         // Request UI to unlock wallet
255         emit requireUnlock();
256     }
257     // If wallet is still locked, unlock was failed or cancelled, mark context as invalid
258     bool valid = getEncryptionStatus() != Locked;
259
260     return UnlockContext(this, valid, was_locked);
261 }
262
263 WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock):
264         wallet(wallet),
265         valid(valid),
266         relock(relock)
267 {
268 }
269
270 WalletModel::UnlockContext::~UnlockContext()
271 {
272     if(valid && relock)
273     {
274         wallet->setWalletLocked(true);
275     }
276 }
277
278 void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
279 {
280     // Transfer context; old object no longer relocks wallet
281     *this = rhs;
282     rhs.relock = false;
283 }