Packages: core (BIP39/32/44/49/84/86 derivation, WIF), keystore (PBKDF2 + AES-GCM), psbt, p2p (BIP157/158 SPV daemon, Dandelion++ broadcast with relay-echo verification), blockbook, rpc, electrum, net, bns, web-ui (React/Vite), mobile (Expo). ops/ holds the nginx vhost, systemd unit and push.sh deploy script for digibyte.x/web.
104 lines
3.9 KiB
TypeScript
104 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);
|
|
}
|