]>
Commit | Line | Data |
---|---|---|
ef079e18 WL |
1 | #include "walletmodel.h" |
2 | #include "guiconstants.h" | |
3 | #include "optionsmodel.h" | |
4 | #include "addresstablemodel.h" | |
5 | #include "transactiontablemodel.h" | |
6 | ||
6b6aaa16 | 7 | #include "ui_interface.h" |
ed6d0b5f | 8 | #include "wallet.h" |
9eace6b1 | 9 | #include "walletdb.h" // for BackupWallet |
ef079e18 | 10 | |
a5e6d723 | 11 | #include <QSet> |
ef079e18 | 12 | |
ee014e5b WL |
13 | WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : |
14 | QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), | |
5df0b03c | 15 | transactionTableModel(0), |
ae8adeb9 WL |
16 | cachedBalance(0), cachedUnconfirmedBalance(0), cachedNumTransactions(0), |
17 | cachedEncryptionStatus(Unencrypted) | |
ef079e18 | 18 | { |
ef079e18 WL |
19 | addressTableModel = new AddressTableModel(wallet, this); |
20 | transactionTableModel = new TransactionTableModel(wallet, this); | |
ab1b288f WL |
21 | |
22 | subscribeToCoreSignals(); | |
23 | } | |
24 | ||
25 | WalletModel::~WalletModel() | |
26 | { | |
27 | unsubscribeFromCoreSignals(); | |
ef079e18 WL |
28 | } |
29 | ||
30 | qint64 WalletModel::getBalance() const | |
31 | { | |
32 | return wallet->GetBalance(); | |
33 | } | |
34 | ||
df5ccbd2 WL |
35 | qint64 WalletModel::getUnconfirmedBalance() const |
36 | { | |
37 | return wallet->GetUnconfirmedBalance(); | |
38 | } | |
39 | ||
ef079e18 WL |
40 | int WalletModel::getNumTransactions() const |
41 | { | |
42 | int numTransactions = 0; | |
ef079e18 | 43 | { |
f8dcd5ca | 44 | LOCK(wallet->cs_wallet); |
ef079e18 WL |
45 | numTransactions = wallet->mapWallet.size(); |
46 | } | |
47 | return numTransactions; | |
48 | } | |
49 | ||
fe4a6550 | 50 | void WalletModel::updateStatus() |
ef079e18 | 51 | { |
fe4a6550 WL |
52 | EncryptionStatus newEncryptionStatus = getEncryptionStatus(); |
53 | ||
54 | if(cachedEncryptionStatus != newEncryptionStatus) | |
55 | emit encryptionStatusChanged(newEncryptionStatus); | |
56 | } | |
57 | ||
58 | void WalletModel::updateTransaction(const QString &hash, int status) | |
59 | { | |
60 | if(transactionTableModel) | |
61 | transactionTableModel->updateTransaction(hash, status); | |
62 | ||
63 | // Balance and number of transactions might have changed | |
5df0b03c WL |
64 | qint64 newBalance = getBalance(); |
65 | qint64 newUnconfirmedBalance = getUnconfirmedBalance(); | |
66 | int newNumTransactions = getNumTransactions(); | |
67 | ||
68 | if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance) | |
69 | emit balanceChanged(newBalance, newUnconfirmedBalance); | |
5df0b03c WL |
70 | if(cachedNumTransactions != newNumTransactions) |
71 | emit numTransactionsChanged(newNumTransactions); | |
72 | ||
73 | cachedBalance = newBalance; | |
74 | cachedUnconfirmedBalance = newUnconfirmedBalance; | |
75 | cachedNumTransactions = newNumTransactions; | |
ef079e18 WL |
76 | } |
77 | ||
fe4a6550 | 78 | void WalletModel::updateAddressBook(const QString &address, const QString &label, int status) |
98e61758 | 79 | { |
fe4a6550 WL |
80 | if(addressTableModel) |
81 | addressTableModel->updateEntry(address, label, status); | |
98e61758 WL |
82 | } |
83 | ||
a5e6d723 | 84 | bool WalletModel::validateAddress(const QString &address) |
ef079e18 | 85 | { |
491ad6db WL |
86 | CBitcoinAddress addressParsed(address.toStdString()); |
87 | return addressParsed.IsValid(); | |
a5e6d723 WL |
88 | } |
89 | ||
90 | WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients) | |
91 | { | |
92 | qint64 total = 0; | |
93 | QSet<QString> setAddress; | |
94 | QString hex; | |
95 | ||
96 | if(recipients.empty()) | |
ef079e18 | 97 | { |
a5e6d723 | 98 | return OK; |
ef079e18 WL |
99 | } |
100 | ||
a5e6d723 WL |
101 | // Pre-check input data for validity |
102 | foreach(const SendCoinsRecipient &rcp, recipients) | |
ef079e18 | 103 | { |
491ad6db | 104 | if(!validateAddress(rcp.address)) |
a5e6d723 WL |
105 | { |
106 | return InvalidAddress; | |
107 | } | |
108 | setAddress.insert(rcp.address); | |
109 | ||
110 | if(rcp.amount <= 0) | |
111 | { | |
112 | return InvalidAmount; | |
113 | } | |
114 | total += rcp.amount; | |
ef079e18 WL |
115 | } |
116 | ||
a5e6d723 WL |
117 | if(recipients.size() > setAddress.size()) |
118 | { | |
119 | return DuplicateAddress; | |
120 | } | |
121 | ||
122 | if(total > getBalance()) | |
ef079e18 WL |
123 | { |
124 | return AmountExceedsBalance; | |
125 | } | |
126 | ||
a5e6d723 | 127 | if((total + nTransactionFee) > getBalance()) |
ef079e18 | 128 | { |
a5e6d723 | 129 | return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); |
ef079e18 WL |
130 | } |
131 | ||
ef079e18 | 132 | { |
f8dcd5ca PW |
133 | LOCK2(cs_main, wallet->cs_wallet); |
134 | ||
a5e6d723 | 135 | // Sendmany |
bde280b9 | 136 | std::vector<std::pair<CScript, int64> > vecSend; |
a5e6d723 WL |
137 | foreach(const SendCoinsRecipient &rcp, recipients) |
138 | { | |
139 | CScript scriptPubKey; | |
140 | scriptPubKey.SetBitcoinAddress(rcp.address.toStdString()); | |
141 | vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); | |
142 | } | |
143 | ||
ef079e18 | 144 | CWalletTx wtx; |
a5e6d723 | 145 | CReserveKey keyChange(wallet); |
bde280b9 | 146 | int64 nFeeRequired = 0; |
a5e6d723 | 147 | bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); |
ef079e18 | 148 | |
a5e6d723 | 149 | if(!fCreated) |
ef079e18 | 150 | { |
a5e6d723 WL |
151 | if((total + nFeeRequired) > wallet->GetBalance()) |
152 | { | |
153 | return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); | |
154 | } | |
155 | return TransactionCreationFailed; | |
ef079e18 | 156 | } |
ab1b288f | 157 | if(!uiInterface.ThreadSafeAskFee(nFeeRequired, tr("Sending...").toStdString())) |
ef079e18 WL |
158 | { |
159 | return Aborted; | |
160 | } | |
a5e6d723 | 161 | if(!wallet->CommitTransaction(wtx, keyChange)) |
ef079e18 | 162 | { |
a5e6d723 | 163 | return TransactionCommitFailed; |
ef079e18 | 164 | } |
a5e6d723 | 165 | hex = QString::fromStdString(wtx.GetHash().GetHex()); |
ef079e18 WL |
166 | } |
167 | ||
dab7acdf | 168 | // Add addresses / update labels that we've sent to to the address book |
a5e6d723 | 169 | foreach(const SendCoinsRecipient &rcp, recipients) |
ef079e18 | 170 | { |
a5e6d723 | 171 | std::string strAddress = rcp.address.toStdString(); |
dab7acdf | 172 | std::string strLabel = rcp.label.toStdString(); |
a5e6d723 | 173 | { |
f8dcd5ca | 174 | LOCK(wallet->cs_wallet); |
dab7acdf PK |
175 | |
176 | std::map<CBitcoinAddress, std::string>::iterator mi = wallet->mapAddressBook.find(strAddress); | |
177 | ||
178 | // Check if we have a new address or an updated label | |
179 | if (mi == wallet->mapAddressBook.end() || mi->second != strLabel) | |
180 | { | |
181 | wallet->SetAddressBookName(strAddress, strLabel); | |
182 | } | |
a5e6d723 | 183 | } |
ef079e18 | 184 | } |
a5e13258 | 185 | |
a5e6d723 | 186 | return SendCoinsReturn(OK, 0, hex); |
ef079e18 WL |
187 | } |
188 | ||
189 | OptionsModel *WalletModel::getOptionsModel() | |
190 | { | |
191 | return optionsModel; | |
192 | } | |
193 | ||
194 | AddressTableModel *WalletModel::getAddressTableModel() | |
195 | { | |
196 | return addressTableModel; | |
197 | } | |
198 | ||
199 | TransactionTableModel *WalletModel::getTransactionTableModel() | |
200 | { | |
201 | return transactionTableModel; | |
202 | } | |
ebff5c40 | 203 | |
ae8adeb9 WL |
204 | WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const |
205 | { | |
206 | if(!wallet->IsCrypted()) | |
207 | { | |
208 | return Unencrypted; | |
209 | } | |
210 | else if(wallet->IsLocked()) | |
211 | { | |
212 | return Locked; | |
213 | } | |
214 | else | |
215 | { | |
216 | return Unlocked; | |
217 | } | |
218 | } | |
b7bcaf94 | 219 | |
94f778bd | 220 | bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase) |
b7bcaf94 WL |
221 | { |
222 | if(encrypted) | |
223 | { | |
224 | // Encrypt | |
225 | return wallet->EncryptWallet(passphrase); | |
226 | } | |
227 | else | |
228 | { | |
229 | // Decrypt -- TODO; not supported yet | |
230 | return false; | |
231 | } | |
232 | } | |
233 | ||
94f778bd | 234 | bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) |
b7bcaf94 WL |
235 | { |
236 | if(locked) | |
237 | { | |
238 | // Lock | |
239 | return wallet->Lock(); | |
240 | } | |
241 | else | |
242 | { | |
243 | // Unlock | |
244 | return wallet->Unlock(passPhrase); | |
245 | } | |
246 | } | |
247 | ||
94f778bd | 248 | bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass) |
b7bcaf94 WL |
249 | { |
250 | bool retval; | |
b7bcaf94 | 251 | { |
f8dcd5ca | 252 | LOCK(wallet->cs_wallet); |
b7bcaf94 WL |
253 | wallet->Lock(); // Make sure wallet is locked before attempting pass change |
254 | retval = wallet->ChangeWalletPassphrase(oldPass, newPass); | |
255 | } | |
256 | return retval; | |
257 | } | |
258 | ||
4efbda3f | 259 | bool WalletModel::backupWallet(const QString &filename) |
260 | { | |
261 | return BackupWallet(*wallet, filename.toLocal8Bit().data()); | |
262 | } | |
263 | ||
ab1b288f WL |
264 | // Handlers for core signals |
265 | static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet) | |
266 | { | |
267 | OutputDebugStringF("NotifyKeyStoreStatusChanged\n"); | |
268 | QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection); | |
269 | } | |
270 | ||
271 | static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const std::string &address, const std::string &label, ChangeType status) | |
272 | { | |
273 | OutputDebugStringF("NotifyAddressBookChanged %s %s status=%i\n", address.c_str(), label.c_str(), status); | |
274 | QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection, | |
275 | Q_ARG(QString, QString::fromStdString(address)), | |
276 | Q_ARG(QString, QString::fromStdString(label)), | |
277 | Q_ARG(int, status)); | |
278 | } | |
279 | ||
280 | static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status) | |
281 | { | |
282 | OutputDebugStringF("NotifyTransactionChanged %s status=%i\n", hash.GetHex().c_str(), status); | |
283 | QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection, | |
284 | Q_ARG(QString, QString::fromStdString(hash.GetHex())), | |
285 | Q_ARG(int, status)); | |
286 | } | |
287 | ||
288 | void WalletModel::subscribeToCoreSignals() | |
289 | { | |
290 | // Connect signals to wallet | |
291 | wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); | |
292 | wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4)); | |
293 | wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); | |
294 | } | |
295 | ||
296 | void WalletModel::unsubscribeFromCoreSignals() | |
297 | { | |
298 | // Disconnect signals from wallet | |
299 | wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); | |
300 | wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4)); | |
301 | wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); | |
302 | } | |
303 | ||
b7bcaf94 WL |
304 | // WalletModel::UnlockContext implementation |
305 | WalletModel::UnlockContext WalletModel::requestUnlock() | |
306 | { | |
307 | bool was_locked = getEncryptionStatus() == Locked; | |
308 | if(was_locked) | |
309 | { | |
310 | // Request UI to unlock wallet | |
311 | emit requireUnlock(); | |
312 | } | |
313 | // If wallet is still locked, unlock was failed or cancelled, mark context as invalid | |
314 | bool valid = getEncryptionStatus() != Locked; | |
315 | ||
316 | return UnlockContext(this, valid, was_locked); | |
317 | } | |
318 | ||
319 | WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock): | |
320 | wallet(wallet), | |
321 | valid(valid), | |
322 | relock(relock) | |
323 | { | |
324 | } | |
325 | ||
326 | WalletModel::UnlockContext::~UnlockContext() | |
327 | { | |
328 | if(valid && relock) | |
329 | { | |
330 | wallet->setWalletLocked(true); | |
331 | } | |
332 | } | |
333 | ||
334 | void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) | |
335 | { | |
336 | // Transfer context; old object no longer relocks wallet | |
337 | *this = rhs; | |
338 | rhs.relock = false; | |
339 | } |