Skip to content

Commit 60f676a

Browse files
committed
poc: Load snapshot through the options dialog
This is a quick and dirty POC to showcase and test the load snapshot functionality. This is by no means meant to be a robust approach...
1 parent 03c76f5 commit 60f676a

12 files changed

+158
-0
lines changed

src/kernel/notifications_interface.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class Notifications
4040
[[nodiscard]] virtual InterruptResult blockTip(SynchronizationState state, CBlockIndex& index, double verification_progress) { return {}; }
4141
virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {}
4242
virtual void progress(const bilingual_str& title, int progress_percent, bool resume_possible) {}
43+
virtual void snapshotLoadProgress(double progress) {}
4344
virtual void warningSet(Warning id, const bilingual_str& message) {}
4445
virtual void warningUnset(Warning id) {}
4546

src/node/interface_ui.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct UISignals {
2323
boost::signals2::signal<CClientUIInterface::NotifyNetworkActiveChangedSig> NotifyNetworkActiveChanged;
2424
boost::signals2::signal<CClientUIInterface::NotifyAlertChangedSig> NotifyAlertChanged;
2525
boost::signals2::signal<CClientUIInterface::ShowProgressSig> ShowProgress;
26+
boost::signals2::signal<CClientUIInterface::SnapshotLoadProgressSig> SnapshotLoadProgress;
2627
boost::signals2::signal<CClientUIInterface::NotifyBlockTipSig> NotifyBlockTip;
2728
boost::signals2::signal<CClientUIInterface::NotifyHeaderTipSig> NotifyHeaderTip;
2829
boost::signals2::signal<CClientUIInterface::BannedListChangedSig> BannedListChanged;
@@ -43,6 +44,7 @@ ADD_SIGNALS_IMPL_WRAPPER(NotifyNumConnectionsChanged);
4344
ADD_SIGNALS_IMPL_WRAPPER(NotifyNetworkActiveChanged);
4445
ADD_SIGNALS_IMPL_WRAPPER(NotifyAlertChanged);
4546
ADD_SIGNALS_IMPL_WRAPPER(ShowProgress);
47+
ADD_SIGNALS_IMPL_WRAPPER(SnapshotLoadProgress);
4648
ADD_SIGNALS_IMPL_WRAPPER(NotifyBlockTip);
4749
ADD_SIGNALS_IMPL_WRAPPER(NotifyHeaderTip);
4850
ADD_SIGNALS_IMPL_WRAPPER(BannedListChanged);
@@ -55,6 +57,7 @@ void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { re
5557
void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); }
5658
void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); }
5759
void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); }
60+
void CClientUIInterface::SnapshotLoadProgress(double progress) { return g_ui_signals.SnapshotLoadProgress(progress); }
5861
void CClientUIInterface::NotifyBlockTip(SynchronizationState s, const CBlockIndex& block, double verification_progress) { return g_ui_signals.NotifyBlockTip(s, block, verification_progress); }
5962
void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, int64_t height, int64_t timestamp, bool presync) { return g_ui_signals.NotifyHeaderTip(s, height, timestamp, presync); }
6063
void CClientUIInterface::BannedListChanged() { return g_ui_signals.BannedListChanged(); }

src/node/interface_ui.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ class CClientUIInterface
102102
*/
103103
ADD_SIGNALS_DECL_WRAPPER(ShowProgress, void, const std::string& title, int nProgress, bool resume_possible);
104104

105+
/** Snapshot load progress. */
106+
ADD_SIGNALS_DECL_WRAPPER(SnapshotLoadProgress, void, double progress);
107+
105108
/** New block has been accepted */
106109
ADD_SIGNALS_DECL_WRAPPER(NotifyBlockTip, void, SynchronizationState, const CBlockIndex& block, double verification_progress);
107110

