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());