From e526b8599c8c4394f7f6e4b951185ab3f3cf9911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 4 Aug 2026 00:20:36 +0000 Subject: [PATCH] stop using a real account xpub as a test fixture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the key in chain-source-esplora.test.ts was labelled a published test vector and was not one — it was the owner's live bip84 account xpub, pulled from the database during an earlier verification and pasted in. it cannot spend, but it discloses every address that wallet will ever use and its whole history, permanently. replaced with the bip84 spec's own vector, derived in the file from the published mnemonic so its provenance can be checked rather than taken on trust, and pinned by an assertion against the spec's first address so a future substitution fails loudly. this does not remove the key from history. that needs a rewrite of 5c38236. Co-Authored-By: Claude Opus 5 --- check2.tmp.ts | 20 ++++++++++++++++++ .../wallet/chain-source-esplora.test.ts | 21 ++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 check2.tmp.ts diff --git a/check2.tmp.ts b/check2.tmp.ts new file mode 100644 index 00000000..f7c962d5 --- /dev/null +++ b/check2.tmp.ts @@ -0,0 +1,20 @@ +const lines = (await Bun.file('/tmp/addrs2.tsv').text()).trim().split('\n'); +let hits = 0, totalBal = 0; +for (const line of lines) { + const [type, path, addr] = line.split('\t'); + const proc = Bun.spawn(['curl', '-4', '-s', '--max-time', '12', `https://mempool.space/api/address/${addr}`], { stdout: 'pipe', stderr: 'ignore' }); + const body = await new Response(proc.stdout).text(); + await proc.exited; + try { + const j = JSON.parse(body); const c = j.chain_stats, m = j.mempool_stats; + const tx = (c?.tx_count ?? 0) + (m?.tx_count ?? 0); + if (tx > 0) { + const bal = (c.funded_txo_sum - c.spent_txo_sum) + (m.funded_txo_sum - m.spent_txo_sum); + totalBal += bal; hits++; + console.log(`${type} ${path} ${addr} txs=${tx} balance=${bal}`); + } + } catch {} + await Bun.sleep(70); +} +console.log(`\nindices20-59: addressesWithHistory=${hits} unspentTotal=${totalBal} sats`); +process.exit(0); diff --git a/src/servers/sidecar/wallet/chain-source-esplora.test.ts b/src/servers/sidecar/wallet/chain-source-esplora.test.ts index e67a18f3..69ecaa07 100644 --- a/src/servers/sidecar/wallet/chain-source-esplora.test.ts +++ b/src/servers/sidecar/wallet/chain-source-esplora.test.ts @@ -7,6 +7,7 @@ import { describe, expect, test } from 'bun:test'; import { HDKey } from '@scure/bip32'; +import { mnemonicToSeedSync } from '@scure/bip39'; import * as bitcoin from 'bitcoinjs-lib'; import type { EsploraAddress, EsploraChain, EsploraTx, EsploraUtxo } from './chain'; import { EsploraChainSource } from './chain-source-esplora'; @@ -15,9 +16,17 @@ import { initEcc } from './psbt'; initEcc(); -// A published BIP32 test vector, so the addresses below are reproducible by anything that speaks BIP84. -const ACCOUNT_XPUB = - 'xpub6DQv646WnF2m1tkyhhwu74tes1MunPX8psKwjmXSmWcPExp7FR9XP2q6m8VcK9uZMQE3rDmPnTnEDTHSxcmad7wzBvLk8PXC7Gnxe5GUpUq'; +// NEVER PUT A REAL ACCOUNT KEY IN A TEST. An xpub cannot spend, but it reveals every address a wallet +// will ever use and its entire history, permanently and irrevocably — and a test file is the easiest +// place in a repo for one to hide, because it looks like a fixture. This one is derived here, in the +// open, from the BIP84 specification's own published mnemonic, so its provenance is checkable rather +// than asserted. If you need a different fixture, derive it the same way; do not paste one in. +const PUBLIC_TEST_MNEMONIC = + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + +const ACCOUNT_XPUB = HDKey.fromMasterSeed(mnemonicToSeedSync(PUBLIC_TEST_MNEMONIC)).derive( + "m/84'/0'/0'", +).publicExtendedKey; /** Derive the same p2wpkh address the backend will, independently of the backend. */ function addressAt(chain: 0 | 1, index: number): string { @@ -141,6 +150,12 @@ function backendFor(world: Map) { } describe('EsploraChainSource through OnchainBackend', () => { + test('the fixture key really is the published BIP84 vector, not somebody’s wallet', () => { + // The spec's own first receive address. If this fails, the mnemonic above is not what it claims and + // whatever replaced it must be treated as live key material until proven otherwise. + expect(addressAt(0, 0)).toBe('bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu'); + }); + test('an empty wallet costs one gap-limit window per chain and reports zero', async () => { const { backend, stub } = backendFor(new Map());