deviant/scripts/live-noble-send.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

71 lines
3.1 KiB
JavaScript

/**
* LIVE chipnet proof for the noble crypto shim (ADR-0023).
*
* Run with the crypto-shim loader so EVERY libauth crypto call is served by
* pure-JS noble, with no WASM:
*
* node --import tsx --import ./scripts/crypto-shim-register.mjs \
* scripts/live-noble-send.mjs
*
* It exercises the EXACT signing path src/wallet/shell-runtime.ts uses:
* cashscript's `TransactionBuilder` + `SignatureTemplate(priv, _, ECDSA)`
* `.unlockP2PKH()`. Under the shim, the P2PKH sighash (double-SHA-256) and the
* ECDSA signature are produced entirely by noble. It self-sends a small amount
* of plain BCH (A → A) and confirms the tx lands on chain by re-querying it.
*/
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { TestNetWallet } from 'mainnet-js';
import { ElectrumNetworkProvider, TransactionBuilder, SignatureTemplate, SignatureAlgorithm } from 'cashscript';
const ROOT = join(import.meta.dirname, '..');
if (!globalThis.__DEVIANT_NOBLE_SHIM_ACTIVE__) {
throw new Error(
'Crypto shim is NOT active — run with `--import ./scripts/crypto-shim-register.mjs`. ' +
'Refusing to claim a shim-backed broadcast without the shim wired in.',
);
}
console.log(`✓ noble crypto shim active (libauth WASM bypassed; marker=${globalThis.__DEVIANT_NOBLE_SHIM_ACTIVE__})`);
const SEND_SATS = BigInt(process.env.SATS ?? '5000');
const FEE = 300n;
async function main() {
const { wif } = JSON.parse(readFileSync(join(ROOT, '.chipnet-keys.json'), 'utf8'));
const sender = await TestNetWallet.fromWIF(wif);
const provider = new ElectrumNetworkProvider('chipnet');
// Pick a plain (non-token) UTXO with room for the send + fee.
const utxos = await provider.getUtxos(sender.cashaddr);
const plain = utxos
.filter((u) => !u.token && u.satoshis >= SEND_SATS + FEE)
.sort((a, b) => Number(a.satoshis - b.satoshis));
const input = plain[0];
if (!input) throw new Error('No spendable plain BCH UTXO found.');
const change = input.satoshis - SEND_SATS - FEE;
console.log(`Input ${input.txid.slice(0, 12)}…:${input.vout} = ${input.satoshis} sats`);
console.log(`Self-send ${SEND_SATS} sats (+${change} change) to ${sender.cashaddr}, fee ${FEE}`);
// The exact construction shell-runtime.ts uses: ECDSA P2PKH unlocker.
const template = new SignatureTemplate(sender.privateKey, undefined, SignatureAlgorithm.ECDSA);
const tb = new TransactionBuilder({ provider });
tb.addInput(input, template.unlockP2PKH());
tb.addOutput({ to: sender.cashaddr, amount: SEND_SATS });
if (change > 546n) tb.addOutput({ to: sender.cashaddr, amount: change });
const sent = await tb.send();
const txid = typeof sent === 'string' ? sent : (sent.txid ?? sent.txId);
console.log(`\nBroadcast TXID: ${txid}`);
const raw = await provider.getRawTransaction(txid);
console.log(`On-chain confirmation: getRawTransaction returned ${raw.length / 2} bytes for ${txid}`);
console.log('✓ chipnet accepted a P2PKH tx whose ECDSA signature + sighash were produced by the noble shim.');
}
main().catch((err) => {
console.error('FAILED:', err?.message ?? err);
if (err?.stack) console.error(err.stack);
process.exit(1);
});