Skip to content

Commit 109cf42

Browse files
committed
qml: Implement UTXO snapshot loading infrastructure
- Add SnapshotQml class to encapsulate snapshot loading operations - Integrate QML properties for UTXO snapshot management - Wire up snapshot loading functionality with node interface
1 parent 184a9b8 commit 109cf42

11 files changed

+229
-57
lines changed

src/Makefile.qt.include

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ QT_MOC_CPP = \
4343
qml/models/moc_options_model.cpp \
4444
qml/models/moc_peerdetailsmodel.cpp \
4545
qml/models/moc_peerlistsortproxy.cpp \
46+
qml/models/moc_snapshotqml.cpp \
4647
qml/models/moc_walletlistmodel.cpp \
4748
qml/models/moc_walletqmlmodel.cpp \
4849
qml/moc_appmode.cpp \
@@ -126,6 +127,7 @@ BITCOIN_QT_H = \
126127
qml/models/options_model.h \
127128
qml/models/peerdetailsmodel.h \
128129
qml/models/peerlistsortproxy.h \
130+
qml/models/snapshotqml.h \
129131
qml/models/walletlistmodel.h \
130132
qml/models/walletqmlmodel.h \
131133
qml/appmode.h \
@@ -318,6 +320,7 @@ BITCOIN_QML_BASE_CPP = \
318320
qml/models/options_model.cpp \
319321
qml/models/peerdetailsmodel.cpp \
320322
qml/models/peerlistsortproxy.cpp \
323+
qml/models/snapshotqml.cpp \
321324
qml/models/walletlistmodel.cpp \
322325
qml/models/walletqmlmodel.cpp \
323326
qml/imageprovider.cpp \

src/qml/components/ConnectionSettings.qml

