utxosnapshot: gate requireCheckpoint on trust source
Local file snapshots (init.cpp) skip the known-checkpoint gate; P2P-delivered snapshots (bootstrap.cpp) keep it. Rationale: the checkpoint gate exists to prevent malicious peers from injecting fake UTXO sets. Local file loads come from operator-trusted sources (filesystem access already grants equal power), so the gate is unnecessary friction.
This commit is contained in:
+5
-2
@@ -792,8 +792,11 @@ bool DownloadUtxoSnapshot(const std::string& host,
|
||||
|
||||
printf("Bootstrap: UTXO snapshot downloaded, loading into database...\n");
|
||||
|
||||
// Load the snapshot into a fresh active chain DB
|
||||
if (!UtxoSnapshot::LoadSnapshot(tmpPath, dataDir, strError)) {
|
||||
// Load the snapshot into a fresh active chain DB. P2P-delivered
|
||||
// snapshots keep the checkpoint gate on (requireCheckpoint=true) —
|
||||
// the manifest height+hash already passed IsKnownCheckpoint above,
|
||||
// and we re-check here as defense in depth.
|
||||
if (!UtxoSnapshot::LoadSnapshot(tmpPath, dataDir, strError, /*requireCheckpoint=*/true)) {
|
||||
fs::remove(tmpPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
+6
-1
@@ -1025,8 +1025,13 @@ bool AppInit2()
|
||||
printf("Found utxo-snapshot.bin — loading UTXO snapshot...\n");
|
||||
uiInterface.InitMessage(_("Loading UTXO snapshot..."));
|
||||
|
||||
// Local file load: skip the checkpoint gate. The operator has
|
||||
// filesystem access, so the trust model is already equivalent
|
||||
// to direct chain state modification — a malicious local file
|
||||
// is no worse than a malicious chain DB. P2P-delivered
|
||||
// snapshots (SnapshotNet) keep the checkpoint gate on.
|
||||
std::string strError;
|
||||
if (UtxoSnapshot::LoadSnapshot(snapshotFile, dataPath, strError)) {
|
||||
if (UtxoSnapshot::LoadSnapshot(snapshotFile, dataPath, strError, /*requireCheckpoint=*/false)) {
|
||||
printf("UTXO snapshot loaded successfully.\n");
|
||||
} else {
|
||||
printf("UTXO snapshot load failed: %s\n", strError.c_str());
|
||||
|
||||
+13
-3
@@ -214,11 +214,20 @@ static bool SnapAddressFromScript(const CScript& script, int& nType, uint160& ha
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LoadSnapshot - load a UTXO snapshot into a fresh LevelDB
|
||||
//
|
||||
// `requireCheckpoint` controls whether the snapshot's tip block must be a
|
||||
// known checkpoint. This gate exists to prevent malicious P2P peers from
|
||||
// tricking the daemon into accepting a fake UTXO set at an arbitrary
|
||||
// height on an alternate chain. Local file loads (operator already has
|
||||
// filesystem access, so the trust model is the same as editing the chain
|
||||
// state directly) skip the gate via requireCheckpoint=false. P2P-delivered
|
||||
// snapshots (SnapshotNet) keep the gate on.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool LoadSnapshot(const fs::path& snapshotPath,
|
||||
const fs::path& /*dataDir — unused; resolved per-backend via GetChainDataDir()*/,
|
||||
std::string& strError)
|
||||
std::string& strError,
|
||||
bool requireCheckpoint)
|
||||
{
|
||||
FILE* file = fopen(snapshotPath.string().c_str(), "rb");
|
||||
if (!file) {
|
||||
@@ -274,8 +283,9 @@ bool LoadSnapshot(const fs::path& snapshotPath,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify snapshot block is a known checkpoint
|
||||
if (!Checkpoints::IsKnownCheckpoint(height, blockHash)) {
|
||||
// Verify snapshot block is a known checkpoint (only for P2P-delivered
|
||||
// snapshots — local files are operator-trusted and can be at any height)
|
||||
if (requireCheckpoint && !Checkpoints::IsKnownCheckpoint(height, blockHash)) {
|
||||
fclose(file);
|
||||
strError = "Snapshot block " + blockHash.ToString() + " at height "
|
||||
+ std::to_string(height) + " is not a known checkpoint";
|
||||
|
||||
+5
-1
@@ -29,10 +29,14 @@ namespace UtxoSnapshot {
|
||||
// Load a UTXO snapshot from a file into a fresh LevelDB.
|
||||
// Writes block index entries, UTXOs, hashBestChain, and dbformat.
|
||||
// The LevelDB must NOT be open yet (call before LoadBlockIndex).
|
||||
// `requireCheckpoint` enforces that the snapshot tip is a known
|
||||
// checkpoint (for P2P-delivered snapshots). Local loads from a
|
||||
// trusted operator pass false.
|
||||
// Returns true on success, sets strError on failure.
|
||||
bool LoadSnapshot(const std::filesystem::path& snapshotPath,
|
||||
const std::filesystem::path& dataDir,
|
||||
std::string& strError);
|
||||
std::string& strError,
|
||||
bool requireCheckpoint);
|
||||
|
||||
} // namespace UtxoSnapshot
|
||||
|
||||
|
||||
Reference in New Issue
Block a user