You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

walletmodel.cpp 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. // Copyright (c) 2011-2016 The Starwels developers
  2. // Distributed under the MIT software license, see the accompanying
  3. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4. #include "walletmodel.h"
  5. #include "addresstablemodel.h"
  6. #include "consensus/validation.h"
  7. #include "guiconstants.h"
  8. #include "guiutil.h"
  9. #include "optionsmodel.h"
  10. #include "paymentserver.h"
  11. #include "recentrequeststablemodel.h"
  12. #include "sendcoinsdialog.h"
  13. #include "transactiontablemodel.h"
  14. #include "base58.h"
  15. #include "chain.h"
  16. #include "keystore.h"
  17. #include "validation.h"
  18. #include "net.h" // for g_connman
  19. #include "policy/fees.h"
  20. #include "policy/rbf.h"
  21. #include "sync.h"
  22. #include "ui_interface.h"
  23. #include "util.h" // for GetBoolArg
  24. #include "wallet/coincontrol.h"
  25. #include "wallet/feebumper.h"
  26. #include "wallet/wallet.h"
  27. #include "wallet/walletdb.h" // for BackupWallet
  28. #include <stdint.h>
  29. #include <QDebug>
  30. #include <QMessageBox>
  31. #include <QSet>
  32. #include <QTimer>
  33. WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) :
  34. QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0),
  35. transactionTableModel(0),
  36. recentRequestsTableModel(0),
  37. cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0),
  38. cachedEncryptionStatus(Unencrypted),
  39. cachedNumBlocks(0)
  40. {
  41. fHaveWatchOnly = wallet->HaveWatchOnly();
  42. fForceCheckBalanceChanged = false;
  43. addressTableModel = new AddressTableModel(wallet, this);
  44. transactionTableModel = new TransactionTableModel(platformStyle, wallet, this);
  45. recentRequestsTableModel = new RecentRequestsTableModel(wallet, this);
  46. // This timer will be fired repeatedly to update the balance
  47. pollTimer = new QTimer(this);
  48. connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollBalanceChanged()));
  49. pollTimer->start(MODEL_UPDATE_DELAY);
  50. subscribeToCoreSignals();
  51. }
  52. WalletModel::~WalletModel()
  53. {
  54. unsubscribeFromCoreSignals();
  55. }
  56. CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
  57. {
  58. if (coinControl)
  59. {
  60. return wallet->GetAvailableBalance(coinControl);
  61. }
  62. return wallet->GetBalance();
  63. }
  64. CAmount WalletModel::getUnconfirmedBalance() const
  65. {
  66. return wallet->GetUnconfirmedBalance();
  67. }
  68. CAmount WalletModel::getImmatureBalance() const
  69. {
  70. return wallet->GetImmatureBalance();
  71. }
  72. bool WalletModel::haveWatchOnly() const
  73. {
  74. return fHaveWatchOnly;
  75. }
  76. CAmount WalletModel::getWatchBalance() const
  77. {
  78. return wallet->GetWatchOnlyBalance();
  79. }
  80. CAmount WalletModel::getWatchUnconfirmedBalance() const
  81. {
  82. return wallet->GetUnconfirmedWatchOnlyBalance();
  83. }
  84. CAmount WalletModel::getWatchImmatureBalance() const
  85. {
  86. return wallet->GetImmatureWatchOnlyBalance();
  87. }
  88. void WalletModel::updateStatus()
  89. {
  90. EncryptionStatus newEncryptionStatus = getEncryptionStatus();
  91. if(cachedEncryptionStatus != newEncryptionStatus)
  92. Q_EMIT encryptionStatusChanged(newEncryptionStatus);
  93. }
  94. void WalletModel::pollBalanceChanged()
  95. {
  96. // Get required locks upfront. This avoids the GUI from getting stuck on
  97. // periodical polls if the core is holding the locks for a longer time -
  98. // for example, during a wallet rescan.
  99. TRY_LOCK(cs_main, lockMain);
  100. if(!lockMain)
  101. return;
  102. TRY_LOCK(wallet->cs_wallet, lockWallet);
  103. if(!lockWallet)
  104. return;
  105. if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks)
  106. {
  107. fForceCheckBalanceChanged = false;
  108. // Balance and number of transactions might have changed
  109. cachedNumBlocks = chainActive.Height();
  110. checkBalanceChanged();
  111. if(transactionTableModel)
  112. transactionTableModel->updateConfirmations();
  113. }
  114. }
  115. void WalletModel::checkBalanceChanged()
  116. {
  117. CAmount newBalance = getBalance();
  118. CAmount newUnconfirmedBalance = getUnconfirmedBalance();
  119. CAmount newImmatureBalance = getImmatureBalance();
  120. CAmount newWatchOnlyBalance = 0;
  121. CAmount newWatchUnconfBalance = 0;
  122. CAmount newWatchImmatureBalance = 0;
  123. if (haveWatchOnly())
  124. {
  125. newWatchOnlyBalance = getWatchBalance();
  126. newWatchUnconfBalance = getWatchUnconfirmedBalance();
  127. newWatchImmatureBalance = getWatchImmatureBalance();
  128. }
  129. if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance ||
  130. cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance)
  131. {
  132. cachedBalance = newBalance;
  133. cachedUnconfirmedBalance = newUnconfirmedBalance;
  134. cachedImmatureBalance = newImmatureBalance;
  135. cachedWatchOnlyBalance = newWatchOnlyBalance;
  136. cachedWatchUnconfBalance = newWatchUnconfBalance;
  137. cachedWatchImmatureBalance = newWatchImmatureBalance;
  138. Q_EMIT balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance,
  139. newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance);
  140. }
  141. }
  142. void WalletModel::updateTransaction()
  143. {
  144. // Balance and number of transactions might have changed
  145. fForceCheckBalanceChanged = true;
  146. }
  147. void WalletModel::updateAddressBook(const QString &address, const QString &label,
  148. bool isMine, const QString &purpose, int status)
  149. {
  150. if(addressTableModel)
  151. addressTableModel->updateEntry(address, label, isMine, purpose, status);
  152. }
  153. void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly)
  154. {
  155. fHaveWatchOnly = fHaveWatchonly;
  156. Q_EMIT notifyWatchonlyChanged(fHaveWatchonly);
  157. }
  158. bool WalletModel::validateAddress(const QString &address)
  159. {
  160. CStarwelsAddress addressParsed(address.toStdString());
  161. return addressParsed.IsValid();
  162. }
  163. WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
  164. {
  165. CAmount total = 0;
  166. bool fSubtractFeeFromAmount = false;
  167. QList<SendCoinsRecipient> recipients = transaction.getRecipients();
  168. std::vector<CRecipient> vecSend;
  169. if(recipients.empty())
  170. {
  171. return OK;
  172. }
  173. QSet<QString> setAddress; // Used to detect duplicates
  174. int nAddresses = 0;
  175. // Pre-check input data for validity
  176. for (const SendCoinsRecipient &rcp : recipients)
  177. {
  178. if (rcp.fSubtractFeeFromAmount)
  179. fSubtractFeeFromAmount = true;
  180. if (rcp.paymentRequest.IsInitialized())
  181. { // PaymentRequest...
  182. CAmount subtotal = 0;
  183. const payments::PaymentDetails& details = rcp.paymentRequest.getDetails();
  184. for (int i = 0; i < details.outputs_size(); i++)
  185. {
  186. const payments::Output& out = details.outputs(i);
  187. if (out.amount() <= 0) continue;
  188. subtotal += out.amount();
  189. const unsigned char* scriptStr = (const unsigned char*)out.script().data();
  190. CScript scriptPubKey(scriptStr, scriptStr+out.script().size());
  191. CAmount nAmount = out.amount();
  192. CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount};
  193. vecSend.push_back(recipient);
  194. }
  195. if (subtotal <= 0)
  196. {
  197. return InvalidAmount;
  198. }
  199. total += subtotal;
  200. }
  201. else
  202. { // User-entered starwels address / amount:
  203. if(!validateAddress(rcp.address))
  204. {
  205. return InvalidAddress;
  206. }
  207. if(rcp.amount <= 0)
  208. {
  209. return InvalidAmount;
  210. }
  211. setAddress.insert(rcp.address);
  212. ++nAddresses;
  213. CScript scriptPubKey = GetScriptForDestination(CStarwelsAddress(rcp.address.toStdString()).Get());
  214. CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount};
  215. vecSend.push_back(recipient);
  216. total += rcp.amount;
  217. }
  218. }
  219. if(setAddress.size() != nAddresses)
  220. {
  221. return DuplicateAddress;
  222. }
  223. CAmount nBalance = getBalance(&coinControl);
  224. if(total > nBalance)
  225. {
  226. return AmountExceedsBalance;
  227. }
  228. {
  229. LOCK2(cs_main, wallet->cs_wallet);
  230. transaction.newPossibleKeyChange(wallet);
  231. CAmount nFeeRequired = 0;
  232. int nChangePosRet = -1;
  233. std::string strFailReason;
  234. CWalletTx *newTx = transaction.getTransaction();
  235. CReserveKey *keyChange = transaction.getPossibleKeyChange();
  236. bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl);
  237. transaction.setTransactionFee(nFeeRequired);
  238. if (fSubtractFeeFromAmount && fCreated)
  239. transaction.reassignAmounts(nChangePosRet);
  240. if(!fCreated)
  241. {
  242. if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance)
  243. {
  244. return SendCoinsReturn(AmountWithFeeExceedsBalance);
  245. }
  246. Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason),
  247. CClientUIInterface::MSG_ERROR);
  248. return TransactionCreationFailed;
  249. }
  250. // reject absurdly high fee. (This can never happen because the
  251. // wallet caps the fee at maxTxFee. This merely serves as a
  252. // belt-and-suspenders check)
  253. if (nFeeRequired > maxTxFee)
  254. return AbsurdFee;
  255. }
  256. return SendCoinsReturn(OK);
  257. }
  258. WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction)
  259. {
  260. QByteArray transaction_array; /* store serialized transaction */
  261. {
  262. LOCK2(cs_main, wallet->cs_wallet);
  263. CWalletTx *newTx = transaction.getTransaction();
  264. for (const SendCoinsRecipient &rcp : transaction.getRecipients())
  265. {
  266. if (rcp.paymentRequest.IsInitialized())
  267. {
  268. // Make sure any payment requests involved are still valid.
  269. if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) {
  270. return PaymentRequestExpired;
  271. }
  272. // Store PaymentRequests in wtx.vOrderForm in wallet.
  273. std::string key("PaymentRequest");
  274. std::string value;
  275. rcp.paymentRequest.SerializeToString(&value);
  276. newTx->vOrderForm.push_back(make_pair(key, value));
  277. }
  278. else if (!rcp.message.isEmpty()) // Message from normal starwels:URI (starwels:123...?message=example)
  279. newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString()));
  280. }
  281. CReserveKey *keyChange = transaction.getPossibleKeyChange();
  282. CValidationState state;
  283. if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), state))
  284. return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason()));
  285. CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
  286. ssTx << *newTx->tx;
  287. transaction_array.append(&(ssTx[0]), ssTx.size());
  288. }
  289. // Add addresses / update labels that we've sent to the address book,
  290. // and emit coinsSent signal for each recipient
  291. for (const SendCoinsRecipient &rcp : transaction.getRecipients())
  292. {
  293. // Don't touch the address book when we have a payment request
  294. if (!rcp.paymentRequest.IsInitialized())
  295. {
  296. std::string strAddress = rcp.address.toStdString();
  297. CTxDestination dest = CStarwelsAddress(strAddress).Get();
  298. std::string strLabel = rcp.label.toStdString();
  299. {
  300. LOCK(wallet->cs_wallet);
  301. std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest);
  302. // Check if we have a new address or an updated label
  303. if (mi == wallet->mapAddressBook.end())
  304. {
  305. wallet->SetAddressBook(dest, strLabel, "send");
  306. }
  307. else if (mi->second.name != strLabel)
  308. {
  309. wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
  310. }
  311. }
  312. }
  313. Q_EMIT coinsSent(wallet, rcp, transaction_array);
  314. }
  315. checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
  316. return SendCoinsReturn(OK);
  317. }
  318. OptionsModel *WalletModel::getOptionsModel()
  319. {
  320. return optionsModel;
  321. }
  322. AddressTableModel *WalletModel::getAddressTableModel()
  323. {
  324. return addressTableModel;
  325. }
  326. TransactionTableModel *WalletModel::getTransactionTableModel()
  327. {
  328. return transactionTableModel;
  329. }
  330. RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel()
  331. {
  332. return recentRequestsTableModel;
  333. }
  334. WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
  335. {
  336. if(!wallet->IsCrypted())
  337. {
  338. return Unencrypted;
  339. }
  340. else if(wallet->IsLocked())
  341. {
  342. return Locked;
  343. }
  344. else
  345. {
  346. return Unlocked;
  347. }
  348. }
  349. bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
  350. {
  351. if(encrypted)
  352. {
  353. // Encrypt
  354. return wallet->EncryptWallet(passphrase);
  355. }
  356. else
  357. {
  358. // Decrypt -- TODO; not supported yet
  359. return false;
  360. }
  361. }
  362. bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
  363. {
  364. if(locked)
  365. {
  366. // Lock
  367. return wallet->Lock();
  368. }
  369. else
  370. {
  371. // Unlock
  372. return wallet->Unlock(passPhrase);
  373. }
  374. }
  375. bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
  376. {
  377. bool retval;
  378. {
  379. LOCK(wallet->cs_wallet);
  380. wallet->Lock(); // Make sure wallet is locked before attempting pass change
  381. retval = wallet->ChangeWalletPassphrase(oldPass, newPass);
  382. }
  383. return retval;
  384. }
  385. bool WalletModel::backupWallet(const QString &filename)
  386. {
  387. return wallet->BackupWallet(filename.toLocal8Bit().data());
  388. }
  389. // Handlers for core signals
  390. static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet)
  391. {
  392. qDebug() << "NotifyKeyStoreStatusChanged";
  393. QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
  394. }
  395. static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
  396. const CTxDestination &address, const std::string &label, bool isMine,
  397. const std::string &purpose, ChangeType status)
  398. {
  399. QString strAddress = QString::fromStdString(CStarwelsAddress(address).ToString());
  400. QString strLabel = QString::fromStdString(label);
  401. QString strPurpose = QString::fromStdString(purpose);
  402. qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status);
  403. QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
  404. Q_ARG(QString, strAddress),
  405. Q_ARG(QString, strLabel),
  406. Q_ARG(bool, isMine),
  407. Q_ARG(QString, strPurpose),
  408. Q_ARG(int, status));
  409. }
  410. static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status)
  411. {
  412. Q_UNUSED(wallet);
  413. Q_UNUSED(hash);
  414. Q_UNUSED(status);
  415. QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection);
  416. }
  417. static void ShowProgress(WalletModel *walletmodel, const std::string &title, int nProgress)
  418. {
  419. // emits signal "showProgress"
  420. QMetaObject::invokeMethod(walletmodel, "showProgress", Qt::QueuedConnection,
  421. Q_ARG(QString, QString::fromStdString(title)),
  422. Q_ARG(int, nProgress));
  423. }
  424. static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly)
  425. {
  426. QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection,
  427. Q_ARG(bool, fHaveWatchonly));
  428. }
  429. void WalletModel::subscribeToCoreSignals()
  430. {
  431. // Connect signals to wallet
  432. wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
  433. wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
  434. wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
  435. wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
  436. wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1));
  437. }
  438. void WalletModel::unsubscribeFromCoreSignals()
  439. {
  440. // Disconnect signals from wallet
  441. wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
  442. wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
  443. wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
  444. wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
  445. wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1));
  446. }
  447. // WalletModel::UnlockContext implementation
  448. WalletModel::UnlockContext WalletModel::requestUnlock()
  449. {
  450. bool was_locked = getEncryptionStatus() == Locked;
  451. if(was_locked)
  452. {
  453. // Request UI to unlock wallet
  454. Q_EMIT requireUnlock();
  455. }
  456. // If wallet is still locked, unlock was failed or cancelled, mark context as invalid
  457. bool valid = getEncryptionStatus() != Locked;
  458. return UnlockContext(this, valid, was_locked);
  459. }
  460. WalletModel::UnlockContext::UnlockContext(WalletModel *_wallet, bool _valid, bool _relock):
  461. wallet(_wallet),
  462. valid(_valid),
  463. relock(_relock)
  464. {
  465. }
  466. WalletModel::UnlockContext::~UnlockContext()
  467. {
  468. if(valid && relock)
  469. {
  470. wallet->setWalletLocked(true);
  471. }
  472. }
  473. void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
  474. {
  475. // Transfer context; old object no longer relocks wallet
  476. *this = rhs;
  477. rhs.relock = false;
  478. }
  479. bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
  480. {
  481. return wallet->GetPubKey(address, vchPubKeyOut);
  482. }
  483. bool WalletModel::IsSpendable(const CTxDestination& dest) const
  484. {
  485. return IsMine(*wallet, dest) & ISMINE_SPENDABLE;
  486. }
  487. bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const
  488. {
  489. return wallet->GetKey(address, vchPrivKeyOut);
  490. }
  491. // returns a list of COutputs from COutPoints
  492. void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
  493. {
  494. LOCK2(cs_main, wallet->cs_wallet);
  495. for (const COutPoint& outpoint : vOutpoints)
  496. {
  497. if (!wallet->mapWallet.count(outpoint.hash)) continue;
  498. int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
  499. if (nDepth < 0) continue;
  500. COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */);
  501. vOutputs.push_back(out);
  502. }
  503. }
  504. bool WalletModel::isSpent(const COutPoint& outpoint) const
  505. {
  506. LOCK2(cs_main, wallet->cs_wallet);
  507. return wallet->IsSpent(outpoint.hash, outpoint.n);
  508. }
  509. // AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address)
  510. void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
  511. {
  512. for (auto& group : wallet->ListCoins()) {
  513. auto& resultGroup = mapCoins[QString::fromStdString(CStarwelsAddress(group.first).ToString())];
  514. for (auto& coin : group.second) {
  515. resultGroup.emplace_back(std::move(coin));
  516. }
  517. }
  518. }
  519. bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const
  520. {
  521. LOCK2(cs_main, wallet->cs_wallet);
  522. return wallet->IsLockedCoin(hash, n);
  523. }
  524. void WalletModel::lockCoin(COutPoint& output)
  525. {
  526. LOCK2(cs_main, wallet->cs_wallet);
  527. wallet->LockCoin(output);
  528. }
  529. void WalletModel::unlockCoin(COutPoint& output)
  530. {
  531. LOCK2(cs_main, wallet->cs_wallet);
  532. wallet->UnlockCoin(output);
  533. }
  534. void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
  535. {
  536. LOCK2(cs_main, wallet->cs_wallet);
  537. wallet->ListLockedCoins(vOutpts);
  538. }
  539. void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
  540. {
  541. vReceiveRequests = wallet->GetDestValues("rr"); // receive request
  542. }
  543. bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
  544. {
  545. CTxDestination dest = CStarwelsAddress(sAddress).Get();
  546. std::stringstream ss;
  547. ss << nId;
  548. std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata
  549. LOCK(wallet->cs_wallet);
  550. if (sRequest.empty())
  551. return wallet->EraseDestData(dest, key);
  552. else
  553. return wallet->AddDestData(dest, key, sRequest);
  554. }
  555. bool WalletModel::transactionCanBeAbandoned(uint256 hash) const
  556. {
  557. return wallet->TransactionCanBeAbandoned(hash);
  558. }
  559. bool WalletModel::abandonTransaction(uint256 hash) const
  560. {
  561. LOCK2(cs_main, wallet->cs_wallet);
  562. return wallet->AbandonTransaction(hash);
  563. }
  564. bool WalletModel::transactionCanBeBumped(uint256 hash) const
  565. {
  566. LOCK2(cs_main, wallet->cs_wallet);
  567. const CWalletTx *wtx = wallet->GetWalletTx(hash);
  568. return wtx && SignalsOptInRBF(*wtx) && !wtx->mapValue.count("replaced_by_txid");
  569. }
  570. bool WalletModel::bumpFee(uint256 hash)
  571. {
  572. std::unique_ptr<CFeeBumper> feeBump;
  573. {
  574. CCoinControl coin_control;
  575. coin_control.signalRbf = true;
  576. LOCK2(cs_main, wallet->cs_wallet);
  577. feeBump.reset(new CFeeBumper(wallet, hash, coin_control, 0));
  578. }
  579. if (feeBump->getResult() != BumpFeeResult::OK)
  580. {
  581. QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" +
  582. (feeBump->getErrors().size() ? QString::fromStdString(feeBump->getErrors()[0]) : "") +")");
  583. return false;
  584. }
  585. // allow a user based fee verification
  586. QString questionString = tr("Do you want to increase the fee?");
  587. questionString.append("<br />");
  588. CAmount oldFee = feeBump->getOldFee();
  589. CAmount newFee = feeBump->getNewFee();
  590. questionString.append("<table style=\"text-align: left;\">");
  591. questionString.append("<tr><td>");
  592. questionString.append(tr("Current fee:"));
  593. questionString.append("</td><td>");
  594. questionString.append(StarwelsUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), oldFee));
  595. questionString.append("</td></tr><tr><td>");
  596. questionString.append(tr("Increase:"));
  597. questionString.append("</td><td>");
  598. questionString.append(StarwelsUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), newFee - oldFee));
  599. questionString.append("</td></tr><tr><td>");
  600. questionString.append(tr("New fee:"));
  601. questionString.append("</td><td>");
  602. questionString.append(StarwelsUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), newFee));
  603. questionString.append("</td></tr></table>");
  604. SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString);
  605. confirmationDialog.exec();
  606. QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result();
  607. // cancel sign&broadcast if users doesn't want to bump the fee
  608. if (retval != QMessageBox::Yes) {
  609. return false;
  610. }
  611. WalletModel::UnlockContext ctx(requestUnlock());
  612. if(!ctx.isValid())
  613. {
  614. return false;
  615. }
  616. // sign bumped transaction
  617. bool res = false;
  618. {
  619. LOCK2(cs_main, wallet->cs_wallet);
  620. res = feeBump->signTransaction(wallet);
  621. }
  622. if (!res) {
  623. QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction."));
  624. return false;
  625. }
  626. // commit the bumped transaction
  627. {
  628. LOCK2(cs_main, wallet->cs_wallet);
  629. res = feeBump->commit(wallet);
  630. }
  631. if(!res) {
  632. QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" +
  633. QString::fromStdString(feeBump->getErrors()[0])+")");
  634. return false;
  635. }
  636. return true;
  637. }
  638. bool WalletModel::isWalletEnabled()
  639. {
  640. return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
  641. }
  642. bool WalletModel::hdEnabled() const
  643. {
  644. return wallet->IsHDEnabled();
  645. }
  646. int WalletModel::getDefaultConfirmTarget() const
  647. {
  648. return nTxConfirmTarget;
  649. }
  650. bool WalletModel::getDefaultWalletRbf() const
  651. {
  652. return fWalletRbf;
  653. }