Skip to content

Commit e02f382

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 a49ccaf commit e02f382

File tree

9 files changed

+204
-14
lines changed

9 files changed

+204
-14
lines changed

src/Makefile.qt.include

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ QT_MOC_CPP = \
4747
qml/models/moc_peerlistsortproxy.cpp \
4848
qml/models/moc_transaction.cpp \
4949
qml/models/moc_sendrecipient.cpp \
50+
qml/models/moc_snapshotqml.cpp \
5051
qml/models/moc_walletlistmodel.cpp \
5152
qml/models/moc_walletqmlmodel.cpp \
5253
qml/models/moc_walletqmlmodel.cpp \
@@ -138,6 +139,7 @@ BITCOIN_QT_H = \
138139
qml/models/peerlistsortproxy.h \
139140
qml/models/transaction.h \
140141
qml/models/sendrecipient.h \
142+
qml/models/snapshotqml.h \
141143
qml/models/walletlistmodel.h \
142144
qml/models/walletqmlmodel.h \
143145
qml/models/walletqmlmodeltransaction.h \
@@ -338,6 +340,7 @@ BITCOIN_QML_BASE_CPP = \
338340
qml/models/peerlistsortproxy.cpp \
339341
qml/models/transaction.cpp \
340342
qml/models/sendrecipient.cpp \
343+
qml/models/snapshotqml.cpp \
341344
qml/models/walletlistmodel.cpp \
342345
qml/models/walletqmlmodel.cpp \
343346
qml/models/walletqmlmodeltransaction.cpp \

src/qml/components/ConnectionSettings.qml

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ ColumnLayout {
3939
onClicked: root.gotoSnapshot()
4040
}
4141
Separator {
42-
visible: !root.onboarding && !snapshotImportCompleted
42+
visible: !root.onboarding && !snapshotImportCompleted && !root.isIBDCompleted
4343
Layout.fillWidth: true
4444
}
4545
Setting {

src/qml/models/chainmodel.cpp

+30
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,29 @@ 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+
QString fullHash = QString::fromStdString(hash_serialized.ToString());
121+
122+
int midPoint = fullHash.length() / 2;
123+
QString firstHalf = fullHash.left(midPoint);
124+
QString secondHalf = fullHash.mid(midPoint);
125+
126+
snapshot_info["height"] = height;
127+
snapshot_info["hashSerializedFirstHalf"] = firstHalf;
128+
snapshot_info["hashSerializedSecondHalf"] = secondHalf;
129+
snapshot_info["date"] = QDateTime::fromSecsSinceEpoch(date).toString("MMMM d yyyy");
130+
}
131+
132+
return snapshot_info;
133+
}

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

+69
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)
@@ -80,6 +86,14 @@ void NodeModel::setVerificationProgress(double new_progress)
8086
if (new_progress != m_verification_progress) {
8187
setRemainingSyncTime(new_progress);
8288

89+
if (new_progress >= 0.00001) {
90+
setHeadersSynced(true);
91+
}
92+
93+
if (new_progress >= 0.999) {
94+
setIsIBDCompleted(true);
95+
}
96+
8397
m_verification_progress = new_progress;
8498
Q_EMIT verificationProgressChanged();
8599
}
@@ -176,3 +190,58 @@ QString NodeModel::defaultProxyAddress()
176190
{
177191
return QString::fromStdString(m_node.defaultProxyAddress());
178192
}
193+
194+
void NodeModel::ConnectToSnapshotLoadProgressSignal()
195+
{
196+
assert(!m_handler_snapshot_load_progress);
197+
198+
m_handler_snapshot_load_progress = m_node.handleSnapshotLoadProgress(
199+
[this](double progress) {
200+
setSnapshotProgress(progress);
201+
});
202+
}
203+
204+
void NodeModel::snapshotLoadThread(QString path_file) {
205+
m_snapshot_loading = true;
206+
Q_EMIT snapshotLoadingChanged();
207+
208+
path_file = QUrl(path_file).toLocalFile();
209+
210+
QThread* snapshot_thread = QThread::create([this, path_file]() {
211+
SnapshotQml loader(m_node, path_file);
212+
bool result = loader.processPath();
213+
if (!result) {
214+
m_snapshot_loading = false;
215+
Q_EMIT snapshotLoadingChanged();
216+
} else {
217+
m_snapshot_loaded = true;
218+
Q_EMIT snapshotLoaded(result);
219+
Q_EMIT snapshotLoadingChanged();
220+
}
221+
});
222+
223+
connect(snapshot_thread, &QThread::finished, snapshot_thread, &QThread::deleteLater);
224+
225+
snapshot_thread->start();
226+
}
227+
228+
void NodeModel::setSnapshotProgress(double new_progress) {
229+
if (new_progress != m_snapshot_progress) {
230+
m_snapshot_progress = new_progress;
231+
Q_EMIT snapshotProgressChanged();
232+
}
233+
}
234+
235+
void NodeModel::setHeadersSynced(bool new_synced) {
236+
if (new_synced != m_headers_synced) {
237+
m_headers_synced = new_synced;
238+
Q_EMIT headersSyncedChanged();
239+
}
240+
}
241+
242+
void NodeModel::setIsIBDCompleted(bool new_completed) {
243+
if (new_completed != m_is_ibd_completed) {
244+
m_is_ibd_completed = new_completed;
245+
Q_EMIT isIBDCompletedChanged();
246+
}
247+
}

src/qml/models/nodemodel.h

