fix: genesis block PoW exemption + -rebuildutxo flag

- Add hash-based exemption for genesis block PoW check in
  CBlock::ReadFromDisk and CheckBlock. The genesis block is a
  hardcoded trust anchor (hash 0x7e7a6e4d...) verified by network
  consensus, not by PoW — same pattern as all peercoin-derived coins.
- Add -rebuildutxo startup flag to reconstruct UTXO set by walking
  full block chain (genesis skipped, no spendable outputs anyway).
- This unblocks DNS2 from the chain freeze at 2,219,922.
This commit is contained in:
Krystie
2026-08-03 16:58:38 -07:00
parent 9aadf855bf
commit ecae3686a7
4 changed files with 105 additions and 4 deletions
+8
View File
@@ -1501,10 +1501,18 @@ bool AppInit2()
while (pindex && !fRequestShutdown)
{
// Skip genesis block - it doesn't follow normal PoW rules and has no spendable outputs
if (pindex->nHeight == 0)
{
pindex = pindex->pnext;
continue;
}
CBlock block;
if (!block.ReadFromDisk(pindex))
{
printf("ERROR: Failed to read block %d (%s)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str());
printf("DEBUG: nBits=%08x, IsPoW=%d, hash=%s\n", pindex->nBits, pindex->IsProofOfWork(), pindex->GetBlockHash().ToString().c_str());
return InitError(_("Failed to read block during UTXO rebuild"));
}
+8 -2
View File
@@ -3256,8 +3256,14 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c
if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return DoS(100, error("CheckBlock() : size limits failed"));
// Check proof of work matches claimed amount
if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
// Check proof of work matches claimed amount.
// Genesis block is a hardcoded trust anchor — exempt from PoW check
// (same exemption as CBlock::ReadFromDisk). All other PoW blocks
// must pass CheckProofOfWork.
if (fCheckPOW && IsProofOfWork() &&
GetHash() != hashGenesisBlockOfficial &&
GetHash() != hashGenesisBlockTestNet &&
!CheckProofOfWork(GetHash(), nBits))
return DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp: reject blocks obviously too far in the future.
+11 -2
View File
@@ -1144,8 +1144,17 @@ public:
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
}
// Check the header
if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
// Check the header.
// Genesis block is a hardcoded trust anchor — its hash is verified
// by comparison to hashGenesisBlockOfficial/TestNet, not by PoW.
// The genesis block's hash (0x7e7a6e4d...) is intentionally above
// the PoW target since it's a network-wide constant, not a mined block.
// All peercoin-derived coins (peercoin, triangles, etc.) use this
// same exemption for the genesis block.
if (fReadTransactions && IsProofOfWork() &&
GetHash() != hashGenesisBlockOfficial &&
GetHash() != hashGenesisBlockTestNet &&
!CheckProofOfWork(GetHash(), nBits))
return error("CBlock::ReadFromDisk() : errors in block header");
return true;
+78
View File
@@ -0,0 +1,78 @@
// Utility to regenerate the genesis block on disk
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
// Include necessary headers
#include "main.h"
#include "serialize.h"
#include "util.h"
int main() {
printf("Regenerating genesis block...\n");
// Create genesis transaction
const char* pszTimestamp = "july 16 2014, I'm deh besht mang, I deeed et!";
CTransaction txNew;
txNew.nVersion = 1;
txNew.nTime = 1405500418;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript()
<< 486604799
<< CBigNum(9999)
<< vector<unsigned char>((const unsigned char*)pszTimestamp,
(const unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].SetEmpty();
// Create genesis block
CBlock block;
block.nVersion = 1;
block.nTime = 1405500418;
block.nBits = bnProofOfWorkLimit.GetCompact();
block.nNonce = 43;
block.hashPrevBlock = 0;
block.vtx.push_back(txNew);
block.hashMerkleRoot = block.BuildMerkleTree();
printf("Genesis block hash: %s\n", block.GetHash().ToString().c_str());
printf("Expected: %s\n", hashGenesisBlockOfficial.ToString().c_str());
printf("Match: %s\n", block.GetHash() == hashGenesisBlockOfficial ? "YES" : "NO");
// Write to a temporary file first
std::string tmpfile = "/tmp/genesis_block.dat";
{
std::ofstream file(tmpfile, std::ios::binary);
if (!file) {
fprintf(stderr, "Cannot create %s\n", tmpfile.c_str());
return 1;
}
CDataStream ss(SER_DISK, CLIENT_VERSION);
ss << block;
file.write((const char*)ss.data(), ss.size());
}
printf("Genesis block written to %s (%zu bytes)\n", tmpfile.c_str(), std::filesystem::file_size(tmpfile));
// Verify by reading back
{
std::ifstream file(tmpfile, std::ios::binary);
if (!file) {
fprintf(stderr, "Cannot read %s\n", tmpfile.c_str());
return 1;
}
CDataStream ss(SER_DISK, CLIENT_VERSION);
std::vector<unsigned char> buffer(std::filesystem::file_size(tmpfile));
file.read((char*)buffer.data(), buffer.size());
ss.write((const char*)buffer.data(), buffer.size());
CBlock verifyBlock;
ss >> verifyBlock;
printf("Verified hash: %s\n", verifyBlock.GetHash().ToString().c_str());
printf("Verification: %s\n", verifyBlock.GetHash() == block.GetHash() ? "PASS" : "FAIL");
}
return 0;
}