src/node/interfaces.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,10 @@ class NodeImpl : public Node
391391
{
392392
return MakeSignalHandler(::uiInterface.ShowProgress_connect(fn));
393393
}
394+
std::unique_ptr<Handler> handleSnapshotLoadProgress(SnapshotLoadProgressFn fn) override
395+
{
396+
return MakeSignalHandler(::uiInterface.SnapshotLoadProgress_connect(fn));
397+
}
394398
std::unique_ptr<Handler> handleInitWallet(InitWalletFn fn) override
395399
{
396400
return MakeSignalHandler(::uiInterface.InitWallet_connect(fn));

src/node/kernel_notifications.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ void KernelNotifications::progress(const bilingual_str& title, int progress_perc
7777
uiInterface.ShowProgress(title.translated, progress_percent, resume_possible);
7878
}
7979

80+
void KernelNotifications::snapshotLoadProgress(double progress)
81+
{
82+
uiInterface.SnapshotLoadProgress(progress);
83+
}
84+
8085
void KernelNotifications::warningSet(kernel::Warning id, const bilingual_str& message)
8186
{
8287
if (m_warnings.Set(id, message)) {

src/node/kernel_notifications.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class KernelNotifications : public kernel::Notifications
4141

4242
void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override;
4343

44+
void snapshotLoadProgress(double progress) override;
45+
4446
void warningSet(kernel::Warning id, const bilingual_str& message) override;
4547

4648
void warningUnset(kernel::Warning id) override;

src/qt/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ add_library(bitcoinqt STATIC EXCLUDE_FROM_ALL
111111
qvaluecombobox.h
112112
rpcconsole.cpp
113113
rpcconsole.h
114+
snapshotmodel.cpp
115+
snapshotmodel.h
114116
splashscreen.cpp
115117
splashscreen.h
116118
trafficgraphwidget.cpp

src/qt/optionsdialog.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <qt/guiconstants.h>
1313
#include <qt/guiutil.h>
1414
#include <qt/optionsmodel.h>
15+
#include <qt/snapshotmodel.h>
1516

1617
#include <common/system.h>
1718
#include <interfaces/node.h>
@@ -25,12 +26,16 @@
2526
#include <QApplication>
2627
#include <QDataWidgetMapper>
2728
#include <QDir>
29+
#include <QFileDialog>
2830
#include <QFontDialog>
2931
#include <QIntValidator>
3032
#include <QLocale>
3133
#include <QMessageBox>
3234
#include <QSystemTrayIcon>
3335
#include <QTimer>
36+
#include <QProgressDialog>
37+
38+
#include <interfaces/handler.h>
3439

3540
int setFontChoice(QComboBox* cb, const OptionsModel::FontChoice& fc)
3641
{
@@ -121,6 +126,10 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
121126
connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyPortTor, &QWidget::setEnabled);
122127
connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState);
123128

129+
QPushButton* loadSnapshotButton = new QPushButton(tr("Load Snapshot..."), this);
130+
ui->verticalLayout_Main->insertWidget(ui->verticalLayout_Main->indexOf(ui->enableServer) + 1, loadSnapshotButton);
131+
connect(loadSnapshotButton, &QPushButton::clicked, this, &OptionsDialog::on_loadSnapshotButton_clicked);
132+
124133
/* Window elements init */
125134
#ifdef Q_OS_MACOS
126135
/* remove Window tab on Mac */
@@ -400,6 +409,51 @@ void OptionsDialog::on_showTrayIcon_stateChanged(int state)
400409
}
401410
}
402411

412+
void OptionsDialog::on_loadSnapshotButton_clicked()
413+
{
414+
QString filename = QFileDialog::getOpenFileName(this,
415+
tr("Load Snapshot"),
416+
tr("Bitcoin Snapshot Files (*.dat);;"));
417+
418+
if (filename.isEmpty()) return;
419+
420+
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm snapshot load"),
421+
tr("Are you sure you want to load this snapshot? This will delete your current blockchain data."),
422+
QMessageBox::Yes | QMessageBox::Cancel,
423+
QMessageBox::Cancel);
424+
425+
if (retval != QMessageBox::Yes) return;
426+
427+
QProgressDialog* progress = new QProgressDialog(tr("Loading snapshot..."), tr("Cancel"), 0, 100, this);
428+
progress->setWindowModality(Qt::WindowModal);
429+
progress->setMinimumDuration(0);
430+
progress->setValue(0);
431+
progress->show();
432+
433+
// Store the handler in the member variable to keep it alive
434+
m_snapshot_load_handler = model->node().handleSnapshotLoadProgress(
435+
[progress](double p) {
436+
progress->setValue(static_cast<int>(p * 100));
437+
QApplication::processEvents();
438+
});
439+
440+
SnapshotModel snapshotModel(model->node(), filename);
441+
bool success = snapshotModel.processPath();
442+
443+
// Clean up the progress dialog
444+
progress->close();
445+
progress->deleteLater();
446+
447+
// Clean up the handler
448+
m_snapshot_load_handler.reset();
449+
450+
if (success) {
451+
QMessageBox::information(this, tr("Success"), tr("Snapshot loaded successfully"));
452+
} else {
453+
QMessageBox::critical(this, tr("Error"), tr("Error loading snapshot"));
454+
}
455+
}
456+
403457
void OptionsDialog::togglePruneWarning(bool enabled)
404458
{
405459
ui->pruneWarning->setVisible(!ui->pruneWarning->isVisible());

src/qt/optionsdialog.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ namespace Ui {
2020
class OptionsDialog;
2121
}
2222

23+
namespace interfaces {
24+
class Handler;
25+
}
26+
2327
/** Proxy address widget validator, checks for a valid proxy address.
2428
*/
2529
class ProxyAddressValidator : public QValidator
@@ -58,6 +62,8 @@ private Q_SLOTS:
5862
void on_openBitcoinConfButton_clicked();
5963
void on_okButton_clicked();
6064
void on_cancelButton_clicked();
65+
void on_loadSnapshotButton_clicked();
66+
6167

6268
void on_showTrayIcon_stateChanged(int state);
6369

@@ -77,6 +83,7 @@ private Q_SLOTS:
7783
ClientModel* m_client_model{nullptr};
7884
OptionsModel* model{nullptr};
7985
QDataWidgetMapper* mapper{nullptr};
86+
std::unique_ptr<interfaces::Handler> m_snapshot_load_handler;
8087
};
8188

