deviant/scripts/chipnet-doublespend-recheck.mjs
walerikus 518a3ce174 chore: initial project baseline — full Deviant codebase under version control
Brings the whole project into git (previously only the on-ramp feature and
a few parallel files were tracked). Includes: wallet core (src/), CashScript
contracts/, the Capacitor shell (shells/), scripts/, tests, and the docs
corpus (wiki/, snapshots/, Parameters/, Archive/, KnowledgeBase text).

Excluded via .gitignore: node_modules/, dist/, .env (secrets), testnet WIF
key files (.chipnet-*.json, devWallets.ts), KnowledgeBase/RAW media (~9.6 GB),
transcripts/ (session logs), and machine-local config (.claude/*.local,
*.lock). Secret-scanned before commit — no credentials in the tree.

Note: src/swapMath.ts, src/swapBuilder.ts, contracts/OpenMintToken.cash are
legacy (OP_RETURN era) but still imported; committed as-is, flagged for a
separate removal once de-referenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:38:07 +02:00

91 lines
3.5 KiB
JavaScript

#!/usr/bin/env node
/**
* chipnet-doublespend-recheck.mjs — poll the two conflicting Test-2 txids until
* a block settles the race, then report the winner and confirm the loser was
* dropped. Companion to chipnet-doublespend-test.mjs.
*
* Run: node --import tsx scripts/chipnet-doublespend-recheck.mjs <txidA2> <txidB2>
* Env: POLL_SECONDS=2400 (default 40 min)
*/
import { ElectrumClient } from '@electrum-cash/network';
const POLL_SECONDS = Number(process.env.POLL_SECONDS ?? '2400');
const [idA, idB] = process.argv.slice(2);
if (!idA || !idB) {
console.error('usage: ... chipnet-doublespend-recheck.mjs <txidA2> <txidB2>');
process.exit(1);
}
const HOSTS = ['chipnet.bch.ninja', 'chipnet.imaginary.cash'];
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function rpc(client, method, ...params) {
try {
const res = await client.request(method, ...params);
if (res instanceof Error) return { ok: false, error: res.message ?? String(res) };
return { ok: true, value: res };
} catch (err) {
return { ok: false, error: err?.message ?? String(err) };
}
}
async function main() {
const clients = [];
for (const h of HOSTS) {
const c = new ElectrumClient('Deviant-DSrecheck', '1.4.1', h, { disableBrowserVisibilityHandling: true });
await c.connect();
clients.push([h, c]);
}
console.log(`Polling up to ${POLL_SECONDS}s for the race to settle.`);
console.log(` txA2=${idA}`);
console.log(` txB2=${idB}`);
const deadline = Date.now() + POLL_SECONDS * 1000;
let result = null;
while (Date.now() < deadline) {
for (const [host, c] of clients) {
for (const [tag, id] of [['A2', idA], ['B2', idB]]) {
const g = await rpc(c, 'blockchain.transaction.get', id, true);
if (g.ok && g.value && typeof g.value === 'object' && (g.value.confirmations ?? 0) >= 1) {
result = { tag, id, host, conf: g.value.confirmations, block: g.value.blockhash };
}
}
}
if (result) break;
await sleep(20000);
process.stdout.write('.');
}
console.log('');
if (!result) {
console.log(`No block within ${POLL_SECONDS}s — race still pending. Re-run later.`);
} else {
const loserId = result.tag === 'A2' ? idB : idA;
const loserTag = result.tag === 'A2' ? 'B2' : 'A2';
// Probe the loser on both nodes.
let loserState = 'UNKNOWN to node (dropped from mempool)';
for (const [, c] of clients) {
const lg = await rpc(c, 'blockchain.transaction.get', loserId, true);
if (lg.ok && lg.value && typeof lg.value === 'object') {
loserState = `${lg.value.confirmations ?? 0} conf`;
}
}
console.log('==================== RACE SETTLED ====================');
console.log(` WINNER : tx${result.tag} ${result.id}`);
console.log(` ${result.conf} conf, block ${result.block ?? '(n/a)'} (seen on ${result.host})`);
console.log(` LOSER : tx${loserTag} ${loserId}`);
console.log(` ${loserState}`);
console.log('');
if (String(loserState).startsWith('UNKNOWN')) {
console.log(' ✅ Exactly ONE confirmed; the conflicting double-spend was DROPPED.');
console.log(' 0-conf double-spend FAILED — first-seen held across the two-node race.');
} else {
console.log(' ⚠️ Loser still present — inspect manually (unexpected).');
}
console.log('=====================================================');
}
for (const [, c] of clients) await c.disconnect();
process.exit(0);
}
main().catch((e) => { console.error('FAILED:', e?.message ?? e); process.exit(1); });