+30-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ class NodeModel : public QObject
3434
Q_PROPERTY(double verificationProgress READ verificationProgress NOTIFY verificationProgressChanged)
3535
Q_PROPERTY(bool pause READ pause WRITE setPause NOTIFY pauseChanged)
3636
Q_PROPERTY(bool faulted READ errorState WRITE setErrorState NOTIFY errorStateChanged)
37+
Q_PROPERTY(double snapshotProgress READ snapshotProgress WRITE setSnapshotProgress NOTIFY snapshotProgressChanged)
38+
Q_PROPERTY(bool snapshotLoading READ snapshotLoading NOTIFY snapshotLoadingChanged)
39+
Q_PROPERTY(bool isSnapshotLoaded READ isSnapshotLoaded NOTIFY snapshotLoaded)
40+
Q_PROPERTY(bool headersSynced READ headersSynced WRITE setHeadersSynced NOTIFY headersSyncedChanged)
41+
Q_PROPERTY(bool isIBDCompleted READ isIBDCompleted WRITE setIsIBDCompleted NOTIFY isIBDCompletedChanged)
3742

3843
public:
3944
explicit NodeModel(interfaces::Node& node);
@@ -52,13 +57,23 @@ class NodeModel : public QObject
5257
void setPause(bool new_pause);
5358
bool errorState() const { return m_faulted; }
5459
void setErrorState(bool new_error);
60+
bool isSnapshotLoaded() const { return m_snapshot_loaded; }
61+
double snapshotProgress() const { return m_snapshot_progress; }
62+
void setSnapshotProgress(double new_progress);
63+
bool snapshotLoading() const { return m_snapshot_loading; }
64+
bool headersSynced() const { return m_headers_synced; }
65+
void setHeadersSynced(bool new_synced);
66+
bool isIBDCompleted() const { return m_is_ibd_completed; }
67+
void setIsIBDCompleted(bool new_completed);
5568

5669
Q_INVOKABLE float getTotalBytesReceived() const { return (float)m_node.getTotalBytesRecv(); }
5770
Q_INVOKABLE float getTotalBytesSent() const { return (float)m_node.getTotalBytesSent(); }
5871

5972
Q_INVOKABLE void startNodeInitializionThread();
6073
Q_INVOKABLE void requestShutdown();
6174

75+
Q_INVOKABLE void snapshotLoadThread(QString path_file);
76+
6277
void startShutdownPolling();
6378
void stopShutdownPolling();
6479

@@ -80,7 +95,13 @@ public Q_SLOTS:
8095

8196
void setTimeRatioList(int new_time);
8297
void setTimeRatioListInitial();
83-
98+
void initializationFinished();
99+
void snapshotLoaded(bool result);
100+
void snapshotProgressChanged();
101+
void snapshotLoadingChanged();
102+
void showProgress(const QString& title, int progress);
103+
void headersSyncedChanged();
104+
void isIBDCompletedChanged();
84105
protected:
85106
void timerEvent(QTimerEvent* event) override;
86107

@@ -93,17 +114,23 @@ public Q_SLOTS:
93114
double m_verification_progress{0.0};
94115
bool m_pause{false};
95116
bool m_faulted{false};
96-
117+
double m_snapshot_progress{0.0};
97118
int m_shutdown_polling_timer_id{0};
119+
int m_snapshot_timer_id{0};
120+
bool m_snapshot_loading{false};
121+
bool m_snapshot_loaded{false};
122+
bool m_headers_synced{false};
123+
bool m_is_ibd_completed{false};
98124

99125
QVector<QPair<int, double>> m_block_process_time;
100126

101127
interfaces::Node& m_node;
102128
std::unique_ptr<interfaces::Handler> m_handler_notify_block_tip;
103129
std::unique_ptr<interfaces::Handler> m_handler_notify_num_peers_changed;
104-
130+
std::unique_ptr<interfaces::Handler> m_handler_snapshot_load_progress;
105131
void ConnectToBlockTipSignal();
106132
void ConnectToNumConnectionsChangedSignal();
133+
void ConnectToSnapshotLoadProgressSignal();
107134
};
108135

109136
#endif // BITCOIN_QML_MODELS_NODEMODEL_H

src/qml/models/snapshotqml.cpp

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

src/qml/models/snapshotqml.h

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) 2024 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+
#ifndef BITCOIN_QML_MODELS_SNAPSHOTQML_H
6+
#define BITCOIN_QML_MODELS_SNAPSHOTQML_H
7+
8+
#include <interfaces/handler.h>
9+
#include <interfaces/node.h>
10+
11+
#include <QObject>
12+
13+
class SnapshotQml : public QObject
14+
{
15+
Q_OBJECT
16+
public:
17+
SnapshotQml(interfaces::Node& node, QString path);
18+
19+
bool processPath();
20+
21+
private:
22+
interfaces::Node& m_node;
23+
QString m_path;
24+
};
25+
26+
#endif // BITCOIN_QML_MODELS_SNAPSHOTQML_H

src/qml/pages/settings/SettingsConnection.qml

-9
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,5 @@ Page {
9999
onBack: stack.pop()
100100
}
101101
}
102-
Component {
103-
id: generateSnapshotSettings
104-
SettingsSnapshotGen {
105-
onboarding: root.onboarding
106-
generateSnapshot: true
107-
isSnapshotGenerated: ( nodeModel.isSnapshotFileExists() || nodeModel.isSnapshotGenerated )
108-
onBack: stack.pop()
109-
}
110-
}
111102
}
112103
}

0 commit comments

Comments
 (0)