dgb-wallet/packages/p2p/scripts/cache-demo.ts

88 lines
3.1 KiB
TypeScript
Raw Permalink Normal View History

// 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<typeof pool.pickReady>[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);