8289
#endif // BITCOIN_QT_OPTIONSDIALOG_H

src/qt/snapshotmodel.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2024 - 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/snapshotmodel.h>
6+
7+
#include <interfaces/node.h>
8+
#include <node/context.h>
9+
#include <node/utxo_snapshot.h>
10+
#include <clientversion.h>
11+
#include <validation.h>
12+
#include <util/fs.h>
13+
#include <util/fs_helpers.h>
14+
15+
SnapshotModel::SnapshotModel(interfaces::Node& node, QString path)
16+
: m_node(node), m_path(path) {}
17+
18+
bool SnapshotModel::processPath()
19+
{
20+
ChainstateManager& chainman = *m_node.context()->chainman;
21+
const fs::path snapshot_path = fs::u8path(m_path.toStdString());
22+
if (!fs::exists(snapshot_path)) {
23+
return false;
24+
}
25+
26+
FILE* snapshot_file{fsbridge::fopen(snapshot_path, "rb")};
27+
AutoFile coins_file{snapshot_file};
28+
if (coins_file.IsNull()) {
29+
return false;
30+
}
31+
32+
node::SnapshotMetadata metadata{chainman.GetParams().MessageStart()};
33+
try {
34+
coins_file >> metadata;
35+
} catch (const std::exception& e) {
36+
return false;
37+
}
38+
39+
bool result = m_node.loadSnapshot(coins_file, metadata, false);
40+
if (!result) {
41+
return false;
42+
}
43+
44+
return true;
45+
}

0 commit comments

Comments
 (0)