Skip to content

[DRAFT] Expose AssumeUTXO Load Snapshot Functionality To The GUI #870

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

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/interfaces/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <logging.h> // For BCLog::CategoryMask
#include <net.h> // For NodeId
#include <net_types.h> // For banmap_t
#include <node/utxo_snapshot.h> // For SnapshotMetadata
#include <netaddress.h> // For Network
#include <netbase.h> // For ConnectionDirection
#include <support/allocators/secure.h> // For SecureString
Expand Down Expand Up @@ -205,6 +206,9 @@ class Node
//! List rpc commands.
virtual std::vector<std::string> listRpcCommands() = 0;

//! Load and activate a snapshot file
virtual bool loadSnapshot(AutoFile& coins_file, const node::SnapshotMetadata& metadata, bool in_memory) = 0;

//! Set RPC timer interface if unset.
virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) = 0;

Expand Down Expand Up @@ -240,6 +244,10 @@ class Node
using ShowProgressFn = std::function<void(const std::string& title, int progress, bool resume_possible)>;
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;

//! Register handler for snapshot load progress.
using SnapshotLoadProgressFn = std::function<void(double progress)>;
virtual std::unique_ptr<Handler> handleSnapshotLoadProgress(SnapshotLoadProgressFn fn) = 0;

//! Register handler for wallet loader constructed messages.
using InitWalletFn = std::function<void()>;
virtual std::unique_ptr<Handler> handleInitWallet(InitWalletFn fn) = 0;
Expand Down
1 change: 1 addition & 0 deletions src/kernel/notifications_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Notifications
[[nodiscard]] virtual InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) { return {}; }
virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {}
virtual void progress(const bilingual_str& title, int progress_percent, bool resume_possible) {}
virtual void snapshotLoadProgress(double progress) {}
virtual void warningSet(Warning id, const bilingual_str& message) {}
virtual void warningUnset(Warning id) {}

Expand Down
3 changes: 3 additions & 0 deletions src/node/interface_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct UISignals {
boost::signals2::signal<CClientUIInterface::NotifyNetworkActiveChangedSig> NotifyNetworkActiveChanged;
boost::signals2::signal<CClientUIInterface::NotifyAlertChangedSig> NotifyAlertChanged;
boost::signals2::signal<CClientUIInterface::ShowProgressSig> ShowProgress;
boost::signals2::signal<CClientUIInterface::SnapshotLoadProgressSig> SnapshotLoadProgress;
boost::signals2::signal<CClientUIInterface::NotifyBlockTipSig> NotifyBlockTip;
boost::signals2::signal<CClientUIInterface::NotifyHeaderTipSig> NotifyHeaderTip;
boost::signals2::signal<CClientUIInterface::BannedListChangedSig> BannedListChanged;
Expand All @@ -43,6 +44,7 @@ ADD_SIGNALS_IMPL_WRAPPER(NotifyNumConnectionsChanged);
ADD_SIGNALS_IMPL_WRAPPER(NotifyNetworkActiveChanged);
ADD_SIGNALS_IMPL_WRAPPER(NotifyAlertChanged);
ADD_SIGNALS_IMPL_WRAPPER(ShowProgress);
ADD_SIGNALS_IMPL_WRAPPER(SnapshotLoadProgress);
ADD_SIGNALS_IMPL_WRAPPER(NotifyBlockTip);
ADD_SIGNALS_IMPL_WRAPPER(NotifyHeaderTip);
ADD_SIGNALS_IMPL_WRAPPER(BannedListChanged);
Expand All @@ -55,6 +57,7 @@ void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { re
void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); }
void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); }
void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); }
void CClientUIInterface::SnapshotLoadProgress(double progress) { return g_ui_signals.SnapshotLoadProgress(progress); }
void CClientUIInterface::NotifyBlockTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(s, i); }
void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, int64_t height, int64_t timestamp, bool presync) { return g_ui_signals.NotifyHeaderTip(s, height, timestamp, presync); }
void CClientUIInterface::BannedListChanged() { return g_ui_signals.BannedListChanged(); }
Expand Down
3 changes: 3 additions & 0 deletions src/node/interface_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ class CClientUIInterface
*/
ADD_SIGNALS_DECL_WRAPPER(ShowProgress, void, const std::string& title, int nProgress, bool resume_possible);

