Skip to content

Commit

Permalink
wip: wiring load snapshot no on boarding
Browse files Browse the repository at this point in the history
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!
  • Loading branch information
D33r-Gee committed Oct 11, 2024
1 parent 3ca75a4 commit 8434d40
Show file tree
Hide file tree
Showing 15 changed files with 314 additions and 26 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ BITCOIN_CORE_H = \
node/eviction.h \
node/interface_ui.h \
node/kernel_notifications.h \
node/loadsnapshot.cpp \
node/mempool_args.h \
node/mempool_persist_args.h \
node/miner.h \
Expand Down Expand Up @@ -947,6 +948,7 @@ libbitcoinkernel_la_SOURCES = \
logging.cpp \
node/blockstorage.cpp \
node/chainstate.cpp \
node/loadsnapshot.cpp \
node/utxo_snapshot.cpp \
policy/feerate.cpp \
policy/fees.cpp \
Expand Down
3 changes: 3 additions & 0 deletions src/interfaces/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ class Node
//! List rpc commands.
virtual std::vector<std::string> listRpcCommands() = 0;

//! Load UTXO Snapshot.
virtual bool snapshotLoad(const std::string& path_string) = 0;

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

Expand Down
7 changes: 7 additions & 0 deletions src/kernel/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@ class SigNetParams : public CChainParams {

vFixedSeeds.clear();

m_assumeutxo_data = MapAssumeutxo{
{
160000,
{AssumeutxoHash{uint256S("0x5225141cb62dee63ab3be95f9b03d60801f264010b1816d4bd00618b2736e7be")}, 1278002},
},
};

base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
Expand Down
1 change: 1 addition & 0 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ class NodeImpl : public Node
{
m_context = context;
}
bool snapshotLoad(const std::string& path_string) override { return chainman().LoadSnapshot(*m_context, path_string); }
ArgsManager& args() { return *Assert(Assert(m_context)->args); }
ChainstateManager& chainman() { return *Assert(m_context->chainman); }
NodeContext* m_context{nullptr};
Expand Down
110 changes: 110 additions & 0 deletions src/node/loadsnapshot.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// 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 <node/loadsnapshot.h>

#include <logging.h>
#include <node/blockstorage.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
#include <streams.h>
#include <validation.h>

#include <util/fs.h>

#include <util/any.h>
#include <string>


using node::BlockManager;
using node::NodeContext;
using node::SnapshotMetadata;

struct CUpdatedBlock
{
uint256 hash;
int height;
};

bool ChainstateManager::LoadSnapshot(NodeContext& node, const std::string& path_string)
{
fs::path path(fs::u8path(path_string));
if (!fs::exists(path)) {
LogPrintf("[loadsnapshot] snapshot file %s does not exist\n", path.u8string());
return false;
}

FILE* file{fsbridge::fopen(path, "rb")};

AutoFile afile{file};
if (afile.IsNull()) {
LogPrintf("[loadsnapshot] failed to open snapshot file %s\n", path_string);
return false;
}

// Read the snapshot metadata.
SnapshotMetadata metadata;
afile >> metadata;

// Get the base blockhash and look up the corresponding CBlockIndex object.
uint256 base_blockhash = metadata.m_base_blockhash;
int max_secs_to_wait_for_headers = 60 * 10;
CBlockIndex* snapshot_start_block = nullptr;

LogPrintf("[loadsnapshot] waiting to see blockheader %s in headers chain before snapshot activation\n",
base_blockhash.ToString());

if (node.chainman == nullptr) {
// Handle error
LogPrintf("[loadsnapshot] node.chainman is null\n");
return false;
}

ChainstateManager& chainman = *node.chainman;

// snapshot_start_block = chainman.m_blockman.LookupBlockIndex(base_blockhash);

// if (!snapshot_start_block) {
// LogPrintf("[loadsnapshot] can't find blockheader %s\n",
// base_blockhash.ToString());
// return false;
// }

while (max_secs_to_wait_for_headers > 0) {
LogPrintf("[loadsnapshot] base_blockhash = %s\n", base_blockhash.ToString());
snapshot_start_block = WITH_LOCK(::cs_main,
return chainman.m_blockman.LookupBlockIndex(base_blockhash));
max_secs_to_wait_for_headers -= 1;

if (!snapshot_start_block) {
std::this_thread::sleep_for(std::chrono::seconds(1));
} else {
break;
}
}

// // snapshot_start_block = chainman.m_blockman.LookupBlockIndex(base_blockhash);

if (!snapshot_start_block) {
LogPrintf("[loadsnapshot] timed out waiting for snapshot start blockheader %s\n",
base_blockhash.ToString());
return false;
}

// Activate the snapshot.
if (!chainman.ActivateSnapshot(afile, metadata, false)) {
// std::string error_message = "Unable to load UTXO snapshot " + path.u8string();
// throw std::runtime_error(error_message);
LogPrintf("[loadsnapshot] Unable to load UTXO snapshot %s\n", path.u8string());
return false;
}

// Get the new tip and print a log message.
CBlockIndex* new_tip{WITH_LOCK(::cs_main, return chainman.ActiveTip())};
LogPrintf("[loadsnashot] Loaded %d coins from snapshot %s at height %d\n",
metadata.m_coins_count, new_tip->GetBlockHash().ToString(), new_tip->nHeight);

return true;
}
57 changes: 37 additions & 20 deletions src/qml/components/ConnectionSettings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,53 @@ import QtQuick.Layouts 1.15
import "../controls"

ColumnLayout {
// TODO: Remove this once storing the snapshot path is implemented
property bool isOnboarding: false
property bool snapshotImported: false
function setSnapshotImported(imported) {
snapshotImported = imported
}
spacing: 4
Setting {
id: gotoSnapshot
Item {
// TODO: Remove this once storing the snapshot path is implemented
visible: !isOnboarding
height: visible ? implicitHeight : 0
Layout.fillWidth: true
header: qsTr("Load snapshot")
description: qsTr("Instant use with background sync")
actionItem: Item {
width: 26
height: 26
CaretRightIcon {
anchors.centerIn: parent
visible: !snapshotImported
color: gotoSnapshot.stateColor
Layout.preferredHeight: gotoSnapshot.height

Setting {
id: gotoSnapshot
visible: parent.visible
Layout.fillWidth: true
header: qsTr("Load snapshot")
description: qsTr("Instant use with background sync")
actionItem: Item {
width: 26
height: 26
CaretRightIcon {
// TODO: aligment will be fixed once Onboarding snapshot works
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
visible: !snapshotImported
color: gotoSnapshot.stateColor
}
GreenCheckIcon {
anchors.centerIn: parent
visible: snapshotImported
color: Theme.color.transparent
}
}
GreenCheckIcon {
anchors.centerIn: parent
visible: snapshotImported
color: Theme.color.transparent
onClicked: {
connectionSwipe.incrementCurrentIndex()
connectionSwipe.incrementCurrentIndex()
}
}
onClicked: {
connectionSwipe.incrementCurrentIndex()
connectionSwipe.incrementCurrentIndex()
}
}
Separator { Layout.fillWidth: true }
Separator {
Layout.fillWidth: true
// TODO: Remove this once storing the snapshot path is implemented
visible: !isOnboarding
}
Setting {
Layout.fillWidth: true
header: qsTr("Enable listening")
Expand Down
25 changes: 22 additions & 3 deletions src/qml/components/SnapshotSettings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Dialogs 1.3


import "../controls"

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


// TODO: Remove simulation timer before release
Timer {
id: snapshotSimulationTimer
interval: 50 // Update every 50ms
Expand Down Expand Up @@ -78,8 +80,25 @@ ColumnLayout {
Layout.alignment: Qt.AlignCenter
text: qsTr("Choose snapshot file")
onClicked: {
settingsStack.currentIndex = 1
snapshotSimulationTimer.start()
// TODO: Connect this to snapshot loading
// settingsStack.currentIndex = 1
fileDialog.open()
}
}

FileDialog {
id: fileDialog
folder: shortcuts.home
selectMultiple: false
onAccepted: {
console.log("File chosen:", fileDialog.fileUrls)
var snapshotFileName = fileDialog.fileUrl.toString()
console.log("Snapshot file name:", snapshotFileName)
if (snapshotFileName.endsWith(".dat")) {
// optionsModel.setSnapshotDirectory(snapshotFileName)
// console.log("Snapshot directory set:", optionsModel.getSnapshotDirectory())
nodeModel.initializeSnapshot(true, snapshotFileName)
}
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions src/qml/models/nodemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include <QMetaObject>
#include <QTimerEvent>
#include <QString>
#include <QThread>
#include <QUrl>
#include <QDebug>

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

Q_EMIT setTimeRatioListInitial();
// TODO: fix this so that it works once storing the snapshot path is implemented
Q_EMIT initializationFinished();
}

void NodeModel::startShutdownPolling()
Expand Down Expand Up @@ -166,3 +171,25 @@ void NodeModel::ConnectToNumConnectionsChangedSignal()
setNumOutboundPeers(new_num_peers.outbound_full_relay + new_num_peers.block_relay);
});
}

// Loads a snapshot from a given path using FileDialog
void NodeModel::initializeSnapshot(bool initLoadSnapshot, QString path_file) {
if (initLoadSnapshot) {
// TODO: this is to deal with FileDialog returning a QUrl
path_file = QUrl(path_file).toLocalFile();
// TODO: Remove this before release
// qDebug() << "path_file: " << path_file;
QThread* snapshot_thread = new QThread();

// Capture path_file by value
auto lambda = [this, path_file]() {
bool result = this->snapshotLoad(path_file);
Q_EMIT snapshotLoaded(result);
};

connect(snapshot_thread, &QThread::started, lambda);
connect(snapshot_thread, &QThread::finished, snapshot_thread, &QThread::deleteLater);

snapshot_thread->start();
}
}
5 changes: 5 additions & 0 deletions src/qml/models/nodemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class NodeModel : public QObject
Q_INVOKABLE void startNodeInitializionThread();
Q_INVOKABLE void requestShutdown();

Q_INVOKABLE void initializeSnapshot(bool initLoadSnapshot, QString path_file);
Q_INVOKABLE bool snapshotLoad(QString path_file) const { return m_node.snapshotLoad(path_file.toStdString()); }

void startShutdownPolling();
void stopShutdownPolling();

Expand All @@ -77,6 +80,8 @@ public Q_SLOTS:

void setTimeRatioList(int new_time);
void setTimeRatioListInitial();
void initializationFinished();
void snapshotLoaded(bool result);

protected:
void timerEvent(QTimerEvent* event) override;
Expand Down
Loading

0 comments on commit 8434d40

Please sign in to comment.