+10-7
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ ColumnLayout {
1111
id: root
1212
signal next
1313
signal gotoSnapshot
14-
property bool snapshotImported: false
15-
function setSnapshotImported(imported) {
16-
snapshotImported = imported
17-
}
14+
property bool snapshotImportCompleted: chainModel.isSnapshotActive
15+
property bool onboarding: false
16+
1817
spacing: 4
1918
Setting {
2019
id: gotoSnapshot
20+
visible: !root.onboarding
2121
Layout.fillWidth: true
2222
header: qsTr("Load snapshot")
2323
description: qsTr("Instant use with background sync")
@@ -26,19 +26,22 @@ ColumnLayout {
2626
height: 26
2727
CaretRightIcon {
2828
anchors.centerIn: parent
29-
visible: !snapshotImported
29+
visible: !snapshotImportCompleted
3030
color: gotoSnapshot.stateColor
3131
}
3232
GreenCheckIcon {
3333
anchors.centerIn: parent
34-
visible: snapshotImported
34+
visible: snapshotImportCompleted
3535
color: Theme.color.transparent
3636
size: 30
3737
}
3838
}
3939
onClicked: root.gotoSnapshot()
4040
}
41-
Separator { Layout.fillWidth: true }
41+
Separator {
42+
visible: !root.onboarding
43+
Layout.fillWidth: true
44+
}
4245
Setting {
4346
Layout.fillWidth: true
4447
header: qsTr("Enable listening")

src/qml/components/SnapshotSettings.qml

+40-39
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,28 @@
55
import QtQuick 2.15
66
import QtQuick.Controls 2.15
77
import QtQuick.Layouts 1.15
8+
import QtQuick.Dialogs 1.3
89

910
import "../controls"
1011

1112
ColumnLayout {
12-
signal snapshotImportCompleted()
13-
property int snapshotVerificationCycles: 0
14-
property real snapshotVerificationProgress: 0
15-
property bool snapshotVerified: false
16-
1713
id: columnLayout
14+
signal back
15+
property bool snapshotLoading: nodeModel.snapshotLoading
16+
property bool snapshotLoaded: nodeModel.isSnapshotLoaded
17+
property bool snapshotImportCompleted: chainModel.isSnapshotActive
18+
property bool onboarding: false
19+
property bool snapshotVerified: onboarding ? false : chainModel.isSnapshotActive
20+
property string snapshotFileName: ""
21+
property var snapshotInfo: (snapshotVerified || snapshotLoaded) ? chainModel.getSnapshotInfo() : ({})
22+
property string selectedFile: ""
23+
1824
width: Math.min(parent.width, 450)
1925
anchors.horizontalCenter: parent.horizontalCenter
2026

21-
22-
Timer {
23-
id: snapshotSimulationTimer
24-
interval: 50 // Update every 50ms
25-
running: false
26-
repeat: true
27-
onTriggered: {
28-
if (snapshotVerificationProgress < 1) {
29-
snapshotVerificationProgress += 0.01
30-
} else {
31-
snapshotVerificationCycles++
32-
if (snapshotVerificationCycles < 1) {
33-
snapshotVerificationProgress = 0
34-
} else {
35-
running = false
36-
snapshotVerified = true
37-
settingsStack.currentIndex = 2
38-
}
39-
}
40-
}
41-
}
42-
4327
StackLayout {
4428
id: settingsStack
45-
currentIndex: 0
29+
currentIndex: onboarding ? 0 : snapshotLoaded ? 2 : snapshotVerified ? 2 : snapshotLoading ? 1 : 0
4630

4731
ColumnLayout {
4832
Layout.alignment: Qt.AlignHCenter
@@ -77,11 +61,22 @@ ColumnLayout {
7761
Layout.bottomMargin: 20
7862
Layout.alignment: Qt.AlignCenter
7963
text: qsTr("Choose snapshot file")
80-
onClicked: {
81-
settingsStack.currentIndex = 1
82-
snapshotSimulationTimer.start()
64+
onClicked: fileDialog.open()
65+
}
66+
67+
FileDialog {
68+
id: fileDialog
69+
folder: shortcuts.home
70+
selectMultiple: false
71+
selectExisting: true
72+
nameFilters: ["Snapshot files (*.dat)", "All files (*)"]
73+
onAccepted: {
74+
selectedFile = fileUrl.toString()
75+
snapshotFileName = selectedFile
76+
nodeModel.snapshotLoadThread(snapshotFileName)
8377
}
8478
}
79+
// TODO: Handle file error signal
8580
}
8681

8782
ColumnLayout {
@@ -102,14 +97,15 @@ ColumnLayout {
10297
Layout.leftMargin: 20
10398
Layout.rightMargin: 20
10499
header: qsTr("Loading Snapshot")
100+
description: qsTr("This might take a while...")
105101
}
106102

107103
ProgressIndicator {
108104
id: progressIndicator
109105
Layout.topMargin: 20
110106
width: 200
111107
height: 20
112-
progress: snapshotVerificationProgress
108+
progress: nodeModel.snapshotProgress
113109
Layout.alignment: Qt.AlignCenter
114110
progressColor: Theme.color.blue
115111
}
@@ -137,8 +133,11 @@ ColumnLayout {
137133
descriptionColor: Theme.color.neutral6
138134
descriptionSize: 17
139135
descriptionLineHeight: 1.1
140-
description: qsTr("It contains transactions up to January 12, 2024. Newer transactions still need to be downloaded." +
141-
" The data will be verified in the background.")
136+
description: snapshotInfo && snapshotInfo["date"] ?
137+
qsTr("It contains transactions up to %1. Newer transactions still need to be downloaded." +
138+
" The data will be verified in the background.").arg(snapshotInfo["date"]) :
139+
qsTr("It contains transactions up to DEBUG. Newer transactions still need to be downloaded." +
140+
" The data will be verified in the background.")
142141
}
143142

144143
ContinueButton {
@@ -147,9 +146,8 @@ ColumnLayout {
147146
Layout.alignment: Qt.AlignCenter
148147
text: qsTr("Done")
149148
onClicked: {
150-
snapshotImportCompleted()
151-
connectionSwipe.decrementCurrentIndex()
152-
connectionSwipe.decrementCurrentIndex()
149+
chainModel.isSnapshotActiveChanged()
150+
back()
153151
}
154152
}
155153

@@ -188,14 +186,17 @@ ColumnLayout {
188186
font.pixelSize: 14
189187
}
190188
CoreText {
191-
text: qsTr("200,000")
189+
text: snapshotInfo && snapshotInfo["height"] ?
190+
snapshotInfo["height"] : qsTr("DEBUG")
192191
Layout.alignment: Qt.AlignRight
193192
font.pixelSize: 14
194193
}
195194
}
196195
Separator { Layout.fillWidth: true }
197196
CoreText {
198-
text: qsTr("Hash: 0x1234567890abcdef...")
197+
text: snapshotInfo && snapshotInfo["hashSerialized"] ?
198+
qsTr("Hash: %1").arg(snapshotInfo["hashSerialized"].substring(0, 13) + "...") :
199+
qsTr("Hash: DEBUG")
199200
font.pixelSize: 14
200201
}
201202
}

src/qml/models/chainmodel.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
#include <QThread>
1010
#include <QTime>
1111
#include <interfaces/chain.h>
12+
#include <node/utxo_snapshot.h>
13+
#include <kernel/chainparams.h>
14+
#include <validation.h>
1215

1316
ChainModel::ChainModel(interfaces::Chain& chain)
1417
: m_chain{chain}
18+
// m_params{Params()}
1519
{
1620
QTimer* timer = new QTimer();
1721
connect(timer, &QTimer::timeout, this, &ChainModel::setCurrentTimeRatio);
@@ -101,3 +105,22 @@ void ChainModel::setCurrentTimeRatio()
101105

102106
Q_EMIT timeRatioListChanged();
103107
}
108+
109+
// TODO: Change this once a better solution has been found.
110+
// Using hardcoded snapshot info to display in SnapshotSettings.qml
111+
QVariantMap ChainModel::getSnapshotInfo() {
112+
QVariantMap snapshot_info;
113+
114+
const MapAssumeutxo& valid_assumeutxos_map = Params().Assumeutxo();
115+
if (!valid_assumeutxos_map.empty()) {
116+
const int height = valid_assumeutxos_map.rbegin()->first;
117+
const auto& hash_serialized = valid_assumeutxos_map.rbegin()->second.hash_serialized;
118+
int64_t date = m_chain.getBlockTime(height);
119+
120+
snapshot_info["height"] = height;
121+
snapshot_info["hashSerialized"] = QString::fromStdString(hash_serialized.ToString());
122+
snapshot_info["date"] = QDateTime::fromSecsSinceEpoch(date).toString("MMMM d yyyy");
123+
}
124+
125+
return snapshot_info;
126+
}

src/qml/models/chainmodel.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class ChainModel : public QObject
2727
Q_PROPERTY(quint64 assumedBlockchainSize READ assumedBlockchainSize CONSTANT)
2828
Q_PROPERTY(quint64 assumedChainstateSize READ assumedChainstateSize CONSTANT)
2929
Q_PROPERTY(QVariantList timeRatioList READ timeRatioList NOTIFY timeRatioListChanged)
30+
Q_PROPERTY(bool isSnapshotActive READ isSnapshotActive NOTIFY isSnapshotActiveChanged)
3031

3132
public:
3233
explicit ChainModel(interfaces::Chain& chain);
@@ -36,18 +37,21 @@ class ChainModel : public QObject
3637
quint64 assumedBlockchainSize() const { return m_assumed_blockchain_size; };
3738
quint64 assumedChainstateSize() const { return m_assumed_chainstate_size; };
3839
QVariantList timeRatioList() const { return m_time_ratio_list; };
39-
40+
bool isSnapshotActive() const { return m_chain.hasAssumedValidChain(); };
4041
int timestampAtMeridian();
4142

4243
void setCurrentTimeRatio();
4344

45+
Q_INVOKABLE QVariantMap getSnapshotInfo();
46+
4447
public Q_SLOTS:
4548
void setTimeRatioList(int new_time);
4649
void setTimeRatioListInitial();
4750

4851
Q_SIGNALS:
4952
void timeRatioListChanged();
5053
void currentNetworkNameChanged();
54+
void isSnapshotActiveChanged();
5155

5256
private:
5357
QString m_current_network_name;

src/qml/models/nodemodel.cpp

+47
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <qml/models/nodemodel.h>
6+
#include <qml/models/snapshotqml.h>
67

78
#include <interfaces/node.h>
89
#include <net.h>
@@ -14,14 +15,19 @@
1415

1516
#include <QDateTime>
1617
#include <QMetaObject>
18+
#include <QObject>
1719
#include <QTimerEvent>
1820
#include <QString>
21+
#include <QUrl>
22+
#include <QThread>
23+
#include <QDebug>
1924

2025
NodeModel::NodeModel(interfaces::Node& node)
2126
: m_node{node}
2227
{
2328
ConnectToBlockTipSignal();
2429
ConnectToNumConnectionsChangedSignal();
30+
ConnectToSnapshotLoadProgressSignal();
2531
}
2632

2733
void NodeModel::setBlockTipHeight(int new_height)
@@ -176,3 +182,44 @@ QString NodeModel::defaultProxyAddress()
176182
{
177183
return QString::fromStdString(m_node.defaultProxyAddress());
178184
}
185+
186+
void NodeModel::ConnectToSnapshotLoadProgressSignal()
187+
{
188+
assert(!m_handler_snapshot_load_progress);
189+
190+
m_handler_snapshot_load_progress = m_node.handleSnapshotLoadProgress(
191+
[this](double progress) {
192+
setSnapshotProgress(progress);
193+
});
194+
}
195+
196+
void NodeModel::snapshotLoadThread(QString path_file) {
197+
m_snapshot_loading = true;
198+
Q_EMIT snapshotLoadingChanged();
199+
200+
path_file = QUrl(path_file).toLocalFile();
201+
202+
QThread* snapshot_thread = QThread::create([this, path_file]() {
203+
SnapshotQml loader(m_node, path_file);
204+
bool result = loader.processPath();
205+
if (!result) {
206+
m_snapshot_loading = false;
207+
Q_EMIT snapshotLoadingChanged();
208+
} else {
209+
m_snapshot_loaded = true;
210+
Q_EMIT snapshotLoaded(result);
211+
Q_EMIT snapshotLoadingChanged();
212+
}
213+
});
214+
215+
connect(snapshot_thread, &QThread::finished, snapshot_thread, &QThread::deleteLater);
216+
217+
snapshot_thread->start();
218+
}
219+
220+
void NodeModel::setSnapshotProgress(double new_progress) {
221+
if (new_progress != m_snapshot_progress) {
222+
m_snapshot_progress = new_progress;
223+
Q_EMIT snapshotProgressChanged();
224+
}
225+
}

0 commit comments

Comments
 (0)