void WalletView::incomingTransaction(const QModelIndex& parent, int start, int /*end*/) { // Prevent balloon-spam when initial block download is in progress if(!walletModel || !clientModel || clientModel->inInitialBlockDownload()) return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); QString address = ttm->index(start, TransactionTableModel::ToAddress, parent).data().toString(); gui->incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address); }
void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { if(!walletModel || !clientModel) return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); if(!clientModel->inInitialBlockDownload()) { // On new transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent) .data().toString(); QString type = ttm->index(start, TransactionTableModel::Type, parent) .data().toString(); QString address = ttm->index(start, TransactionTableModel::ToAddress, parent) .data().toString(); QIcon icon = qvariant_cast<QIcon>(ttm->index(start, TransactionTableModel::ToAddress, parent) .data(Qt::DecorationRole)); notificator->notify(Notificator::Information, (amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"), tr("Date: %1\n" "Amount: %2\n" "Type: %3\n" "Address: %4\n") .arg(date) .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true)) .arg(type) .arg(address), icon); } }
void ShadowGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { if(!walletModel || !clientModel || clientModel->inInitialBlockDownload() || !nNodeState == NS_READY) return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); // Ignore staking transactions... We should create an Option, and allow people to select/deselect what // type of transactions they want to see if(!(clientModel->getOptionsModel()->getNotifications().first() == "*") && ! clientModel->getOptionsModel()->getNotifications().contains(type)) return; // On new transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); QString address = ttm->index(start, TransactionTableModel::ToAddress, parent).data().toString(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); QIcon icon = qvariant_cast<QIcon>(ttm->index(start, TransactionTableModel::ToAddress, parent).data(Qt::DecorationRole)); notificator->notify(Notificator::Information, (amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"), tr("Date: %1\n" "Amount: %2\n" "Type: %3\n" "Address: %4\n") .arg(date) .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true)) .arg(type) .arg(address), icon); }
void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { TransactionTableModel *ttm = model->getTransactionTableModel(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); if(amount>0 && !model->inInitialBlockDownload()) { // On incoming transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam QString date = ttm->index(start, TransactionTableModel::Date, parent) .data().toString(); QString type = ttm->index(start, TransactionTableModel::Type, parent) .data().toString(); QString address = ttm->index(start, TransactionTableModel::ToAddress, parent) .data().toString(); // TODO! Make chain of notifications (if Freedesktop notify not supported use Qt Notify and add this to Notification) /*trayIcon->showMessage(tr("Incoming transaction"), tr("Date: ") + date + "\n" + tr("Amount: ") + QString::fromStdString(FormatMoney(amount, true)) + "\n" + tr("Type: ") + type + "\n" + tr("Address: ") + address + "\n", QSystemTrayIcon::Information);*/ // TODO! Icon hardcoded because of that QIcon don't return path of file Notifiction::instance()->notify(tr("Date: %1\n" "Amount: %2\n" "Type: %3\n" "Address: %4") .arg(date) .arg(QString::fromStdString(FormatMoney(amount, true))) .arg(type) .arg(address), tr("Incoming transaction"), QLatin1String("/usr/share/icons/oxygen/64x64/actions/main-receive.png")); } }
//! Simple qt wallet tests. // // Test widgets can be debugged interactively calling show() on them and // manually running the event loop, e.g.: // // sendCoinsDialog.show(); // QEventLoop().exec(); // // This also requires overriding the default minimal Qt platform: // // src/qt/test/test_bitcoin-qt -platform xcb # Linux // src/qt/test/test_bitcoin-qt -platform windows # Windows // src/qt/test/test_bitcoin-qt -platform cocoa # macOS void WalletTests::walletTests() { // Set up wallet and chain with 101 blocks (1 mature block for spending). TestChain100Setup test; test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); bitdb.MakeMock(); std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat")); CWallet wallet(std::move(dbw)); bool firstRun; wallet.LoadWallet(firstRun); { LOCK(wallet.cs_wallet); wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive"); wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); } wallet.ScanForWalletTransactions(chainActive.Genesis(), true); wallet.SetBroadcastTransactions(true); // Create widgets for sending coins and listing transactions. std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); SendCoinsDialog sendCoinsDialog(platformStyle.get()); OptionsModel optionsModel; WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel); sendCoinsDialog.setModel(&walletModel); // Send two transactions, and verify they are added to transaction list. TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 101); uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN); uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN); QCOMPARE(transactionTableModel->rowCount({}), 103); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); bitdb.Flush(true); bitdb.Reset(); }
void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/) { // Prevent balloon-spam when initial block download is in progress if (!walletModel || !clientModel || clientModel->node().isInitialBlockDownload()) return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); if (!ttm || ttm->processingQueuedTransactions()) return; QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); QModelIndex index = ttm->index(start, 0, parent); QString address = ttm->data(index, TransactionTableModel::AddressRole).toString(); QString label = ttm->data(index, TransactionTableModel::LabelRole).toString(); Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, walletModel->getWalletName()); }
/* Update our model of the wallet incrementally, to synchronize our model of the wallet with that of the core. Call with transaction that was added, removed or changed. */ void updateWallet(const uint256 &hash, int status, bool showTransaction) { qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status); // Find bounds of this transaction in model QList<TransactionRecord>::iterator lower = qLowerBound( cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); QList<TransactionRecord>::iterator upper = qUpperBound( cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan()); int lowerIndex = (lower - cachedWallet.begin()); int upperIndex = (upper - cachedWallet.begin()); bool inModel = (lower != upper); if(status == CT_UPDATED) { if(showTransaction && !inModel) status = CT_NEW; /* Not in model, but want to show, treat as new */ if(!showTransaction && inModel) status = CT_DELETED; /* In model, but want to hide, treat as deleted */ } qDebug() << " inModel=" + QString::number(inModel) + " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) + " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status); switch(status) { case CT_NEW: if(inModel) { qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is already in model"; break; } if(showTransaction) { LOCK2(cs_main, wallet->cs_wallet); // Find transaction in wallet std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash); if(mi == wallet->mapWallet.end()) { qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet"; break; } // Added -- insert at the right position QList<TransactionRecord> toInsert = TransactionRecord::decomposeTransaction(wallet, mi->second); if(!toInsert.isEmpty()) /* only if something to insert */ { parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); int insert_idx = lowerIndex; for (const TransactionRecord &rec : toInsert) { cachedWallet.insert(insert_idx, rec); insert_idx += 1; } parent->endInsertRows(); } } break; case CT_DELETED: if(!inModel) { qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_DELETED, but transaction is not in model"; break; } // Removed -- remove entire transaction from table parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); cachedWallet.erase(lower, upper); parent->endRemoveRows(); break; case CT_UPDATED: // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for // visible transactions. for (int i = lowerIndex; i < upperIndex; i++) { TransactionRecord *rec = &cachedWallet[i]; rec->status.needsUpdate = true; } break; } }