fix(bootstrap): keep the archive unless a checksum truly mismatches
Any verification failure deleted the downloaded archive and forced a full multi-GB re-download on the next attempt. But most failures are not a corrupt archive: a missing or unreadable checksum sidecar, or a fetch blip while pulling the checksum, would throw away a perfectly good file. On top of that the checksum parser took the first token of whatever came back, so an HTTP error page could be mistaken for a digest. - The archive is now removed only on a genuine digest mismatch. A missing sidecar, an unusable one, or a transient fetch error keeps the file so the next attempt can reuse or resume it; the no-checksum case still fails closed and keeps the archive. - The checksum parser now accepts a token only if it is exactly the expected length and all hex, lowercasing it; anything else (an error page, a truncated or wrong-width digest) is rejected. - The small-file fetch moved onto the shared capped HTTP helper, and MD5 is consulted only as a fallback when no SHA-256 is published. Tests cover the checksum parser across good, wrong-length, non-hex, and error-page inputs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -137,6 +137,10 @@ public:
|
||||
/// stripped from each component, so "./wallet.dat" and "DRAGONX.conf." are protected too.
|
||||
static bool isProtectedEntry(const std::string& name);
|
||||
|
||||
/// First whitespace-delimited token of a checksum file, lowercased, iff it is exactly
|
||||
/// expectedHexLen hex chars; "" otherwise (an HTTP error page is never a checksum).
|
||||
static std::string parseChecksumFile(const std::string& content, std::size_t expectedHexLen);
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
Progress progress_;
|
||||
@@ -158,9 +162,6 @@ private:
|
||||
bool 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);
|
||||
@@ -169,9 +170,6 @@ private:
|
||||
/// 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);
|
||||
|
||||
@@ -188,6 +186,10 @@ private:
|
||||
int last_curl_{0};
|
||||
std::string download_error_;
|
||||
|
||||
/// Set by verifyChecksums (worker thread only): true only on a real digest MISMATCH, so the caller
|
||||
/// deletes the archive only then - never for a missing/unusable sidecar or a transient fetch error.
|
||||
bool last_verify_mismatch_{false};
|
||||
|
||||
// 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);
|
||||
|
||||
Reference in New Issue
Block a user