Add context menu option for rebroadcasting unconfirmed transactions

0.21
David Burkett 7 months ago
parent 19c4580308
commit 917312bb68

@ -368,6 +368,12 @@ public:
return feebumper::CommitTransaction(*m_wallet.get(), txid, std::move(mtx), errors, bumped_txid) == return feebumper::CommitTransaction(*m_wallet.get(), txid, std::move(mtx), errors, bumped_txid) ==
feebumper::Result::OK; feebumper::Result::OK;
} }
bool transactionCanBeRebroadcast(const uint256& txid) override { return m_wallet->TransactionCanBeRebroadcast(txid); }
bool rebroadcastTransaction(const uint256& txid) override
{
LOCK(m_wallet->cs_wallet);
return m_wallet->RebroadcastTransaction(txid);
}
CTransactionRef getTx(const uint256& txid) override CTransactionRef getTx(const uint256& txid) override
{ {
LOCK(m_wallet->cs_wallet); LOCK(m_wallet->cs_wallet);

@ -185,6 +185,12 @@ public:
std::vector<bilingual_str>& errors, std::vector<bilingual_str>& errors,
uint256& bumped_txid) = 0; uint256& bumped_txid) = 0;
//! Return whether transaction can be rebroadcast.
virtual bool transactionCanBeRebroadcast(const uint256& txid) = 0;
//! Rebroadcast transaction.
virtual bool rebroadcastTransaction(const uint256& txid) = 0;
//! Get a transaction. //! Get a transaction.
virtual CTransactionRef getTx(const uint256& txid) = 0; virtual CTransactionRef getTx(const uint256& txid) = 0;

@ -151,6 +151,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
abandonAction = new QAction(tr("Abandon transaction"), this); abandonAction = new QAction(tr("Abandon transaction"), this);
bumpFeeAction = new QAction(tr("Increase transaction fee"), this); bumpFeeAction = new QAction(tr("Increase transaction fee"), this);
bumpFeeAction->setObjectName("bumpFeeAction"); bumpFeeAction->setObjectName("bumpFeeAction");
rebroadcastAction = new QAction(tr("Rebroadcast transaction"), this);
copyAddressAction = new QAction(tr("Copy address"), this); copyAddressAction = new QAction(tr("Copy address"), this);
copyLabelAction = new QAction(tr("Copy label"), this); copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this); QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
@ -172,6 +173,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu->addSeparator(); contextMenu->addSeparator();
contextMenu->addAction(bumpFeeAction); contextMenu->addAction(bumpFeeAction);
contextMenu->addAction(abandonAction); contextMenu->addAction(abandonAction);
contextMenu->addAction(rebroadcastAction);
contextMenu->addAction(editLabelAction); contextMenu->addAction(editLabelAction);
connect(dateWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseDate); connect(dateWidget, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &TransactionView::chooseDate);
@ -187,6 +189,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
connect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee); connect(bumpFeeAction, &QAction::triggered, this, &TransactionView::bumpFee);
connect(abandonAction, &QAction::triggered, this, &TransactionView::abandonTx); connect(abandonAction, &QAction::triggered, this, &TransactionView::abandonTx);
connect(rebroadcastAction, &QAction::triggered, this, &TransactionView::rebroadcastTx);
connect(copyAddressAction, &QAction::triggered, this, &TransactionView::copyAddress); connect(copyAddressAction, &QAction::triggered, this, &TransactionView::copyAddress);
connect(copyLabelAction, &QAction::triggered, this, &TransactionView::copyLabel); connect(copyLabelAction, &QAction::triggered, this, &TransactionView::copyLabel);
connect(copyAmountAction, &QAction::triggered, this, &TransactionView::copyAmount); connect(copyAmountAction, &QAction::triggered, this, &TransactionView::copyAmount);
@ -394,6 +397,7 @@ void TransactionView::contextualMenu(const QPoint &point)
hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString()); hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
abandonAction->setEnabled(model->wallet().transactionCanBeAbandoned(hash)); abandonAction->setEnabled(model->wallet().transactionCanBeAbandoned(hash));
bumpFeeAction->setEnabled(model->wallet().transactionCanBeBumped(hash)); bumpFeeAction->setEnabled(model->wallet().transactionCanBeBumped(hash));
rebroadcastAction->setEnabled(model->wallet().transactionCanBeRebroadcast(hash));
copyAddressAction->setEnabled(GUIUtil::hasEntryData(transactionView, 0, TransactionTableModel::AddressRole)); copyAddressAction->setEnabled(GUIUtil::hasEntryData(transactionView, 0, TransactionTableModel::AddressRole));
copyLabelAction->setEnabled(GUIUtil::hasEntryData(transactionView, 0, TransactionTableModel::LabelRole)); copyLabelAction->setEnabled(GUIUtil::hasEntryData(transactionView, 0, TransactionTableModel::LabelRole));
@ -443,6 +447,21 @@ void TransactionView::bumpFee()
} }
} }
void TransactionView::rebroadcastTx()
{
if (!transactionView || !transactionView->selectionModel())
return;
QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);
// get the hash from the TxHashRole (QVariant / QString)
uint256 hash;
QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString();
hash.SetHex(hashQStr.toStdString());
// Rebroadcast the wallet transaction over the walletModel
model->wallet().rebroadcastTransaction(hash);
}
void TransactionView::copyAddress() void TransactionView::copyAddress()
{ {
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole); GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole);

