105 lines
3.9 KiB
TypeScript
105 lines
3.9 KiB
TypeScript
|
|
// 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);
|
||
|
|
}
|