// Milestone A6 demo — persistent, disk-backed header chain. // Run once: downloads N headers from network, writes them to disk as // they arrive. Run again: adopts the tip from disk in milliseconds // and resumes from there. // // Usage: // npm run cache -w @dgb-wallet/p2p [target_height] [cache_dir] import { rm } from 'node:fs/promises'; import { PeerPool, HeaderChain, encodeGetHeaders, defaultCacheDir, type BlockHeader, } from '../src/index.js'; const DGB_GENESIS_HASH = '7497ea1b465eb39f1c8f507bc877078fe016d6fcb6dfad3a64c98dcc6e1e8496'; const args = process.argv.slice(2).filter((a) => !a.startsWith('--')); const TARGET = Number(args[0]) || 80_000; const CACHE_DIR = args[1] || defaultCacheDir(); const CLEAR = process.argv.includes('--clear'); console.log('\n=== DigiByte Header Cache Demo (A6, disk-backed) ===\n'); console.log(`Cache dir: ${CACHE_DIR}`); console.log(`Target: ${TARGET.toLocaleString()} headers`); if (CLEAR) console.log('Flag --clear given → wiping cache first'); console.log(); if (CLEAR) { await rm(CACHE_DIR, { recursive: true, force: true }); } const t0 = Date.now(); const chain = new HeaderChain({ dir: CACHE_DIR }); chain.appendGenesis({ hash: DGB_GENESIS_HASH, prevBlock: '00'.repeat(32), merkleRoot: '', version: 0, timestamp: 0, bits: 0, nonce: 0, raw: Buffer.alloc(80), }, 0); const startTip = chain.tipHeight; const openMs = Date.now() - t0; console.log(`[chain] opened in ${openMs}ms — tip=${startTip.toLocaleString()} (adopted from disk)`); if (chain.tipHeight >= TARGET) { console.log(`\nAlready at or past target height ${TARGET.toLocaleString()}. Nothing to sync.`); console.log(`Elapsed: ${(Date.now() - t0)}ms (this launch is entirely disk-served)`); chain.close(); process.exit(0); } console.log(`\nSyncing headers from ${chain.tipHeight.toLocaleString()} to ${TARGET.toLocaleString()}...`); const pool = new PeerPool({ poolSize: 3, peerOpts: { handshakeTimeoutMs: 10_000 } }); let peer: ReturnType[number] | null = null; const netStart = Date.now(); pool.on('connect', () => { if (peer) return; peer = pool.pickReady()[0]; if (!peer) return; console.log(` peer: ${peer.host}:${peer.port}`); peer.on('headers', (headers: BlockHeader[]) => { chain.append(headers); // writes straight to disk, batched per call process.stdout.write(`\r height: ${chain.tipHeight.toLocaleString()} `); if (chain.tipHeight >= TARGET) { const netMs = Date.now() - netStart; const added = chain.tipHeight - startTip; console.log(`\n\n network sync: ${netMs}ms for ${added.toLocaleString()} new headers`); console.log(` disk usage: ${(chain.tipHeight * 80 / 1024 / 1024).toFixed(1)} MiB (80 bytes × ${chain.tipHeight.toLocaleString()} headers)`); chain.close(); pool.stop(); process.exit(0); } else if (headers.length >= 2000) { peer!.send('getheaders', encodeGetHeaders(70019, chain.buildLocator())); } }); peer.send('getheaders', encodeGetHeaders(70019, chain.buildLocator())); }); setTimeout(() => { console.error('\ntimed out before reaching target'); chain.close(); pool.stop(); process.exit(1); }, 120_000);