Skip to content

Commit 8434d40

Browse files
committed
wip: wiring load snapshot no on boarding
adds wiring to connect QML GUI to loading a signet utxo snapshot via the connections settings. This version currently does NOT work with Onboarding. To test the user has to start the node and after onboarding go to the connection settings then load the snapshot from there. This is a work in progress, do not merge!
1 parent 3ca75a4 commit 8434d40

15 files changed

+314
-26
lines changed

Diff for: src/Makefile.am

+2
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ BITCOIN_CORE_H = \
222222
node/eviction.h \
223223
node/interface_ui.h \
224224
node/kernel_notifications.h \
225+
node/loadsnapshot.cpp \
225226
node/mempool_args.h \
226227
node/mempool_persist_args.h \
227228
node/miner.h \
@@ -947,6 +948,7 @@ libbitcoinkernel_la_SOURCES = \
947948
logging.cpp \
948949
node/blockstorage.cpp \
949950
node/chainstate.cpp \
951+
node/loadsnapshot.cpp \
950952
node/utxo_snapshot.cpp \
951953
policy/feerate.cpp \
952954
policy/fees.cpp \

Diff for: src/interfaces/node.h

+3
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ class Node
199199
//! List rpc commands.
200200
virtual std::vector<std::string> listRpcCommands() = 0;
201201

202+
//! Load UTXO Snapshot.
203+
virtual bool snapshotLoad(const std::string& path_string) = 0;
204+
202205
//! Set RPC timer interface if unset.
203206
virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) = 0;
204207

Diff for: src/kernel/chainparams.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,13 @@ class SigNetParams : public CChainParams {
371371

372372
vFixedSeeds.clear();
373373

374+
m_assumeutxo_data = MapAssumeutxo{
375+
{
376+
160000,
377+
{AssumeutxoHash{uint256S("0x5225141cb62dee63ab3be95f9b03d60801f264010b1816d4bd00618b2736e7be")}, 1278002},
378+
},
379+
};
380+
374381
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
375382
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
376383
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);

Diff for: src/node/interfaces.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ class NodeImpl : public Node
395395
{
396396
m_context = context;
397397
}
398+
bool snapshotLoad(const std::string& path_string) override { return chainman().LoadSnapshot(*m_context, path_string); }
398399
ArgsManager& args() { return *Assert(Assert(m_context)->args); }
399400
ChainstateManager& chainman() { return *Assert(m_context->chainman); }
400401
NodeContext* m_context{nullptr};

Diff for: src/node/loadsnapshot.cpp

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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+
6+
// #include <node/loadsnapshot.h>
7+
8+
#include <logging.h>
9+
#include <node/blockstorage.h>
10+
#include <node/context.h>
11+
#include <node/utxo_snapshot.h>
12+
#include <streams.h>
13+
#include <validation.h>
14+
15+
#include <util/fs.h>
16+
17+
#include <util/any.h>
18+
#include <string>
19+
20+
21+
using node::BlockManager;
22+
using node::NodeContext;
23+
using node::SnapshotMetadata;
24+
25+
struct CUpdatedBlock
26+
{
27+
uint256 hash;
28+
int height;
29+
};
30+
31+
bool ChainstateManager::LoadSnapshot(NodeContext& node, const std::string& path_string)
32+
{
33+
fs::path path(fs::u8path(path_string));
34+
if (!fs::exists(path)) {
35+
LogPrintf("[loadsnapshot] snapshot file %s does not exist\n", path.u8string());
36+
return false;
37+
}
38+
39+
FILE* file{fsbridge::fopen(path, "rb")};
40+
41+
AutoFile afile{file};
42+
if (afile.IsNull()) {
43+
LogPrintf("[loadsnapshot] failed to open snapshot file %s\n", path_string);
44+
return false;
45+
}
46+
47+
// Read the snapshot metadata.
48+
SnapshotMetadata metadata;
49+
afile >> metadata;
50+
51+
// Get the base blockhash and look up the corresponding CBlockIndex object.
52+
uint256 base_blockhash = metadata.m_base_blockhash;
53+
int max_secs_to_wait_for_headers = 60 * 10;
54+
CBlockIndex* snapshot_start_block = nullptr;
55+
56+
LogPrintf("[loadsnapshot] waiting to see blockheader %s in headers chain before snapshot activation\n",
57+
base_blockhash.ToString());
58+
59+
if (node.chainman == nullptr) {
60+
// Handle error
61+
LogPrintf("[loadsnapshot] node.chainman is null\n");
62+
return false;
63+
}
64+
65+
ChainstateManager& chainman = *node.chainman;
66+
67+
// snapshot_start_block = chainman.m_blockman.LookupBlockIndex(base_blockhash);
68+
69+
// if (!snapshot_start_block) {
70+
// LogPrintf("[loadsnapshot] can't find blockheader %s\n",
71+
// base_blockhash.ToString());
72+
// return false;
73+
// }
74+
75+
while (max_secs_to_wait_for_headers > 0) {
76+
LogPrintf("[loadsnapshot] base_blockhash = %s\n", base_blockhash.ToString());
77+
snapshot_start_block = WITH_LOCK(::cs_main,
78+
return chainman.m_blockman.LookupBlockIndex(base_blockhash));
79+
max_secs_to_wait_for_headers -= 1;
80+
81+
if (!snapshot_start_block) {
82+
std::this_thread::sleep_for(std::chrono::seconds(1));
83+
} else {
84+
break;
85+
}
86+
}
87+
88+
// // snapshot_start_block = chainman.m_blockman.LookupBlockIndex(base_blockhash);
89+
90+
if (!snapshot_start_block) {
91+
LogPrintf("[loadsnapshot] timed out waiting for snapshot start blockheader %s\n",
92+
base_blockhash.ToString());
93+
return false;
94+
}
95+
96+
// Activate the snapshot.
97+
if (!chainman.ActivateSnapshot(afile, metadata, false)) {
98+
// std::string error_message = "Unable to load UTXO snapshot " + path.u8string();
99+
// throw std::runtime_error(error_message);
100+
LogPrintf("[loadsnapshot] Unable to load UTXO snapshot %s\n", path.u8string());
101+
return false;
102+
}
103+
104+
// Get the new tip and print a log message.
105+
CBlockIndex* new_tip{WITH_LOCK(::cs_main, return chainman.ActiveTip())};
106+
LogPrintf("[loadsnashot] Loaded %d coins from snapshot %s at height %d\n",
107+
metadata.m_coins_count, new_tip->GetBlockHash().ToString(), new_tip->nHeight);
108+
109+
return true;
110+
}

