aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-04 09:10:04 -0700
committerAdam Soltys <[email protected]>2026-07-04 09:10:04 -0700
commit11b2a56a320bbd66b98991fd08478d154ff6c717 (patch)
tree035e64613eda8ea99cee6c7d7de352ed6bbdf2fe /tools
parent5fb48ee5284dbba34c8fa3b77c36379f49404ac2 (diff)
downloadcoinosv3-11b2a56a320bbd66b98991fd08478d154ff6c717.tar.gz
coinosv3-11b2a56a320bbd66b98991fd08478d154ff6c717.zip
Build profiles: HAL_FEATURES selects which features ship
build.js swaps src/features/index.js for a generated module importing only the enabled features (gifts,swaps,ark,sp — unset means all, "none" means a plain on-chain wallet), so excluded feature code and its network endpoints never enter the bundle. Minimal profile: 476KB vs 577KB, with the Ark/gRPC, Boltz, and MuSig2 code verifiably absent. `bun run build:minimal` builds it. tools/minimal-smoke.js proves the minimal profile end-to-end: no receive-mode dropdown, no gift button, no swap/Ark/SP settings cards, no page errors, and a plain on-chain receive still lands. The two blessed profiles are full (everything, the existing e2e suites) and minimal (this smoke test); other combos should work but aren't CI'd. Gift and SP primitives still live inside wallet.js, so those bytes remain in the minimal bundle for now — extracting them is the next step if we want the core leaner still. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
Diffstat (limited to 'tools')
-rw-r--r--tools/minimal-smoke.js99
1 files changed, 99 insertions, 0 deletions
diff --git a/tools/minimal-smoke.js b/tools/minimal-smoke.js
new file mode 100644
index 0000000..ab31619
--- /dev/null
+++ b/tools/minimal-smoke.js
@@ -0,0 +1,99 @@
+// Smoke test for the minimal build profile (HAL_FEATURES=none): a plain
+// on-chain wallet with no swap/Ark/gift/SP surfaces — and still fully working
+// (imports a wallet, receives a payment) against the regtest stack.
+//
+// Usage: bun tools/minimal-smoke.js
+
+import { execSync } from 'node:child_process';
+import puppeteer from 'puppeteer-core';
+import { generateMnemonic } from '@scure/bip39';
+import { wordlist } from '@scure/bip39/wordlists/english';
+import { buildHtml } from '../build.js';
+
+const BCLI = `bitcoin-cli -regtest -datadir=${process.env.HOME}/ark-regtest/bitcoind -rpcport=18543 -rpcwallet=miner`;
+const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
+const sh = (cmd) => execSync(cmd, { shell: '/bin/bash' }).toString().trim();
+
+let ok = true;
+const check = (name, cond, detail = '') => {
+ console.log(` ${cond ? '✓' : '✗'} ${name}${detail ? ' — ' + detail : ''}`);
+ if (!cond) ok = false;
+};
+
+// --- build the minimal profile and serve it ---
+const html = await buildHtml({ minify: true, pwa: false, features: 'none' });
+const full = await buildHtml({ minify: true, pwa: false });
+console.log(`minimal bundle: ${(html.length / 1024).toFixed(0)}KB (full: ${(full.length / 1024).toFixed(0)}KB)`);
+check('meaningfully smaller than full', html.length < full.length * 0.9);
+check('no ark protocol code', !html.includes('Ark VTXO mailbox authorization'));
+check('no swap protocol code', !html.includes('swap/reverse'));
+check('no musig code', !html.includes('MuSig/noncecoef'));
+
+const server = Bun.serve({ port: 5198, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) });
+
+const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: 'new', args: ['--no-sandbox'] });
+const page = await browser.newPage();
+const errs = [];
+page.on('pageerror', (e) => errs.push(e.message));
+const bodyText = () => page.evaluate(() => document.body.innerText);
+const waitText = async (text, ms = 20000) => {
+ for (let i = 0; i < ms / 250; i++) {
+ if ((await bodyText()).toLowerCase().includes(text.toLowerCase())) return true;
+ await sleep(250);
+ }
+ return false;
+};
+const clickText = (sel, text) => page.evaluate((s, x) => {
+ const el = [...document.querySelectorAll(s)].find((e) => e.textContent.trim().toLowerCase().includes(x.toLowerCase()));
+ if (el) { el.click(); return true; }
+ return false;
+}, sel, text);
+
+try {
+ await page.goto('http://localhost:5198', { waitUntil: 'domcontentloaded' });
+ await page.evaluate(() => {
+ localStorage.setItem('btc-wallet-network', 'regtest');
+ localStorage.setItem('btc-wallet-source:regtest', JSON.stringify({ id: 'custom', url: 'http://localhost:30002' }));
+ localStorage.setItem('btc-wallet-ark-provider:regtest', 'local'); // must be ignored by this build
+ });
+ await page.reload({ waitUntil: 'domcontentloaded' });
+ await sleep(400);
+
+ console.log('\n[1] import + core wallet');
+ await clickText('button', 'Import existing');
+ await sleep(200);
+ await page.type('textarea', generateMnemonic(wordlist));
+ await clickText('button', 'Open wallet');
+ check('wallet opened', await waitText('receive', 15000));
+
+ console.log('\n[2] no feature surfaces');
+ await sleep(2500); // give any (wrongly present) feature time to appear
+ check('no receive-mode dropdown', await page.evaluate(() =>
+ ![...document.querySelectorAll('option')].some((o) => ['ln', 'ark', 'sp'].includes(o.value))));
+ await clickText('.tabs button', 'Send');
+ await sleep(300);
+ check('no gift button', !(await bodyText()).includes('Gift some Bitcoin'));
+ await clickText('.tabs button', 'Settings');
+ await sleep(300);
+ const settings = await bodyText();
+ check('no swap provider card', !settings.includes('Swap provider'));
+ check('no ark card', !/⚔ Ark/.test(settings));
+ check('no sp indexer card', !settings.includes('Silent payment indexer'));
+
+ console.log('\n[3] core receive still works');
+ await clickText('.tabs button', 'Receive');
+ await sleep(300);
+ const addr = await page.evaluate(() => document.querySelector('.addr-box')?.textContent.trim());
+ check('onchain address shown', /^bcrt1/.test(addr || ''), (addr || '').slice(0, 16) + '…');
+ sh(`${BCLI} sendtoaddress ${addr} 0.0004`);
+ sh(`${BCLI} generatetoaddress 1 $(${BCLI} getnewaddress) >/dev/null`);
+ check('payment lands', await waitText('40,000', 30000));
+ check('no page errors', errs.length === 0, errs.join(' | '));
+
+ console.log(ok ? '\n✅ SUCCESS: minimal profile is a working on-chain-only wallet'
+ : '\n❌ some checks failed');
+} finally {
+ await browser.close();
+ server.stop(true);
+}
+process.exit(ok ? 0 : 1);