From d2679e2c09c17e4af50535c13324ba2ddb2eb4ad Mon Sep 17 00:00:00 2001 From: Bitflash Date: Mon, 10 Aug 2026 11:50:10 -0300 Subject: tools: static block explorer generator (v0) --- scripts/build-explorer.py | 243 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100755 scripts/build-explorer.py (limited to 'scripts/build-explorer.py') diff --git a/scripts/build-explorer.py b/scripts/build-explorer.py new file mode 100755 index 0000000..7849e8a --- /dev/null +++ b/scripts/build-explorer.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# +# Build a static Bitflash block explorer (v0) from a node's blk0001.dat. +# Emits into : +# blocks.json summary of every main-chain block (newest first) +# block/.json full detail per block (header + all txs) +# index.html self-contained viewer (vanilla JS, same-origin fetch) +# +# Scope (v0, deliberately): block list, block detail, and coinbase. No mempool, +# no per-address history. On this chain the coinbase payout key rotates every +# block, so an address index would be nearly useless; block-level transparency is +# what lets miners and users confirm the chain is honest. +# +# Usage: build-explorer.py + +import sys, os, json, hashlib, struct + +MAGIC = bytes([0xbf, 0x20, 0x5c, 0xfd]) +ADDRESS_VERSION = 25 # Bitflash addresses start with "B" + +def sha256d(b): return hashlib.sha256(hashlib.sha256(b).digest()).digest() + +B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" +def b58check(payload): + v = bytes([ADDRESS_VERSION]) + payload + v += sha256d(v)[:4] + n = int.from_bytes(v, 'big') + s = "" + while n > 0: + n, r = divmod(n, 58); s = B58[r] + s + for byte in v: + if byte == 0: s = "1" + s + else: break + return s + +def hash160(b): + return hashlib.new('ripemd160', hashlib.sha256(b).digest()).digest() + +class R: + def __init__(s, b, o=0): s.b = b; s.o = o + def u(s, n): v = s.b[s.o:s.o+n]; s.o += n; return v + def i32(s): return struct.unpack(' OP_EQUALVERIFY OP_CHECKSIG + if len(spk) == 25 and spk[0] == 0x76 and spk[1] == 0xa9 and spk[2] == 0x14 and spk[23] == 0x88 and spk[24] == 0xac: + return {"type": "p2pkh", "address": b58check(spk[3:23])} + # P2PK: OP_CHECKSIG (65- or 33-byte key) + if len(spk) >= 35 and spk[-1] == 0xac: + if spk[0] == 0x41 and len(spk) == 67: + pk = spk[1:66]; return {"type": "p2pk", "pubkey": pk.hex(), "address": b58check(hash160(pk))} + if spk[0] == 0x21 and len(spk) == 35: + pk = spk[1:34]; return {"type": "p2pk", "pubkey": pk.hex(), "address": b58check(hash160(pk))} + return {"type": "other", "hex": spk.hex()[:80]} + +def parse_tx(r): + start = r.o + r.i32() + nin = r.cs(); vin = [] + for _ in range(nin): + ph = r.u(32); idx = r.u32(); r.script(); r.u32() + coinbase = (ph == b'\x00' * 32 and idx == 0xffffffff) + vin.append({"coinbase": coinbase, "prev": (None if coinbase else ph[::-1].hex()), "vout": (None if coinbase else idx)}) + nout = r.cs(); vout = [] + for _ in range(nout): + val = r.i64(); spk = r.script(); vout.append(dict({"value": val}, **spk_info(spk))) + r.u32() + raw = r.b[start:r.o] + return {"txid": sha256d(raw)[::-1].hex(), "vin": vin, "vout": vout, "size": len(raw)} + +def main(): + if len(sys.argv) != 3: + print("usage: build-explorer.py ", file=sys.stderr); sys.exit(2) + blkpath, out = sys.argv[1], sys.argv[2] + data = open(blkpath, 'rb').read() + blocks = {}; o = 0; n = len(data) + while o + 8 <= n: + if data[o:o+4] != MAGIC: o += 1; continue + o += 4; size = struct.unpack(' + + + + +Bitflash Block Explorer + + + +
+

Bitflash Block Explorer

+ + + +
+
+

Read-only view of the main chain: blocks, their transactions, and the coinbase payout. There is no mempool and no per-address history here. On Bitflash the coinbase payout key rotates every block, so an address index would say almost nothing; what matters is that every block and every coinbase is here to check.

+
+ +
+ + +
HeightTime (UTC)TxsSizeCoinbaseTo
+

+
+ +
+ + + +""" + +if __name__ == "__main__": + main() -- cgit v1.2.3