Files
ObsidianDragon/src/util/bootstrap.h
DanS 474250bb50 v1.1.0: explorer tab, bootstrap fixes, full theme overlay merge
Explorer tab:
- New block explorer tab with search, chain stats, mempool info,
  recent blocks table, block detail modal with tx expansion
- Sidebar nav entry, i18n strings, ui.toml layout values

Bootstrap fixes:
- Move wizard Done handler into render() — was dead code, preventing
  startEmbeddedDaemon() and tryConnect() from firing post-wizard
- Stop deleting BDB database/ dir during cleanup — caused LSN mismatch
  that salvaged wallet.dat into wallet.{timestamp}.bak
- Add banlist.dat, db.log, .lock to cleanup file list
- Fatal extraction failure for blocks/ and chainstate/ files
- Verification progress: split SHA-256 (0-50%) and MD5 (50-100%)

Theme system:
- Expand overlay merge to apply ALL sections (tabs, dialogs, components,
  screens, flat sections), not just theme+backdrop+effects
- Add screens and security section parsing to UISchema
- Build-time theme expansion via expand_themes.py (CMake + build.sh)

Other:
- Version bump to 1.1.0
- WalletState::clear() resets all fields (sync, daemon info, etc.)
- Sidebar item-height 42 → 36
2026-03-17 18:49:46 -05:00

119 lines
4.3 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include <string>
#include <vector>
#include <mutex>
#include <atomic>
#include <thread>
namespace dragonx {
namespace util {
/**
* @brief Bootstrap downloader + extractor for DRAGONX blockchain data.
*
* Downloads a blockchain snapshot zip from bootstrap.dragonx.is, verifies
* SHA-256 / MD5 checksums, extracts it into the DRAGONX data directory
* (skipping wallet.dat), and cleans old chain data before extraction.
* All work runs on a background thread; progress is queried thread-safely
* from the UI thread.
*/
class Bootstrap {
public:
enum class State {
Idle,
Downloading,
Verifying,
Extracting,
Completed,
Failed
};
struct Progress {
State state = State::Idle;
double downloaded_bytes = 0;
double total_bytes = 0; // 0 if server doesn't send Content-Length
float percent = 0.0f; // 0..100
std::string status_text; // human-readable status
std::string error; // non-empty on failure
};
Bootstrap() = default;
~Bootstrap();
// Non-copyable
Bootstrap(const Bootstrap&) = delete;
Bootstrap& operator=(const Bootstrap&) = delete;
/// Base URL for bootstrap downloads (zip + checksum files).
static constexpr const char* kBaseUrl = "https://bootstrap.dragonx.is";
static constexpr const char* kMirrorUrl = "https://bootstrap2.dragonx.is";
static constexpr const char* kZipName = "DRAGONX.zip";
/// Start the bootstrap process on a background thread.
/// @param dataDir Path to DRAGONX data dir (e.g. ~/.hush/DRAGONX/)
/// @param url Bootstrap zip URL
void start(const std::string& dataDir,
const std::string& url = "https://bootstrap.dragonx.is/DRAGONX.zip");
/// Cancel an in-progress download/extract.
void cancel();
/// Thread-safe read of current progress.
Progress getProgress() const;
/// Whether the operation is finished (success or failure).
bool isDone() const;
/// Format a byte count as human-readable string (e.g. "1.24 GB")
static std::string formatSize(double bytes);
private:
mutable std::mutex mutex_;
Progress progress_;
std::atomic<bool> cancel_requested_{false};
std::thread worker_;
std::atomic<bool> worker_running_{false};
bool download(const std::string& url, const std::string& destZip);
bool extract(const std::string& zipPath, const std::string& dataDir);
void cleanChainData(const std::string& dataDir);
void setProgress(State state, const std::string& text, double downloaded = 0, double total = 0);
/// Download a small text file (checksum) into a string. Returns empty on failure.
std::string downloadSmallFile(const std::string& url);
/// Compute SHA-256 of a file, return lowercase hex digest.
/// pctBase/pctRange map file progress onto a portion of the overall percent.
std::string computeSHA256(const std::string& filePath, float pctBase = 0.0f, float pctRange = 100.0f);
/// Compute MD5 of a file, return lowercase hex digest.
/// pctBase/pctRange map file progress onto a portion of the overall percent.
std::string computeMD5(const std::string& filePath, float pctBase = 0.0f, float pctRange = 100.0f);
/// Parse the first hex token from a checksum file (handles "<hash> <filename>" format).
static std::string parseChecksumFile(const std::string& content);
/// Verify downloaded zip against remote checksums. Returns true if valid.
bool verifyChecksums(const std::string& zipPath, const std::string& baseUrl);
/// Perform a HEAD request to get remote file size.
/// Returns -1 on failure.
long long getRemoteFileSize(const std::string& url);
/// Byte offset of the partial file when resuming a download.
/// Added to dlnow in progress reports so the bar reflects true total.
double resume_offset_{0};
// libcurl progress callback (static → calls into instance via clientp)
static int progressCallback(void* clientp, long long dltotal, long long dlnow,
long long ultotal, long long ulnow);
};
} // namespace util
} // namespace dragonx