Bitcoin Core 22.99.0
P2P Digital Currency
paymentserver.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2020 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#if defined(HAVE_CONFIG_H)
7#endif
8
9#include <qt/paymentserver.h>
10
11#include <qt/bitcoinunits.h>
12#include <qt/guiutil.h>
13#include <qt/optionsmodel.h>
14
15#include <chainparams.h>
16#include <interfaces/node.h>
17#include <key_io.h>
18#include <node/ui_interface.h>
19#include <policy/policy.h>
20#include <util/system.h>
21#include <wallet/wallet.h>
22
23#include <cstdlib>
24#include <memory>
25
26#include <QApplication>
27#include <QByteArray>
28#include <QDataStream>
29#include <QDebug>
30#include <QFile>
31#include <QFileOpenEvent>
32#include <QHash>
33#include <QList>
34#include <QLocalServer>
35#include <QLocalSocket>
36#include <QStringList>
37#include <QUrlQuery>
38
39const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
40const QString BITCOIN_IPC_PREFIX("bitcoin:");
41
42//
43// Create a name that is unique for:
44// testnet / non-testnet
45// data directory
46//
47static QString ipcServerName()
48{
49 QString name("BitcoinQt");
50
51 // Append a simple hash of the datadir
52 // Note that gArgs.GetDataDirNet() returns a different path
53 // for -testnet versus main net
55 name.append(QString::number(qHash(ddir)));
56
57 return name;
58}
59
60//
61// We store payment URIs and requests received before
62// the main GUI window is up and ready to ask the user
63// to send payment.
64
65static QSet<QString> savedPaymentRequests;
66
67//
68// Sending to the server is done synchronously, at startup.
69// If the server isn't already running, startup continues,
70// and the items in savedPaymentRequest will be handled
71// when uiReady() is called.
72//
73// Warning: ipcSendCommandLine() is called early in init,
74// so don't use "Q_EMIT message()", but "QMessageBox::"!
75//
76void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
77{
78 for (int i = 1; i < argc; i++)
79 {
80 QString arg(argv[i]);
81 if (arg.startsWith("-"))
82 continue;
83
84 // If the bitcoin: URI contains a payment request, we are not able to detect the
85 // network as that would require fetching and parsing the payment request.
86 // That means clicking such an URI which contains a testnet payment request
87 // will start a mainnet instance and throw a "wrong network" error.
88 if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
89 {
90 if (savedPaymentRequests.contains(arg)) continue;
91 savedPaymentRequests.insert(arg);
92
94 if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
95 {
96 auto tempChainParams = CreateChainParams(gArgs, CBaseChainParams::MAIN);
97
98 if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
100 } else {
102 if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
104 }
105 }
106 }
107 }
108 }
109}
110
111//
112// Sending to the server is done synchronously, at startup.
113// If the server isn't already running, startup continues,
114// and the items in savedPaymentRequest will be handled
115// when uiReady() is called.
116//
118{
119 bool fResult = false;
120 for (const QString& r : savedPaymentRequests)
121 {
122 QLocalSocket* socket = new QLocalSocket();
123 socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
124 if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT))
125 {
126 delete socket;
127 socket = nullptr;
128 return false;
129 }
130
131 QByteArray block;
132 QDataStream out(&block, QIODevice::WriteOnly);
133 out.setVersion(QDataStream::Qt_4_0);
134 out << r;
135 out.device()->seek(0);
136
137 socket->write(block);
138 socket->flush();
139 socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);
140 socket->disconnectFromServer();
141
142 delete socket;
143 socket = nullptr;
144 fResult = true;
145 }
146
147 return fResult;
148}
149
150PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
151 QObject(parent),
152 saveURIs(true),
153 uriServer(nullptr),
154 optionsModel(nullptr)
155{
156 // Install global event filter to catch QFileOpenEvents
157 // on Mac: sent when you click bitcoin: links
158 // other OSes: helpful when dealing with payment request files
159 if (parent)
160 parent->installEventFilter(this);
161
162 QString name = ipcServerName();
163
164 // Clean up old socket leftover from a crash:
165 QLocalServer::removeServer(name);
166
167 if (startLocalServer)
168 {
169 uriServer = new QLocalServer(this);
170
171 if (!uriServer->listen(name)) {
172 // constructor is called early in init, so don't use "Q_EMIT message()" here
173 QMessageBox::critical(nullptr, tr("Payment request error"),
174 tr("Cannot start bitcoin: click-to-pay handler"));
175 }
176 else {
177 connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection);
178 }
179 }
180}
181
183{
184}
185
186//
187// OSX-specific way of handling bitcoin: URIs
188//
189bool PaymentServer::eventFilter(QObject *object, QEvent *event)
190{
191 if (event->type() == QEvent::FileOpen) {
192 QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event);
193 if (!fileEvent->file().isEmpty())
194 handleURIOrFile(fileEvent->file());
195 else if (!fileEvent->url().isEmpty())
196 handleURIOrFile(fileEvent->url().toString());
197
198 return true;
199 }
200
201 return QObject::eventFilter(object, event);
202}
203
205{
206 saveURIs = false;
207 for (const QString& s : savedPaymentRequests)
208 {
210 }
211 savedPaymentRequests.clear();
212}
213
214void PaymentServer::handleURIOrFile(const QString& s)
215{
216 if (saveURIs)
217 {
218 savedPaymentRequests.insert(s);
219 return;
220 }
221
222 if (s.startsWith("bitcoin://", Qt::CaseInsensitive))
223 {
224 Q_EMIT message(tr("URI handling"), tr("'bitcoin://' is not a valid URI. Use 'bitcoin:' instead."),
226 }
227 else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
228 {
229 QUrlQuery uri((QUrl(s)));
230 // normal URI
231 {
232 SendCoinsRecipient recipient;
233 if (GUIUtil::parseBitcoinURI(s, &recipient))
234 {
235 std::string error_msg;
236 const CTxDestination dest = DecodeDestination(recipient.address.toStdString(), error_msg);
237
238 if (!IsValidDestination(dest)) {
239 if (uri.hasQueryItem("r")) { // payment request
240 Q_EMIT message(tr("URI handling"),
241 tr("Cannot process payment request because BIP70 is not supported.\n"
242 "Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.\n"
243 "If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
245 }
246 Q_EMIT message(tr("URI handling"), QString::fromStdString(error_msg),
248 }
249 else
250 Q_EMIT receivedPaymentRequest(recipient);
251 }
252 else
253 Q_EMIT message(tr("URI handling"),
254 tr("URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
256
257 return;
258 }
259 }
260
261 if (QFile::exists(s)) // payment request file
262 {
263 Q_EMIT message(tr("Payment request file handling"),
264 tr("Cannot process payment request because BIP70 is not supported.\n"
265 "Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.\n"
266 "If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
268 }
269}
270
272{
273 QLocalSocket *clientConnection = uriServer->nextPendingConnection();
274
275 while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
276 clientConnection->waitForReadyRead();
277
278 connect(clientConnection, &QLocalSocket::disconnected, clientConnection, &QLocalSocket::deleteLater);
279
280 QDataStream in(clientConnection);
281 in.setVersion(QDataStream::Qt_4_0);
282 if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
283 return;
284 }
285 QString msg;
286 in >> msg;
287
288 handleURIOrFile(msg);
289}
290
292{
293 this->optionsModel = _optionsModel;
294}
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given chain name.
std::unique_ptr< const CChainParams > CreateChainParams(const ArgsManager &args, const std::string &chain)
Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:288
static const std::string TESTNET
static const std::string MAIN
Chain name strings.
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:39
static bool ipcSendCommandLine()
void setOptionsModel(OptionsModel *optionsModel)
PaymentServer(QObject *parent, bool startLocalServer=true)
void message(const QString &title, const QString &message, unsigned int style)
void handleURIConnection()
static void ipcParseCommandLine(int argc, char *argv[])
QLocalServer * uriServer
void receivedPaymentRequest(SendCoinsRecipient)
bool eventFilter(QObject *object, QEvent *event) override
void handleURIOrFile(const QString &s)
OptionsModel * optionsModel
bool IsValidDestinationString(const std::string &str, const CChainParams &params)
Definition: key_io.cpp:272
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg)
Definition: key_io.cpp:261
QString boostPathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
Definition: guiutil.cpp:659
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:132
static bool exists(const path &p)
Definition: fs.h:77
static QString ipcServerName()
const int BITCOIN_IPC_CONNECT_TIMEOUT
const QString BITCOIN_IPC_PREFIX("bitcoin:")
static QSet< QString > savedPaymentRequests
const char * name
Definition: rest.cpp:43
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:332
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:157
ArgsManager gArgs
Definition: system.cpp:85