// Milestone A3 demo — download block headers from a real DGB peer, // chain them by prev-hash, report tip height and hash. // // Scope note: this does NOT do PoW validation. That requires // implementing the 5 DGB algos (SHA256/Scrypt/Skein/Qubit/Odocrypt) // with per-algo difficulty targets (MultiShield). Separate milestone. // For now we trust the peer's chain and validate only the prev-hash // linkage — enough to prove the wire protocol works end-to-end. import { PeerPool, HeaderChain, encodeGetHeaders, type BlockHeader } from '../src/index.js'; // DGB mainnet genesis block hash (chainparams.cpp). Verified via // `digibyte-cli getblockhash 0` on a synced Core. const DGB_GENESIS_HASH = '7497ea1b465eb39f1c8f507bc877078fe016d6fcb6dfad3a64c98dcc6e1e8496'; const RUN_MS = Number(process.argv[2]) || 60_000; const TARGET_HEADERS = Number(process.argv[3]) || 20_000; // stop when we hit this many console.log('\n=== DigiByte P2P Header Sync Demo ===\n'); console.log(`Runtime cap: ${RUN_MS / 1000}s`); console.log(`Target: ${TARGET_HEADERS.toLocaleString()} headers\n`); const pool = new PeerPool({ poolSize: 3, peerOpts: { handshakeTimeoutMs: 10_000 } }); const chain = new HeaderChain(); // Seed with a stub genesis (hash-only) so buildLocator() has an anchor. // We don't actually have the 80-byte genesis header, but for chaining // forward only the hash matters. chain.appendGenesis( { hash: DGB_GENESIS_HASH, prevBlock: '00'.repeat(32), merkleRoot: '', version: 0, timestamp: 0, bits: 0, nonce: 0, raw: Buffer.alloc(80), }, 0, ); let startedRequest = false; let received = 0; let batches = 0; const t0 = Date.now(); // Wait for at least one peer to be ready, then start syncing through it. pool.on( 'connect', ({ host, port }: { host: string; port: number }) => { if (startedRequest) return; startedRequest = true; console.log(`Using peer ${host}:${port} for header sync.\n`); const peer = pool.pickReady()[0]; if (!peer) return; peer.on('headers', (headers: BlockHeader[]) => { batches++; received += headers.length; const res = chain.append(headers); const tipStr = chain.tip ? chain.tip.hash.slice(0, 12) + '...' : '(none)'; console.log( ` batch #${batches}: got ${headers.length}, appended ${res.appended}, tip=${chain.tipHeight} ${tipStr}` + (res.rejectedAt ? ` [REJECTED: ${res.reason}]` : ''), ); if (chain.tipHeight >= TARGET_HEADERS) { finish('target reached'); return; } // Request the next batch: locator = our current tip. if (headers.length >= 2000) { // Peer will send at most 2000 per batch; if it sent 2000 there // are likely more. Request next. peer.send('getheaders', encodeGetHeaders(70019, chain.buildLocator())); } else { finish(`peer sent short batch (${headers.length}) — at their tip`); } }); // Kick off the sync — locator = [genesis]. Peer sends 2000 // headers starting from block 1 forward. peer.send('getheaders', encodeGetHeaders(70019, chain.buildLocator())); }, ); const runTimer = setTimeout(() => finish('runtime cap reached'), RUN_MS); function finish(reason: string): void { clearTimeout(runTimer); const elapsed = ((Date.now() - t0) / 1000).toFixed(1); const rate = received > 0 ? (received / (Date.now() - t0) * 1000).toFixed(0) : '0'; console.log('\n--- summary ---'); console.log(` reason: ${reason}`); console.log(` elapsed: ${elapsed}s`); console.log(` batches: ${batches}`); console.log(` headers total: ${received.toLocaleString()}`); console.log(` chain tip: ${chain.tipHeight.toLocaleString()}`); console.log(` chain size: ${chain.size.toLocaleString()}`); console.log(` effective rate: ${rate} headers/sec`); if (chain.tip) console.log(` tip hash: ${chain.tip.hash}`); pool.stop(); process.exit(0); }