-
Notifications
You must be signed in to change notification settings - Fork 126
Add option for sending Tx to specific peer in UI #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 29.x-knots
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<ui version="4.0"> | ||
<class>SendTransactionDialog</class> | ||
<widget class="QDialog" name="SendTransactionDialog"> | ||
<property name="geometry"> | ||
<rect> | ||
<x>0</x> | ||
<y>0</y> | ||
<width>600</width> | ||
<height>150</height> | ||
</rect> | ||
</property> | ||
<property name="windowTitle"> | ||
<string>Send Transaction to Peer</string> | ||
</property> | ||
<layout class="QVBoxLayout" name="verticalLayout"> | ||
<item> | ||
<layout class="QHBoxLayout" name="transactionLayout"> | ||
<item> | ||
<widget class="QLabel" name="transactionLabel"> | ||
<property name="text"> | ||
<string>Raw Transaction (hex):</string> | ||
</property> | ||
</widget> | ||
</item> | ||
<item> | ||
<widget class="QPlainTextEdit" name="transactionEdit"> | ||
<property name="placeholderText"> | ||
<string>Enter raw transaction in hexadecimal format...</string> | ||
</property> | ||
<property name="maximumBlockCount"> | ||
<number>1</number> | ||
</property> | ||
</widget> | ||
</item> | ||
<item> | ||
<widget class="QToolButton" name="pasteButton"> | ||
<property name="toolTip"> | ||
<string>Paste transaction from clipboard</string> | ||
</property> | ||
<property name="text"> | ||
<string/> | ||
</property> | ||
<property name="icon"> | ||
<iconset resource="../bitcoin.qrc"> | ||
<normaloff>:/icons/editpaste</normaloff>:/icons/editpaste | ||
</iconset> | ||
</property> | ||
<property name="iconSize"> | ||
<size> | ||
<width>22</width> | ||
<height>22</height> | ||
</size> | ||
</property> | ||
<property name="shortcut"> | ||
<string>Alt+P</string> | ||
</property> | ||
</widget> | ||
</item> | ||
</layout> | ||
</item> | ||
<item> | ||
<spacer name="verticalSpacer"> | ||
<property name="orientation"> | ||
<enum>Qt::Vertical</enum> | ||
</property> | ||
<property name="sizeHint" stdset="0"> | ||
<size> | ||
<width>20</width> | ||
<height>40</height> | ||
</size> | ||
</property> | ||
</spacer> | ||
</item> | ||
<item> | ||
<widget class="QDialogButtonBox" name="buttonBox"> | ||
<property name="orientation"> | ||
<enum>Qt::Horizontal</enum> | ||
</property> | ||
<property name="standardButtons"> | ||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
</property> | ||
</widget> | ||
</item> | ||
</layout> | ||
</widget> | ||
<resources/> | ||
<connections> | ||
<connection> | ||
<sender>buttonBox</sender> | ||
<signal>accepted()</signal> | ||
<receiver>SendTransactionDialog</receiver> | ||
<slot>accept()</slot> | ||
</connection> | ||
<connection> | ||
<sender>buttonBox</sender> | ||
<signal>rejected()</signal> | ||
<receiver>SendTransactionDialog</receiver> | ||
<slot>reject()</slot> | ||
</connection> | ||
</connections> | ||
</ui> |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -169,6 +169,8 @@ class PeerIdViewDelegate : public QStyledItemDelegate | |||||
}; | ||||||
|
||||||
#include <qt/rpcconsole.moc> | ||||||
#include <qt/sendtransactiondialog.h> | ||||||
#include <QMessageBox> | ||||||
|
||||||
/** | ||||||
* Split shell command line into a list of arguments and optionally execute the command(s). | ||||||
|
@@ -830,6 +832,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ | |||||
}); | ||||||
peersTableContextMenu->addSeparator(); | ||||||
peersTableContextMenu->addAction(tr("&Disconnect"), this, &RPCConsole::disconnectSelectedNode); | ||||||
peersTableContextMenu->addAction(tr("Send &Transaction"), this, &RPCConsole::sendTransactionToPeer); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &hour"), [this] { banSelectedNode(60 * 60); }); | ||||||
peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 d&ay"), [this] { banSelectedNode(60 * 60 * 24); }); | ||||||
peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &week"), [this] { banSelectedNode(60 * 60 * 24 * 7); }); | ||||||
|
@@ -1637,3 +1640,44 @@ void RPCConsole::updateConsoleStyleSheet() | |||||
scrollbar->setValue(oldScrollValue); | ||||||
#endif | ||||||
} | ||||||
|
||||||
void RPCConsole::sendTransactionToPeer() | ||||||
{ | ||||||
if (!clientModel) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One line or {} please |
||||||
return; | ||||||
|
||||||
QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); | ||||||
if (nodes.isEmpty()) { | ||||||
return; | ||||||
} | ||||||
|
||||||
NodeId peerId = nodes.first().data().toLongLong(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like if multiple peers are selected, you would want to send to all of them? |
||||||
|
||||||
SendTransactionDialog dialog(platformStyle, this); | ||||||
if (dialog.exec() == QDialog::Accepted) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
QString txHex = dialog.getTransactionHex(); | ||||||
|
||||||
if (!txHex.isEmpty()) { | ||||||
QByteArray txBytes = QByteArray::fromHex(txHex.toUtf8()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't need UTF-8 for hex |
||||||
QString txHexForRpc = QString::fromUtf8(txBytes.toHex()); | ||||||
Comment on lines
+1661
to
+1662
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the round trip? |
||||||
|
||||||
QString rpcCommand = QString("sendmsgtopeer %1 \"tx\" \"%2\"").arg(peerId).arg(txHexForRpc); | ||||||
|
||||||
std::string result; | ||||||
std::string executableCommand = rpcCommand.toStdString() + "\n"; | ||||||
|
||||||
try { | ||||||
if (RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, nullptr)) { | ||||||
QMessageBox::information(this, tr("Success"), | ||||||
tr("Transaction sent successfully to peer %1.").arg(peerId)); | ||||||
} else { | ||||||
QMessageBox::warning(this, tr("Error"), | ||||||
tr("Failed to send transaction to peer %1.").arg(peerId)); | ||||||
} | ||||||
} catch (const std::exception& e) { | ||||||
QMessageBox::critical(this, tr("Error"), | ||||||
tr("Error sending transaction to peer %1: %2").arg(peerId).arg(QString::fromStdString(e.what()))); | ||||||
} | ||||||
} | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright (c) 2025 Luke Dashjr | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <qt/sendtransactiondialog.h> | ||
#include <qt/forms/ui_sendtransactiondialog.h> | ||
|
||
#include <qt/guiutil.h> | ||
#include <qt/platformstyle.h> | ||
|
||
#include <QAbstractButton> | ||
#include <QPlainTextEdit> | ||
#include <QClipboard> | ||
#include <QApplication> | ||
#include <QRegularExpression> | ||
|
||
SendTransactionDialog::SendTransactionDialog(const PlatformStyle* platformStyle, QWidget* parent) | ||
: QDialog(parent, GUIUtil::dialog_flags), | ||
ui(new Ui::SendTransactionDialog), | ||
m_platform_style(platformStyle) | ||
{ | ||
ui->setupUi(this); | ||
ui->pasteButton->setIcon(m_platform_style->SingleColorIcon(":/icons/editpaste")); | ||
|
||
QObject::connect(ui->pasteButton, &QAbstractButton::clicked, this, &SendTransactionDialog::on_pasteButton_clicked); | ||
|
||
GUIUtil::handleCloseWindowShortcut(this); | ||
} | ||
|
||
SendTransactionDialog::~SendTransactionDialog() | ||
{ | ||
delete ui; | ||
} | ||
|
||
QString SendTransactionDialog::getTransactionHex() const | ||
{ | ||
return ui->transactionEdit->toPlainText().trimmed(); | ||
} | ||
|
||
void SendTransactionDialog::accept() | ||
{ | ||
QString txHex = getTransactionHex(); | ||
|
||
if (txHex.isEmpty()) { | ||
ui->transactionEdit->setFocus(); | ||
return; | ||
} | ||
|
||
txHex = txHex.replace(QRegularExpression("\\s+"), ""); | ||
bool isValidHex = true; | ||
for (const QChar& c : txHex) { | ||
if (!c.isDigit() && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) { | ||
isValidHex = false; | ||
break; | ||
} | ||
} | ||
|
||
if (!isValidHex || txHex.length() % 2 != 0) { | ||
ui->transactionEdit->setFocus(); | ||
return; | ||
} | ||
|
||
QDialog::accept(); | ||
} | ||
|
||
void SendTransactionDialog::changeEvent(QEvent* e) | ||
{ | ||
if (e->type() == QEvent::PaletteChange) { | ||
ui->pasteButton->setIcon(m_platform_style->SingleColorIcon(":/icons/editpaste")); | ||
} | ||
|
||
QDialog::changeEvent(e); | ||
} | ||
|
||
void SendTransactionDialog::on_pasteButton_clicked() | ||
{ | ||
QClipboard* clipboard = QApplication::clipboard(); | ||
QString clipboardText = clipboard->text(); | ||
|
||
if (!clipboardText.isEmpty()) { | ||
ui->transactionEdit->setPlainText(clipboardText); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Copyright (c) 2025 Luke Dashjr | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#ifndef BITCOIN_QT_SENDTRANSACTIONDIALOG_H | ||
#define BITCOIN_QT_SENDTRANSACTIONDIALOG_H | ||
|
||
#include <QDialog> | ||
|
||
class PlatformStyle; | ||
|
||
namespace Ui { | ||
class SendTransactionDialog; | ||
} | ||
|
||
class SendTransactionDialog : public QDialog | ||
{ | ||
Q_OBJECT | ||
|
||
public: | ||
explicit SendTransactionDialog(const PlatformStyle* platformStyle, QWidget* parent); | ||
~SendTransactionDialog(); | ||
|
||
QString getTransactionHex() const; | ||
|
||
protected Q_SLOTS: | ||
void accept() override; | ||
void changeEvent(QEvent* e) override; | ||
|
||
private Q_SLOTS: | ||
void on_pasteButton_clicked(); | ||
|
||
private: | ||
Ui::SendTransactionDialog* ui; | ||
const PlatformStyle* m_platform_style; | ||
}; | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason we can't just use
QInputDialog
for this?