Diff for: src/qml/components/ConnectionSettings.qml

+37-20
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,53 @@ import QtQuick.Layouts 1.15
88
import "../controls"
99

1010
ColumnLayout {
11+
// TODO: Remove this once storing the snapshot path is implemented
12+
property bool isOnboarding: false
1113
property bool snapshotImported: false
1214
function setSnapshotImported(imported) {
1315
snapshotImported = imported
1416
}
1517
spacing: 4
16-
Setting {
17-
id: gotoSnapshot
18+
Item {
19+
// TODO: Remove this once storing the snapshot path is implemented
20+
visible: !isOnboarding
21+
height: visible ? implicitHeight : 0
1822
Layout.fillWidth: true
19-
header: qsTr("Load snapshot")
20-
description: qsTr("Instant use with background sync")
21-
actionItem: Item {
22-
width: 26
23-
height: 26
24-
CaretRightIcon {
25-
anchors.centerIn: parent
26-
visible: !snapshotImported
27-
color: gotoSnapshot.stateColor
23+
Layout.preferredHeight: gotoSnapshot.height
24+
25+
Setting {
26+
id: gotoSnapshot
27+
visible: parent.visible
28+
Layout.fillWidth: true
29+
header: qsTr("Load snapshot")
30+
description: qsTr("Instant use with background sync")
31+
actionItem: Item {
32+
width: 26
33+
height: 26
34+
CaretRightIcon {
35+
// TODO: aligment will be fixed once Onboarding snapshot works
36+
anchors.right: parent.right
37+
anchors.verticalCenter: parent.verticalCenter
38+
visible: !snapshotImported
39+
color: gotoSnapshot.stateColor
40+
}
41+
GreenCheckIcon {
42+
anchors.centerIn: parent
43+
visible: snapshotImported
44+
color: Theme.color.transparent
45+
}
2846
}
29-
GreenCheckIcon {
30-
anchors.centerIn: parent
31-
visible: snapshotImported
32-
color: Theme.color.transparent
47+
onClicked: {
48+
connectionSwipe.incrementCurrentIndex()
49+
connectionSwipe.incrementCurrentIndex()
3350
}
3451
}
35-
onClicked: {
36-
connectionSwipe.incrementCurrentIndex()
37-
connectionSwipe.incrementCurrentIndex()
38-
}
3952
}
40-
Separator { Layout.fillWidth: true }
53+
Separator {
54+
Layout.fillWidth: true
55+
// TODO: Remove this once storing the snapshot path is implemented
56+
visible: !isOnboarding
57+
}
4158
Setting {
4259
Layout.fillWidth: true
4360
header: qsTr("Enable listening")

Diff for: src/qml/components/SnapshotSettings.qml

+22-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import QtQuick 2.15
66
import QtQuick.Controls 2.15
77
import QtQuick.Layouts 1.15
8+
import QtQuick.Dialogs 1.3
9+
810

911
import "../controls"
1012

@@ -18,7 +20,7 @@ ColumnLayout {
1820
width: Math.min(parent.width, 450)
1921
anchors.horizontalCenter: parent.horizontalCenter
2022

21-
23+
// TODO: Remove simulation timer before release
2224
Timer {
2325
id: snapshotSimulationTimer
2426
interval: 50 // Update every 50ms
@@ -78,8 +80,25 @@ ColumnLayout {
7880
Layout.alignment: Qt.AlignCenter
7981
text: qsTr("Choose snapshot file")
8082
onClicked: {
81-
settingsStack.currentIndex = 1
82-
snapshotSimulationTimer.start()
83+
// TODO: Connect this to snapshot loading
84+
// settingsStack.currentIndex = 1
85+
fileDialog.open()
86+
}
87+
}
88+
89+
FileDialog {
90+
id: fileDialog
91+
folder: shortcuts.home
92+
selectMultiple: false
93+
onAccepted: {
94+
console.log("File chosen:", fileDialog.fileUrls)
95+
var snapshotFileName = fileDialog.fileUrl.toString()
96+
console.log("Snapshot file name:", snapshotFileName)
97+
if (snapshotFileName.endsWith(".dat")) {
98+
// optionsModel.setSnapshotDirectory(snapshotFileName)
99+
// console.log("Snapshot directory set:", optionsModel.getSnapshotDirectory())
100+
nodeModel.initializeSnapshot(true, snapshotFileName)
101+
}
83102
}
84103
}
85104
}

Diff for: src/qml/models/nodemodel.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#include <QMetaObject>
1717
#include <QTimerEvent>
1818
#include <QString>
19+
#include <QThread>
20+
#include <QUrl>
21+
#include <QDebug>
1922

2023
NodeModel::NodeModel(interfaces::Node& node)
2124
: m_node{node}
@@ -121,6 +124,8 @@ void NodeModel::initializeResult(bool success, interfaces::BlockAndHeaderTipInfo
121124
setVerificationProgress(tip_info.verification_progress);
122125

123126
Q_EMIT setTimeRatioListInitial();
127+
// TODO: fix this so that it works once storing the snapshot path is implemented
128+
Q_EMIT initializationFinished();
124129
}
125130

126131
void NodeModel::startShutdownPolling()
@@ -166,3 +171,25 @@ void NodeModel::ConnectToNumConnectionsChangedSignal()
166171
setNumOutboundPeers(new_num_peers.outbound_full_relay + new_num_peers.block_relay);
167172
});
168173
}
174+
175+
// Loads a snapshot from a given path using FileDialog
176+
void NodeModel::initializeSnapshot(bool initLoadSnapshot, QString path_file) {
177+
if (initLoadSnapshot) {
178+
// TODO: this is to deal with FileDialog returning a QUrl
179+
path_file = QUrl(path_file).toLocalFile();
180+
// TODO: Remove this before release
181+
// qDebug() << "path_file: " << path_file;
182+
QThread* snapshot_thread = new QThread();
183+
184+
// Capture path_file by value
185+
auto lambda = [this, path_file]() {
186+
bool result = this->snapshotLoad(path_file);
187+
Q_EMIT snapshotLoaded(result);
188+
};
189+
190+
connect(snapshot_thread, &QThread::started, lambda);
191+
connect(snapshot_thread, &QThread::finished, snapshot_thread, &QThread::deleteLater);
192+
193+
snapshot_thread->start();
194+
}
195+
}

Diff for: src/qml/models/nodemodel.h

+5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class NodeModel : public QObject
5959
Q_INVOKABLE void startNodeInitializionThread();
6060
Q_INVOKABLE void requestShutdown();
6161

62+
Q_INVOKABLE void initializeSnapshot(bool initLoadSnapshot, QString path_file);
63+
Q_INVOKABLE bool snapshotLoad(QString path_file) const { return m_node.snapshotLoad(path_file.toStdString()); }
64+
6265
void startShutdownPolling();
6366
void stopShutdownPolling();
6467

@@ -77,6 +80,8 @@ public Q_SLOTS:
7780

7881
void setTimeRatioList(int new_time);
7982
void setTimeRatioListInitial();
83+
void initializationFinished();
84+
void snapshotLoaded(bool result);
8085

8186
protected:
8287
void timerEvent(QTimerEvent* event) override;

0 commit comments

Comments
 (0)