-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DRAFT] WIP QML Load Snapshot Signet (160,000 height) #424
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ | |
#include <node/context.h> | ||
#include <node/interface_ui.h> | ||
#include <node/transaction.h> | ||
#include <node/utxo_snapshot.h> | ||
#include <policy/feerate.h> | ||
#include <policy/fees.h> | ||
#include <policy/policy.h> | ||
|
@@ -395,9 +396,42 @@ class NodeImpl : public Node | |
{ | ||
m_context = context; | ||
} | ||
double getSnapshotProgress() override { return m_snapshot_progress.load(); } | ||
bool snapshotLoad(const std::string& path_string) override | ||
{ | ||
const fs::path path = fs::u8path(path_string); | ||
if (!fs::exists(path)) { return false; } | ||
|
||
AutoFile afile{fsbridge::fopen(path, "rb")}; | ||
if (afile.IsNull()) { return false; } | ||
|
||
SnapshotMetadata metadata; | ||
try { | ||
afile >> metadata; | ||
} catch (const std::exception& e) { return false; } | ||
|
||
const uint256& base_blockhash = metadata.m_base_blockhash; | ||
|
||
if (!m_context->chainman) { return false; } | ||
|
||
ChainstateManager& chainman = *m_context->chainman; | ||
CBlockIndex* snapshot_start_block = nullptr; | ||
|
||
// Wait for the block to appear in the block index | ||
//TODO: remove this once another method is implemented | ||
constexpr int max_wait_seconds = 600; // 10 minutes | ||
for (int i = 0; i < max_wait_seconds; ++i) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't have a blocking component here. If there are any issues we should just fail. |
||
snapshot_start_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(base_blockhash)); | ||
if (snapshot_start_block) break; | ||
std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
} | ||
|
||
return chainman.ActivateSnapshot(afile, metadata, false); | ||
} | ||
ArgsManager& args() { return *Assert(Assert(m_context)->args); } | ||
ChainstateManager& chainman() { return *Assert(m_context->chainman); } | ||
NodeContext* m_context{nullptr}; | ||
std::atomic<double> m_snapshot_progress{0.0}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagine the actual progress should be owned by the object in charge of doing the processing. ChainStateManager? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes! Will set it up so that the handler notifies the snapshot progress and that ownership belongs to ChainstateManager. |
||
}; | ||
|
||
bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock, const CChain& active, const BlockManager& blockman) | ||
|
@@ -510,7 +544,7 @@ class RpcHandlerImpl : public Handler | |
class ChainImpl : public Chain | ||
{ | ||
public: | ||
explicit ChainImpl(NodeContext& node) : m_node(node) {} | ||
explicit ChainImpl(node::NodeContext& node) : m_node(node) {} | ||
std::optional<int> getHeight() override | ||
{ | ||
const int height{WITH_LOCK(::cs_main, return chainman().ActiveChain().Height())}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,44 +5,28 @@ | |
import QtQuick 2.15 | ||
import QtQuick.Controls 2.15 | ||
import QtQuick.Layouts 1.15 | ||
import QtQuick.Dialogs 1.3 | ||
|
||
import "../controls" | ||
|
||
ColumnLayout { | ||
signal snapshotImportCompleted() | ||
property int snapshotVerificationCycles: 0 | ||
property real snapshotVerificationProgress: 0 | ||
property bool snapshotVerified: false | ||
|
||
id: columnLayout | ||
signal back | ||
property bool snapshotLoading: nodeModel.snapshotLoading | ||
property bool snapshotLoaded: nodeModel.isSnapshotLoaded | ||
property bool snapshotImportCompleted: chainModel.isSnapshotActive | ||
property bool onboarding: false | ||
property bool snapshotVerified: onboarding ? false : chainModel.isSnapshotActive | ||
property string snapshotFileName: "" | ||
property var snapshotInfo: ({}) | ||
property string selectedFile: "" | ||
|
||
width: Math.min(parent.width, 450) | ||
anchors.horizontalCenter: parent.horizontalCenter | ||
|
||
|
||
Timer { | ||
id: snapshotSimulationTimer | ||
interval: 50 // Update every 50ms | ||
running: false | ||
repeat: true | ||
onTriggered: { | ||
if (snapshotVerificationProgress < 1) { | ||
snapshotVerificationProgress += 0.01 | ||
} else { | ||
snapshotVerificationCycles++ | ||
if (snapshotVerificationCycles < 1) { | ||
snapshotVerificationProgress = 0 | ||
} else { | ||
running = false | ||
snapshotVerified = true | ||
settingsStack.currentIndex = 2 | ||
} | ||
} | ||
} | ||
} | ||
|
||
StackLayout { | ||
id: settingsStack | ||
currentIndex: 0 | ||
currentIndex: onboarding ? 0 : snapshotLoaded ? 2 : snapshotVerified ? 2 : snapshotLoading ? 1 : 0 | ||
|
||
ColumnLayout { | ||
Layout.alignment: Qt.AlignHCenter | ||
|
@@ -77,11 +61,22 @@ ColumnLayout { | |
Layout.bottomMargin: 20 | ||
Layout.alignment: Qt.AlignCenter | ||
text: qsTr("Choose snapshot file") | ||
onClicked: { | ||
settingsStack.currentIndex = 1 | ||
snapshotSimulationTimer.start() | ||
onClicked: fileDialog.open() | ||
} | ||
|
||
FileDialog { | ||
id: fileDialog | ||
folder: shortcuts.home | ||
selectMultiple: false | ||
selectExisting: true | ||
nameFilters: ["Snapshot files (*.dat)", "All files (*)"] | ||
onAccepted: { | ||
selectedFile = fileUrl.toString() | ||
snapshotFileName = selectedFile | ||
nodeModel.initializeSnapshot(true, snapshotFileName) | ||
} | ||
} | ||
// TODO: Handle file error signal | ||
} | ||
|
||
ColumnLayout { | ||
|
@@ -102,17 +97,10 @@ ColumnLayout { | |
Layout.leftMargin: 20 | ||
Layout.rightMargin: 20 | ||
header: qsTr("Loading Snapshot") | ||
description: qsTr("This might take a while...") | ||
} | ||
|
||
ProgressIndicator { | ||
id: progressIndicator | ||
Layout.topMargin: 20 | ||
width: 200 | ||
height: 20 | ||
progress: snapshotVerificationProgress | ||
Layout.alignment: Qt.AlignCenter | ||
progressColor: Theme.color.blue | ||
} | ||
// TODO: add progress indicator once the snapshot progress is implemented | ||
} | ||
|
||
ColumnLayout { | ||
|
@@ -137,20 +125,19 @@ ColumnLayout { | |
descriptionColor: Theme.color.neutral6 | ||
descriptionSize: 17 | ||
descriptionLineHeight: 1.1 | ||
description: qsTr("It contains transactions up to January 12, 2024. Newer transactions still need to be downloaded." + | ||
" The data will be verified in the background.") | ||
description: snapshotInfo && snapshotInfo["date"] ? | ||
qsTr("It contains transactions up to %1. Newer transactions still need to be downloaded." + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not really "transactions up to", it's all coins (utxo set) that exist at the snapshot height. Maybe say something like:
(intentionally leaves it vague what's in the snapshot) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the sequence, this is the fourth snapshot-related screen (state) people see:
I think the point that not all historical transactions are in the snapshot is worth clarifying. The first time people are exposed to what this feature does is in 2, so I'd probably start tweaking at that point. We call it a "recent transaction snapshot" there, which is more precise than just "transactions" ("unspent transaction snapshot" would be a little closer). We don't need to be 100% technically accurate and I'm not sure users are well-served if we're completely vague about this either. They should be able to form a good enough mental model for making decisions and understanding their impact. So how about this?
Or this?
Either way, we should ensure to tweak the text across the sequence if we make changes here. We show the block height in a "View details" option below (did not check if it's implemented in this PR already), so not sure whether we need to include that in the text as well. As for what happens next, my thinking was that we just say that the data will be filled in and verified here (keep it short), and then have indicators contextually in the UI as to what is happening (balance, send and transaction list have respective load states). So less absorption of logic here, and more contextual info in the appropriate places. Sorry, this has gotten quite long. Hope it's a helpful response. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think it's better to be incomplete than incorrect. The nice thing about being vague is that it's clear to the reader there's more to learn. One reason the phrasing "recent / historical transactions" is potentially confusing is that people may think they can recover historical wallet transactions with it. But it has nothing to do with that. The concept that's useful to explain is that the snapshot allows us to start syncing at block %1, saving time by skipping historical blocks. This way the user can get started (with their wallet) earlier. Meanwhile it's safe, because we do check those historical blocks in the background later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TL;DR I reviewed the onboarding text again, and it seems like the use of Unless we come up with something better, I'd be fine with retaining what we have currently though I agree it's inaccurate. Suggestions below:
It does seem like we can strike a better balance between technical accuracy & usefulness to user here, and use vagueness where required.
Agree, this was in the initial mockups I did. edit: removed something here. read on...
Agree.
This screen can omit mention of transactions "The snapshot allows you to start using the application more quickly. Once loaded, it will be automatically verified in the background. Learn more" instead of "You can start using the application more quickly by loading a recent transaction snapshot. It will be automatically verified in the background."
I'd suggest something like "The loaded snapshot contains data up to [some date]. The initial download will continue now. The snapshot is verified in the background." instead of "It contains transactions up to September 10, 2023. Newer transactions still need to be downloaded. The data will be verified in the background." edit: rephrased my own suggestion slightly. |
||
" The data will be verified in the background.").arg(snapshotInfo["date"]) : | ||
qsTr("It contains transactions up to DEBUG. Newer transactions still need to be downloaded." + | ||
" The data will be verified in the background.") | ||
} | ||
|
||
ContinueButton { | ||
Layout.preferredWidth: Math.min(300, columnLayout.width - 2 * Layout.leftMargin) | ||
Layout.topMargin: 40 | ||
Layout.alignment: Qt.AlignCenter | ||
text: qsTr("Done") | ||
onClicked: { | ||
snapshotImportCompleted() | ||
connectionSwipe.decrementCurrentIndex() | ||
connectionSwipe.decrementCurrentIndex() | ||
} | ||
onClicked: back() | ||
} | ||
|
||
Setting { | ||
|
@@ -188,16 +175,25 @@ ColumnLayout { | |
font.pixelSize: 14 | ||
} | ||
CoreText { | ||
text: qsTr("200,000") | ||
text: snapshotInfo && snapshotInfo["height"] ? | ||
snapshotInfo["height"] : qsTr("DEBUG") | ||
Layout.alignment: Qt.AlignRight | ||
font.pixelSize: 14 | ||
} | ||
} | ||
Separator { Layout.fillWidth: true } | ||
CoreText { | ||
text: qsTr("Hash: 0x1234567890abcdef...") | ||
text: snapshotInfo && snapshotInfo["hashSerialized"] ? | ||
qsTr("Hash: %1").arg(snapshotInfo["hashSerialized"].substring(0, 13) + "...") : | ||
qsTr("Hash: DEBUG") | ||
font.pixelSize: 14 | ||
} | ||
|
||
Component.onCompleted: { | ||
if (snapshotVerified || snapshotLoaded) { | ||
snapshotInfo = chainModel.getSnapshotInfo() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
8beb50a: the signet snapshot was merged into Bitcoin Core more than a year ago in bitcoin/bitcoin@edbed31. This also seems to be using the older serialization format from before bitcoin/bitcoin#29612.
I guess that's all fine for a proof of concept, but it would be better if the main branch of this repo was more up to date.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Sjors! Thanks for taking a look and yes completely agree with:
We are currently taking steps to sync with upstream, meanwhile I wanted to get the ball rolling, although I will pull in the update versions of the assume utxo functionality so that when the sync happens this PR can be merged with minimal refactoring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another thing you could do is look into making a PR to the Bitcoin Core repo with your proposed interface changes. One way to go about that is to somehow use them in existing or a new RPC method. E.g. snapshot load progress is probably useful to have available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the current medium term plan, once we have an implementation that works and makes sense here we plan to go upstream (bitcoin/bitcoin) and open a PR there.