@ -77,6 +77,7 @@ private:
QDateTimeEdit *dateTo; QDateTimeEdit *dateTo;
QAction *abandonAction{nullptr}; QAction *abandonAction{nullptr};
QAction *bumpFeeAction{nullptr}; QAction *bumpFeeAction{nullptr};
QAction *rebroadcastAction{nullptr};
QAction *copyAddressAction{nullptr}; QAction *copyAddressAction{nullptr};
QAction *copyLabelAction{nullptr}; QAction *copyLabelAction{nullptr};
@ -103,6 +104,7 @@ private Q_SLOTS:
void updateWatchOnlyColumn(bool fHaveWatchOnly); void updateWatchOnlyColumn(bool fHaveWatchOnly);
void abandonTx(); void abandonTx();
void bumpFee(); void bumpFee();
void rebroadcastTx();
Q_SIGNALS: Q_SIGNALS:
void doubleClicked(const QModelIndex&); void doubleClicked(const QModelIndex&);

@ -1175,6 +1175,49 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
return true; return true;
} }
bool CWallet::TransactionCanBeRebroadcast(const uint256& hashTx) const
{
LOCK(cs_wallet);
// Can't relay if wallet is not broadcasting
if (!GetBroadcastTransactions()) return false;
const CWalletTx* wtx = GetWalletTx(hashTx);
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0;
}
bool CWallet::RebroadcastTransaction(const uint256& hashTx)
{
LOCK(cs_wallet);
// Can't relay if wallet is not broadcasting
if (!GetBroadcastTransactions()) return false;
// Can't mark abandoned if confirmed or in mempool
auto it = mapWallet.find(hashTx);
assert(it != mapWallet.end());
const CWalletTx& wtx = it->second;
// Don't relay abandoned transactions
if (wtx.isAbandoned()) return false;
// Don't try to submit coinbase or HogEx transactions. These would fail anyway but would
// cause log spam.
if (wtx.IsCoinBase() || wtx.IsHogEx()) return false;
// Don't try to submit conflicted or confirmed transactions.
if (wtx.GetDepthInMainChain() != 0) return false;
// Submit transaction to mempool for relay
WalletLogPrintf("Submitting wtx %s to mempool for relay\n", wtx.GetHash().ToString());
std::string err_string;
const bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, true, err_string);
if (!ret) {
WalletLogPrintf("RebroadcastTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
}
return ret;
}
void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx) void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx)
{ {
LOCK(cs_wallet); LOCK(cs_wallet);

@ -1293,6 +1293,12 @@ public:
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */ /* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
bool AbandonTransaction(const uint256& hashTx); bool AbandonTransaction(const uint256& hashTx);
/** Return whether transaction can be rebroadcast */
bool TransactionCanBeRebroadcast(const uint256& hashTx) const;
/* Rebroadcast a transaction. */
bool RebroadcastTransaction(const uint256& hashTx);
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
bool MarkReplaced(const uint256& originalHash, const uint256& newHash); bool MarkReplaced(const uint256& originalHash, const uint256& newHash);

Loading…
Cancel
Save