PPCoin: Qt: display coinstake as single 'mint by stake' transaction
[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 "ui_interface.h"
8 #include "wallet.h"
9 #include "walletdb.h" // for BackupWallet
10
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     addressTableModel = new AddressTableModel(wallet, this);
20     transactionTableModel = new TransactionTableModel(wallet, this);
21 }
22
23 qint64 WalletModel::getBalance() const
24 {
25     return wallet->GetBalance();
26 }
27
28 qint64 WalletModel::getStake() const
29 {
30     return wallet->GetStake();
31 }
32
33 qint64 WalletModel::getUnconfirmedBalance() const
34 {
35     return wallet->GetUnconfirmedBalance();
36 }
37
38 int WalletModel::getNumTransactions() const
39 {
40     int numTransactions = 0;
41     {
42         LOCK(wallet->cs_wallet);
43         numTransactions = wallet->mapWallet.size();
44     }
45     return numTransactions;
46 }
47
48 void WalletModel::update()
49 {
50     qint64 newBalance = getBalance();
51     qint64 newUnconfirmedBalance = getUnconfirmedBalance();
52     int newNumTransactions = getNumTransactions();
53     EncryptionStatus newEncryptionStatus = getEncryptionStatus();
54
55     if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance)
56         emit balanceChanged(newBalance, getStake(), newUnconfirmedBalance);
57
58     if(cachedNumTransactions != newNumTransactions)
59         emit numTransactionsChanged(newNumTransactions);
60
61     if(cachedEncryptionStatus != newEncryptionStatus)
62         emit encryptionStatusChanged(newEncryptionStatus);
63
64     cachedBalance = newBalance;
65     cachedUnconfirmedBalance = newUnconfirmedBalance;
66     cachedNumTransactions = newNumTransactions;
67 }
68
69 void WalletModel::updateAddressList()
70 {
71     addressTableModel->update();
72 }
73
74 bool WalletModel::validateAddress(const QString &address)
75 {
76     CBitcoinAddress addressParsed(address.toStdString());
77     return addressParsed.IsValid();
78 }
79
80 WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients)
81 {
82     qint64 total = 0;
83     QSet<QString> setAddress;
84     QString hex;
85
86     if(recipients.empty())
87     {
88         return OK;
89     }
90
91     // Pre-check input data for validity
92     foreach(const SendCoinsRecipient &rcp, recipients)
93     {
94         if(!validateAddress(rcp.address))
95         {
96             return InvalidAddress;
97         }
98         setAddress.insert(rcp.address);
99
100         if(rcp.amount <= 0)
101         {
102             return InvalidAmount;
103         }
104         total += rcp.amount;
105     }
106
107     if(recipients.size() > setAddress.size())
108     {
109         return DuplicateAddress;
110     }
111
112     if(total > getBalance())
113     {
114         return AmountExceedsBalance;
115     }
116
117     if((total + nTransactionFee) > getBalance())
118     {
119         return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee);
120     }
121
122     {
123         LOCK2(cs_main, wallet->cs_wallet);
124
125         // Sendmany
126         std::vector<std::pair<CScript, int64> > vecSend;
127         foreach(const SendCoinsRecipient &rcp, recipients)
128         {
129             CScript scriptPubKey;
130             scriptPubKey.SetBitcoinAddress(rcp.address.toStdString());
131             vecSend.push_back(make_pair(scriptPubKey, rcp.amount));
132         }
133
134         CWalletTx wtx;
135         CReserveKey keyChange(wallet);
136         int64 nFeeRequired = 0;
137         bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
138
139         if(!fCreated)
140         {
141             if((total + nFeeRequired) > wallet->GetBalance())
142             {
143                 return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired);
144             }
145             return TransactionCreationFailed;
146         }
147         if(!ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString()))
148         {
149             return Aborted;
150         }
151         if(!wallet->CommitTransaction(wtx, keyChange))
152         {
153             return TransactionCommitFailed;
154         }
155         hex = QString::fromStdString(wtx.GetHash().GetHex());
156     }
157
158     // Add addresses / update labels that we've sent to to the address book
159     foreach(const SendCoinsRecipient &rcp, recipients)
160     {
161         std::string strAddress = rcp.address.toStdString();
162         std::string strLabel = rcp.label.toStdString();
163         {
164             LOCK(wallet->cs_wallet);
165
166             std::map<CBitcoinAddress, std::string>::iterator mi = wallet->mapAddressBook.find(strAddress);
167
168             // Check if we have a new address or an updated label
169             if (mi == wallet->mapAddressBook.end() || mi->second != strLabel)
170             {
171                 wallet->SetAddressBookName(strAddress, strLabel);
172             }
173         }
174     }
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     {
242         LOCK(wallet->cs_wallet);
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 }