Skip to content

Commit 6c25cd7

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 bb8bec2 commit 6c25cd7

File tree

8 files changed

+188
-14
lines changed

8 files changed

+188
-14
lines changed

src/Makefile.qt.include

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ QT_MOC_CPP = \
4646
qml/models/moc_peerlistsortproxy.cpp \
4747
qml/models/moc_transaction.cpp \
4848
qml/models/moc_sendrecipient.cpp \
49+
qml/models/moc_snapshotqml.cpp \
4950
qml/models/moc_walletlistmodel.cpp \
5051
qml/models/moc_walletqmlmodel.cpp \
5152
qml/models/moc_walletqmlmodel.cpp \
@@ -136,6 +137,7 @@ BITCOIN_QT_H = \
136137
qml/models/peerlistsortproxy.h \
137138
qml/models/transaction.h \
138139
qml/models/sendrecipient.h \
140+
qml/models/snapshotqml.h \
139141
qml/models/walletlistmodel.h \
140142
qml/models/walletqmlmodel.h \
141143
qml/models/walletqmlmodeltransaction.h \
@@ -335,6 +337,7 @@ BITCOIN_QML_BASE_CPP = \
335337
qml/models/peerlistsortproxy.cpp \
336338
qml/models/transaction.cpp \
337339
qml/models/sendrecipient.cpp \
340+
qml/models/snapshotqml.cpp \
338341
qml/models/walletlistmodel.cpp \
339342
qml/models/walletqmlmodel.cpp \
340343
qml/models/walletqmlmodeltransaction.cpp \

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

+58
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,10 @@ 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+
8393
m_verification_progress = new_progress;
8494
Q_EMIT verificationProgressChanged();
8595
}
@@ -176,3 +186,51 @@ QString NodeModel::defaultProxyAddress()
176186
{
177187
return QString::fromStdString(m_node.defaultProxyAddress());
178188
}
189+
190+
void NodeModel::ConnectToSnapshotLoadProgressSignal()
191+
{
192+
assert(!m_handler_snapshot_load_progress);
193+
194+
m_handler_snapshot_load_progress = m_node.handleSnapshotLoadProgress(
195+
[this](double progress) {
196+
setSnapshotProgress(progress);
197+
});
198+
}
199+
200+
void NodeModel::snapshotLoadThread(QString path_file) {
201+
m_snapshot_loading = true;
202+
Q_EMIT snapshotLoadingChanged();
203+
204+
path_file = QUrl(path_file).toLocalFile();
205+
206+
QThread* snapshot_thread = QThread::create([this, path_file]() {
207+
SnapshotQml loader(m_node, path_file);
208+
bool result = loader.processPath();
209+
if (!result) {
210+
m_snapshot_loading = false;
211+
Q_EMIT snapshotLoadingChanged();
212+
} else {
213+
m_snapshot_loaded = true;
214+
Q_EMIT snapshotLoaded(result);
215+
Q_EMIT snapshotLoadingChanged();
216+
}
217+
});
218+
219+
connect(snapshot_thread, &QThread::finished, snapshot_thread, &QThread::deleteLater);
220+
221+
snapshot_thread->start();
222+
}
223+
224+
void NodeModel::setSnapshotProgress(double new_progress) {
225+
if (new_progress != m_snapshot_progress) {
226+
m_snapshot_progress = new_progress;
227+
Q_EMIT snapshotProgressChanged();
228+
}
229+
}
230+
231+
void NodeModel::setHeadersSynced(bool new_synced) {
232+
if (new_synced != m_headers_synced) {
233+
m_headers_synced = new_synced;
234+
Q_EMIT headersSyncedChanged();
235+
}
236+
}

src/qml/models/nodemodel.h

+26-4
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+
3742

3843
public:
3944
explicit NodeModel(interfaces::Node& node);
@@ -52,13 +57,21 @@ 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);
5566

5667
Q_INVOKABLE float getTotalBytesReceived() const { return (float)m_node.getTotalBytesRecv(); }
5768
Q_INVOKABLE float getTotalBytesSent() const { return (float)m_node.getTotalBytesSent(); }
5869

5970
Q_INVOKABLE void startNodeInitializionThread();
6071
Q_INVOKABLE void requestShutdown();
6172

73+
Q_INVOKABLE void snapshotLoadThread(QString path_file);
74+
6275
void startShutdownPolling();
6376
void stopShutdownPolling();
6477

@@ -80,7 +93,12 @@ public Q_SLOTS:
8093

8194
void setTimeRatioList(int new_time);
8295
void setTimeRatioListInitial();
83-
96+
void initializationFinished();
97+
void snapshotLoaded(bool result);
98+
void snapshotProgressChanged();
99+
void snapshotLoadingChanged();
100+
void showProgress(const QString& title, int progress);
101+
void headersSyncedChanged();
84102
protected:
85103
void timerEvent(QTimerEvent* event) override;
86104

@@ -93,17 +111,21 @@ public Q_SLOTS:
93111
double m_verification_progress{0.0};
94112
bool m_pause{false};
95113
bool m_faulted{false};
96-
114+
double m_snapshot_progress{0.0};
97115
int m_shutdown_polling_timer_id{0};
98-
116+
int m_snapshot_timer_id{0};
117+
bool m_snapshot_loading{false};
118+
bool m_snapshot_loaded{false};
119+
bool m_headers_synced{false};
99120
QVector<QPair<int, double>> m_block_process_time;
100121

101122
interfaces::Node& m_node;
102123
std::unique_ptr<interfaces::Handler> m_handler_notify_block_tip;
103124
std::unique_ptr<interfaces::Handler> m_handler_notify_num_peers_changed;
104-
125+
std::unique_ptr<interfaces::Handler> m_handler_snapshot_load_progress;
105126
void ConnectToBlockTipSignal();
106127
void ConnectToNumConnectionsChangedSignal();
128+
void ConnectToSnapshotLoadProgressSignal();
107129
};
108130

109131
#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)