walletframe.cpp raw
1 // Copyright (c) 2011-present 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 #include <qt/walletframe.h>
6
7 #include <node/interface_ui.h>
8 #include <psbt.h>
9 #include <qt/guiutil.h>
10 #include <qt/overviewpage.h>
11 #include <qt/psbtoperationsdialog.h>
12 #include <qt/walletmodel.h>
13 #include <qt/walletview.h>
14 #include <util/fs.h>
15 #include <util/fs_helpers.h>
16
17 #include <cassert>
18 #include <fstream>
19 #include <string>
20
21 #include <QApplication>
22 #include <QClipboard>
23 #include <QGroupBox>
24 #include <QHBoxLayout>
25 #include <QLabel>
26 #include <QPushButton>
27 #include <QVBoxLayout>
28
29 WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, QWidget* parent)
30 : QFrame(parent),
31 platformStyle(_platformStyle),
32 m_size_hint(OverviewPage{platformStyle, nullptr}.sizeHint())
33 {
34 // Leave HBox hook for adding a list view later
35 QHBoxLayout *walletFrameLayout = new QHBoxLayout(this);
36 setContentsMargins(0,0,0,0);
37 walletStack = new QStackedWidget(this);
38 walletFrameLayout->setContentsMargins(0,0,0,0);
39 walletFrameLayout->addWidget(walletStack);
40
41 // hbox for no wallet
42 QGroupBox* no_wallet_group = new QGroupBox(walletStack);
43 QVBoxLayout* no_wallet_layout = new QVBoxLayout(no_wallet_group);
44
45 QLabel *noWallet = new QLabel(tr("No wallet has been loaded.\nGo to File > Open Wallet to load a wallet.\n- OR -"));
46 noWallet->setAlignment(Qt::AlignCenter);
47 no_wallet_layout->addWidget(noWallet, 0, Qt::AlignHCenter | Qt::AlignBottom);
48
49 // A button for create wallet dialog
50 QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack);
51 connect(create_wallet_button, &QPushButton::clicked, this, &WalletFrame::createWalletButtonClicked);
52 no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
53 no_wallet_group->setLayout(no_wallet_layout);
54
55 walletStack->addWidget(no_wallet_group);
56 }
57
58 WalletFrame::~WalletFrame() = default;
59
60 void WalletFrame::setClientModel(ClientModel *_clientModel)
61 {
62 this->clientModel = _clientModel;
63
64 for (auto i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) {
65 i.value()->setClientModel(_clientModel);
66 }
67 }
68
69 bool WalletFrame::addView(WalletView* walletView)
70 {
71 if (!clientModel) return false;
72
73 if (mapWalletViews.contains(walletView->getWalletModel())) return false;
74
75 walletView->setClientModel(clientModel);
76 walletView->showOutOfSyncWarning(bOutOfSync);
77
78 WalletView* current_wallet_view = currentWalletView();
79 if (current_wallet_view) {
80 walletView->setCurrentIndex(current_wallet_view->currentIndex());
81 } else {
82 walletView->gotoOverviewPage();
83 }
84
85 walletStack->addWidget(walletView);
86 mapWalletViews[walletView->getWalletModel()] = walletView;
87
88 return true;
89 }
90
91 void WalletFrame::setCurrentWallet(WalletModel* wallet_model)
92 {
93 if (!mapWalletViews.contains(wallet_model)) return;
94
95 // Stop the effect of hidden widgets on the size hint of the shown one in QStackedWidget.
96 WalletView* view_about_to_hide = currentWalletView();
97 if (view_about_to_hide) {
98 QSizePolicy sp = view_about_to_hide->sizePolicy();
99 sp.setHorizontalPolicy(QSizePolicy::Ignored);
100 view_about_to_hide->setSizePolicy(sp);
101 }
102
103 WalletView *walletView = mapWalletViews.value(wallet_model);
104 assert(walletView);
105
106 // Set or restore the default QSizePolicy which could be set to QSizePolicy::Ignored previously.
107 QSizePolicy sp = walletView->sizePolicy();
108 sp.setHorizontalPolicy(QSizePolicy::Preferred);
109 walletView->setSizePolicy(sp);
110 walletView->updateGeometry();
111
112 walletStack->setCurrentWidget(walletView);
113
114 Q_EMIT currentWalletSet();
115 }
116
117 void WalletFrame::removeWallet(WalletModel* wallet_model)
118 {
119 if (!mapWalletViews.contains(wallet_model)) return;
120
121 WalletView *walletView = mapWalletViews.take(wallet_model);
122 walletStack->removeWidget(walletView);
123 delete walletView;
124 }
125
126 void WalletFrame::removeAllWallets()
127 {
128 QMap<WalletModel*, WalletView*>::const_iterator i;
129 for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
130 walletStack->removeWidget(i.value());
131 mapWalletViews.clear();
132 }
133
134 bool WalletFrame::handlePaymentRequest(const SendCoinsRecipient &recipient)
135 {
136 WalletView *walletView = currentWalletView();
137 if (!walletView)
138 return false;
139
140 return walletView->handlePaymentRequest(recipient);
141 }
142
143 void WalletFrame::showOutOfSyncWarning(bool fShow)
144 {
145 bOutOfSync = fShow;
146 QMap<WalletModel*, WalletView*>::const_iterator i;
147 for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
148 i.value()->showOutOfSyncWarning(fShow);
149 }
150
151 void WalletFrame::gotoOverviewPage()
152 {
153 QMap<WalletModel*, WalletView*>::const_iterator i;
154 for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
155 i.value()->gotoOverviewPage();
156 }
157
158 void WalletFrame::gotoHistoryPage()
159 {
160 QMap<WalletModel*, WalletView*>::const_iterator i;
161 for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
162 i.value()->gotoHistoryPage();
163 }
164
165 void WalletFrame::gotoReceiveCoinsPage()
166 {
167 QMap<WalletModel*, WalletView*>::const_iterator i;
168 for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
169 i.value()->gotoReceiveCoinsPage();
170 }
171
172 void WalletFrame::gotoSendCoinsPage(QString addr)
173 {
174 QMap<WalletModel*, WalletView*>::const_iterator i;
175 for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
176 i.value()->gotoSendCoinsPage(addr);
177 }
178
179 void WalletFrame::gotoSignMessageTab(QString addr)
180 {
181 WalletView *walletView = currentWalletView();
182 if (walletView)
183 walletView->gotoSignMessageTab(addr);
184 }
185
186 void WalletFrame::gotoVerifyMessageTab(QString addr)
187 {
188 WalletView *walletView = currentWalletView();
189 if (walletView)
190 walletView->gotoVerifyMessageTab(addr);
191 }
192
193 void WalletFrame::gotoLoadPSBT(bool from_clipboard)
194 {
195 std::vector<unsigned char> data;
196
197 if (from_clipboard) {
198 std::string raw = QApplication::clipboard()->text().toStdString();
199 auto result = DecodeBase64(raw);
200 if (!result) {
201 Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
202 return;
203 }
204 data = std::move(*result);
205 } else {
206 QString filename = GUIUtil::getOpenFileName(this,
207 tr("Load Transaction Data"), QString(),
208 tr("Partially Signed Transaction (*.psbt)"), nullptr);
209 if (filename.isEmpty()) return;
210 if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
211 Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
212 return;
213 }
214 std::ifstream in{filename.toLocal8Bit().data(), std::ios::binary};
215 data.assign(std::istreambuf_iterator<char>{in}, {});
216
217 // Some psbt files may be base64 strings in the file rather than binary data
218 std::string b64_str{data.begin(), data.end()};
219 b64_str.erase(b64_str.find_last_not_of(" \t\n\r\f\v") + 1); // Trim trailing whitespace
220 auto b64_dec = DecodeBase64(b64_str);
221 if (b64_dec.has_value()) {
222 data = b64_dec.value();
223 }
224 }
225
226 util::Result<PartiallySignedTransaction> psbt_res = DecodeRawPSBT(MakeByteSpan(data));
227 if (!psbt_res) {
228 Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(util::ErrorString(psbt_res).original), CClientUIInterface::MSG_ERROR);
229 return;
230 }
231
232 auto dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
233 dlg->openWithPSBT(*psbt_res);
234 GUIUtil::ShowModalDialogAsynchronously(dlg);
235 }
236
237 void WalletFrame::encryptWallet()
238 {
239 WalletView *walletView = currentWalletView();
240 if (walletView)
241 walletView->encryptWallet();
242 }
243
244 void WalletFrame::backupWallet()
245 {
246 WalletView *walletView = currentWalletView();
247 if (walletView)
248 walletView->backupWallet();
249 }
250
251 void WalletFrame::changePassphrase()
252 {
253 WalletView *walletView = currentWalletView();
254 if (walletView)
255 walletView->changePassphrase();
256 }
257
258 void WalletFrame::unlockWallet()
259 {
260 WalletView *walletView = currentWalletView();
261 if (walletView)
262 walletView->unlockWallet();
263 }
264
265 void WalletFrame::usedSendingAddresses()
266 {
267 WalletView *walletView = currentWalletView();
268 if (walletView)
269 walletView->usedSendingAddresses();
270 }
271
272 void WalletFrame::usedReceivingAddresses()
273 {
274 WalletView *walletView = currentWalletView();
275 if (walletView)
276 walletView->usedReceivingAddresses();
277 }
278
279 WalletView* WalletFrame::currentWalletView() const
280 {
281 return qobject_cast<WalletView*>(walletStack->currentWidget());
282 }
283
284 WalletModel* WalletFrame::currentWalletModel() const
285 {
286 WalletView* wallet_view = currentWalletView();
287 return wallet_view ? wallet_view->getWalletModel() : nullptr;
288 }
289