Files
triangles_v5/src/scan_chain_tip.cpp
T

194 lines
5.6 KiB
C++

// Fast chain tip scanner - reads blk0001.dat and finds the chain tip
// Uses Hash9 to compute real block hashes, but skips full validation
// Compile: g++ -O2 -o scan_chain_tip scan_chain_tip.cpp -I. -std=c++11
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <map>
#include <vector>
#include <string>
// Hash9 includes
#include "hashblock.h"
// Triangles mainnet magic
static const unsigned char pchMagic[4] = { 0x70, 0x35, 0x22, 0x05 };
struct BlockHeader {
int32_t nVersion;
uint8_t hashPrevBlock[32];
uint8_t hashMerkleRoot[32];
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
};
struct BlockInfo {
uint8_t prevHash[32];
uint32_t nTime;
long filePos;
};
static void hash9(const void* input, size_t len, uint8_t* output) {
// Use the Hash9 function from hashblock.h
uint256 result = Hash9((const char*)input, (const char*)input + len);
memcpy(output, &result, 32);
}
static std::string hashToHex(const uint8_t* hash) {
char buf[65];
// Display in big-endian (standard Bitcoin display format)
for (int i = 31; i >= 0; i--)
sprintf(buf + (31 - i) * 2, "%02x", hash[i]);
buf[64] = 0;
return std::string(buf);
}
int main(int argc, char* argv[]) {
const char* filename = argc > 1 ? argv[1] : "blk0001.dat";
FILE* f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Cannot open %s\n", filename);
return 1;
}
// Get file size
fseek(f, 0, SEEK_END);
long fileSize = ftell(f);
fseek(f, 0, SEEK_SET);
printf("Scanning %s (%ld bytes, %.1f MB)\n", filename, fileSize, fileSize / 1048576.0);
// Map: block_hash -> BlockInfo
// We store hashes as 32-byte arrays, use string keys for the map
std::map<std::string, BlockInfo> blocks;
// Set of all prevHashes (to find the tip = block not referenced as prev)
std::map<std::string, bool> isPrevOf;
int blockCount = 0;
unsigned char magic[4];
uint32_t blockSize;
BlockHeader header;
while (true) {
long pos = ftell(f);
// Read magic
if (fread(magic, 1, 4, f) != 4) break;
if (memcmp(magic, pchMagic, 4) != 0) {
// Scan forward for magic
fseek(f, pos + 1, SEEK_SET);
continue;
}
// Read block size
if (fread(&blockSize, 4, 1, f) != 1) break;
if (blockSize == 0 || blockSize > 4000000) {
fseek(f, pos + 1, SEEK_SET);
continue;
}
long headerPos = ftell(f);
// Read header
if (fread(&header, sizeof(header), 1, f) != 1) break;
// Compute Hash9 of header
uint8_t blockHash[32];
hash9(&header, 80, blockHash);
std::string hashKey((char*)blockHash, 32);
std::string prevKey((char*)header.hashPrevBlock, 32);
BlockInfo info;
memcpy(info.prevHash, header.hashPrevBlock, 32);
info.nTime = header.nTime;
info.filePos = pos;
blocks[hashKey] = info;
isPrevOf[prevKey] = true;
blockCount++;
if (blockCount % 100000 == 0) {
float pct = (float)ftell(f) / fileSize * 100;
printf(" %d blocks scanned (%.1f%%)...\n", blockCount, pct);
fflush(stdout);
}
// Skip rest of block
long remaining = blockSize - 80;
if (remaining > 0)
fseek(f, headerPos + blockSize, SEEK_SET);
}
fclose(f);
printf("\nTotal blocks in file: %d\n", blockCount);
// Find chain tips: blocks whose hash is NOT in isPrevOf
printf("\nSearching for chain tips...\n");
std::vector<std::pair<std::string, BlockInfo>> tips;
for (auto& kv : blocks) {
if (isPrevOf.find(kv.first) == isPrevOf.end()) {
tips.push_back(kv);
}
}
printf("Found %zu chain tip(s)\n\n", tips.size());
// For each tip, walk back to find the height
for (auto& tip : tips) {
std::string current = tip.first;
int height = 0;
// Walk back through the chain
while (true) {
auto it = blocks.find(current);
if (it == blocks.end()) break;
std::string prevKey((char*)it->second.prevHash, 32);
// Check if prevHash is all zeros (genesis)
bool isGenesis = true;
for (int i = 0; i < 32; i++) {
if (it->second.prevHash[i] != 0) { isGenesis = false; break; }
}
if (isGenesis) break;
current = prevKey;
height++;
}
uint8_t tipHash[32];
memcpy(tipHash, tip.first.data(), 32);
printf("============================================================\n");
printf("CHAIN TIP:\n");
printf(" Height: %d\n", height);
printf(" Hash: 0x%s\n", hashToHex(tipHash).c_str());
printf(" Time: %u\n", tip.second.nTime);
// Also print last 10 blocks before tip
printf("\nLast 10 blocks before tip:\n");
current = tip.first;
std::vector<std::pair<int, std::string>> lastBlocks;
for (int i = 0; i <= 10 && blocks.find(current) != blocks.end(); i++) {
uint8_t h[32];
memcpy(h, current.data(), 32);
lastBlocks.push_back({height - i, hashToHex(h)});
std::string prevKey((char*)blocks[current].prevHash, 32);
current = prevKey;
}
for (auto it = lastBlocks.rbegin(); it != lastBlocks.rend(); ++it) {
printf(" %8d: 0x%s\n", it->first, it->second.c_str());
}
printf("============================================================\n\n");
}
return 0;
}