/** Snapshot load progress. */
ADD_SIGNALS_DECL_WRAPPER(SnapshotLoadProgress, void, double progress);

/** New block has been accepted */
ADD_SIGNALS_DECL_WRAPPER(NotifyBlockTip, void, SynchronizationState, const CBlockIndex*);

Expand Down
9 changes: 9 additions & 0 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,11 @@ class NodeImpl : public Node
return ::tableRPC.execute(req);
}
std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); }
bool loadSnapshot(AutoFile& coins_file, const SnapshotMetadata& metadata, bool in_memory) override
{
auto activation_result{chainman().ActivateSnapshot(coins_file, metadata, in_memory)};
return activation_result.has_value();
}
void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); }
void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); }
std::optional<Coin> getUnspentOutput(const COutPoint& output) override
Expand Down Expand Up @@ -386,6 +391,10 @@ class NodeImpl : public Node
{
return MakeSignalHandler(::uiInterface.ShowProgress_connect(fn));
}
std::unique_ptr<Handler> handleSnapshotLoadProgress(SnapshotLoadProgressFn fn) override
{
return MakeSignalHandler(::uiInterface.SnapshotLoadProgress_connect(fn));
}
std::unique_ptr<Handler> handleInitWallet(InitWalletFn fn) override
{
return MakeSignalHandler(::uiInterface.InitWallet_connect(fn));
Expand Down
5 changes: 5 additions & 0 deletions src/node/kernel_notifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ void KernelNotifications::progress(const bilingual_str& title, int progress_perc
uiInterface.ShowProgress(title.translated, progress_percent, resume_possible);
}

void KernelNotifications::snapshotLoadProgress(double progress)
{
uiInterface.SnapshotLoadProgress(progress);
}

void KernelNotifications::warningSet(kernel::Warning id, const bilingual_str& message)
{
if (m_warnings.Set(id, message)) {
Expand Down
2 changes: 2 additions & 0 deletions src/node/kernel_notifications.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class KernelNotifications : public kernel::Notifications

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

void snapshotLoadProgress(double progress) override;

void warningSet(kernel::Warning id, const bilingual_str& message) override;

void warningUnset(kernel::Warning id) override;
Expand Down
2 changes: 2 additions & 0 deletions src/qt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ add_library(bitcoinqt STATIC EXCLUDE_FROM_ALL
qvaluecombobox.h
rpcconsole.cpp
rpcconsole.h
snapshotmodel.cpp
snapshotmodel.h
splashscreen.cpp
splashscreen.h
trafficgraphwidget.cpp
Expand Down
54 changes: 54 additions & 0 deletions src/qt/optionsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/snapshotmodel.h>

#include <common/system.h>
#include <interfaces/node.h>
Expand All @@ -25,12 +26,16 @@
#include <QApplication>
#include <QDataWidgetMapper>
#include <QDir>
#include <QFileDialog>
#include <QFontDialog>
#include <QIntValidator>
#include <QLocale>
#include <QMessageBox>
#include <QSystemTrayIcon>
#include <QTimer>
#include <QProgressDialog>

#include <interfaces/handler.h>

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

QPushButton* loadSnapshotButton = new QPushButton(tr("Load Snapshot..."), this);
ui->verticalLayout_Main->insertWidget(ui->verticalLayout_Main->indexOf(ui->enableServer) + 1, loadSnapshotButton);
connect(loadSnapshotButton, &QPushButton::clicked, this, &OptionsDialog::on_loadSnapshotButton_clicked);

/* Window elements init */
#ifdef Q_OS_MACOS
/* remove Window tab on Mac */
Expand Down Expand Up @@ -400,6 +409,51 @@ void OptionsDialog::on_showTrayIcon_stateChanged(int state)
}
}

void OptionsDialog::on_loadSnapshotButton_clicked()
{
QString filename = QFileDialog::getOpenFileName(this,
tr("Load Snapshot"),
tr("Bitcoin Snapshot Files (*.dat);;"));

if (filename.isEmpty()) return;

QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm snapshot load"),
tr("Are you sure you want to load this snapshot? This will delete your current blockchain data."),
QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Cancel);

if (retval != QMessageBox::Yes) return;

QProgressDialog* progress = new QProgressDialog(tr("Loading snapshot..."), tr("Cancel"), 0, 100, this);
progress->setWindowModality(Qt::WindowModal);
progress->setMinimumDuration(0);
progress->setValue(0);
progress->show();

// Store the handler in the member variable to keep it alive
m_snapshot_load_handler = model->node().handleSnapshotLoadProgress(
[progress](double p) {
progress->setValue(static_cast<int>(p * 100));
QApplication::processEvents();
});

SnapshotModel snapshotModel(model->node(), filename);
bool success = snapshotModel.processPath();

// Clean up the progress dialog
progress->close();
progress->deleteLater();

// Clean up the handler
m_snapshot_load_handler.reset();

if (success) {
QMessageBox::information(this, tr("Success"), tr("Snapshot loaded successfully"));
} else {
QMessageBox::critical(this, tr("Error"), tr("Error loading snapshot"));
}
}

void OptionsDialog::togglePruneWarning(bool enabled)
{
ui->pruneWarning->setVisible(!ui->pruneWarning->isVisible());
Expand Down
7 changes: 7 additions & 0 deletions src/qt/optionsdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ namespace Ui {
class OptionsDialog;
}

namespace interfaces {
class Handler;
}

/** Proxy address widget validator, checks for a valid proxy address.
*/
class ProxyAddressValidator : public QValidator
Expand Down Expand Up @@ -58,6 +62,8 @@ private Q_SLOTS:
void on_openBitcoinConfButton_clicked();
void on_okButton_clicked();
void on_cancelButton_clicked();
void on_loadSnapshotButton_clicked();


void on_showTrayIcon_stateChanged(int state);

Expand All @@ -77,6 +83,7 @@ private Q_SLOTS:
ClientModel* m_client_model{nullptr};
OptionsModel* model{nullptr};
QDataWidgetMapper* mapper{nullptr};
std::unique_ptr<interfaces::Handler> m_snapshot_load_handler;
};

#endif // BITCOIN_QT_OPTIONSDIALOG_H
45 changes: 45 additions & 0 deletions src/qt/snapshotmodel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2024 - present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qt/snapshotmodel.h>

#include <interfaces/node.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
#include <clientversion.h>
#include <validation.h>
#include <util/fs.h>
#include <util/fs_helpers.h>

SnapshotModel::SnapshotModel(interfaces::Node& node, QString path)
: m_node(node), m_path(path) {}

bool SnapshotModel::processPath()
{
ChainstateManager& chainman = *m_node.context()->chainman;
const fs::path snapshot_path = fs::u8path(m_path.toStdString());
if (!fs::exists(snapshot_path)) {
return false;
}

FILE* snapshot_file{fsbridge::fopen(snapshot_path, "rb")};
AutoFile coins_file{snapshot_file};
if (coins_file.IsNull()) {
return false;
}

node::SnapshotMetadata metadata{chainman.GetParams().MessageStart()};
try {
coins_file >> metadata;
} catch (const std::exception& e) {
return false;
}

bool result = m_node.loadSnapshot(coins_file, metadata, false);
if (!result) {
return false;
}

return true;
}
28 changes: 28 additions & 0 deletions src/qt/snapshotmodel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) 2024 - present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_QT_SNAPSHOTMODEL_H
#define BITCOIN_QT_SNAPSHOTMODEL_H

#include <QObject>
#include <interfaces/node.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>

/** Model for snapshot operations. */
class SnapshotModel : public QObject
{
Q_OBJECT

public:
SnapshotModel(interfaces::Node& node, QString path);

bool processPath();

private:
interfaces::Node& m_node;
QString m_path;
};

#endif // BITCOIN_QT_SNAPSHOTMODEL_H
4 changes: 4 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5959,6 +5959,10 @@ util::Result<void> ChainstateManager::PopulateAndValidateSnapshot(
--coins_left;
++coins_processed;

// Show Snapshot Loading progress
double progress = static_cast<double>(coins_processed) / static_cast<double>(coins_count);
GetNotifications().snapshotLoadProgress(progress);

if (coins_processed % 1000000 == 0) {
LogPrintf("[snapshot] %d coins loaded (%.2f%%, %.2f MB)\n",
coins_processed,
Expand Down