diff options
Diffstat (limited to 'tools/ark')
| -rw-r--r-- | tools/ark/custody-test.js | 21 | ||||
| -rw-r--r-- | tools/ark/refresh-test.js | 9 |
2 files changed, 29 insertions, 1 deletions
diff --git a/tools/ark/custody-test.js b/tools/ark/custody-test.js index 214ab45..68e7191 100644 --- a/tools/ark/custody-test.js +++ b/tools/ark/custody-test.js @@ -127,4 +127,25 @@ const coin = (id, amountSat, expiryHeight, extra = {}) => ({ id, amountSat, expi console.log('✓ deep coins: no base fee for a refresh of deep coins alone, a capped coin is never spent, the worker orders them flattened'); } +// Lightning sends: a wallet that renews its own coins spends the nearest +// expiry first; the custodial default keeps the smallest coin that covers it +{ + const m = mk(true); + m.info.lnSendFees = { baseFeeSat: 0, minFeeSat: 0, ppmExpiryTable: [] }; + m.state.vtxos = [coin('young', 3000, 2000), coin('old', 9000, 1100), coin('older', 2000, 1050), coin('past', 8000, 900)]; + const ids = (amt) => m._selectLnInputs(m.state.vtxos, 1000, amt, 0).inputs.map((v) => v.id); + assert.deepEqual(ids(2500), ['young'], 'custodial: the smallest coin that covers it'); + assert.deepEqual(ids(12000), ['old', 'young'], 'custodial: largest first'); + m.lnOldestFirst = () => true; + assert.deepEqual(ids(1500), ['older'], 'self: the nearest expiry that covers it'); + assert.deepEqual(ids(2500), ['old'], '…skipping one too small'); + assert.deepEqual(ids(10000), ['older', 'old'], 'self: gathers nearest expiry first'); + assert.deepEqual(ids(13000), ['older', 'old', 'young'], 'coins inside their timelock carry it alone when they can'); + // a pile of old crumbs that would breach the input cap: largest-first instead + m.state.vtxos = [...Array.from({ length: MAX_SEND_INPUTS + 2 }, (_, i) => coin('crumb' + i, 100, 1010)), coin('big', 6000, 3000)]; + assert.deepEqual(ids(6050).slice(0, 1), ['big'], 'too many old crumbs for one payment: largest first'); + assert.equal(ids(6050).length, 2); + console.log('✓ Lightning sends: nearest expiry first when renewing your own coins, smallest-that-covers otherwise'); +} + console.log('\n✅ custody tiers behave'); diff --git a/tools/ark/refresh-test.js b/tools/ark/refresh-test.js index 0557659..6938daf 100644 --- a/tools/ark/refresh-test.js +++ b/tools/ark/refresh-test.js @@ -29,6 +29,7 @@ import { import { ALICE, aliceReady } from './alice.js'; const ARK = process.env.ARK_URL || 'http://127.0.0.1:3535'; +const ESPLORA = process.env.ESPLORA_URL || 'http://127.0.0.1:30002'; const BCLI = `docker exec bc bitcoin-cli -rpcwallet=coinos`; const FUND_SAT = 12345; const SPEND_SAT = 3000; @@ -86,7 +87,13 @@ if (!status?.fundingTx) { console.error('round never executed'); process.exit(1) const fundingTx = parseTx(status.fundingTx); console.log(`round executed: funding tx ${fundingTx.txid}, ${status.outputVtxos.length} output vtxo(s)`); execSync(`${BCLI} generatetoaddress 2 $(${BCLI} getnewaddress) >/dev/null`, { shell: '/bin/bash' }); -await sleep(2000); +// alice's bark checks the round anchor against esplora, which indexes a block +// ~5 s after it's mined; hand her the coin before then and she drops it +for (let i = 0; i < 30; i++) { + const st = await fetch(`${ESPLORA}/tx/${fundingTx.txid}/status`).then((r) => r.ok && r.json()).catch(() => null); + if (st?.confirmed) break; + await sleep(1000); +} const newVtxo = decodeVtxo(status.outputVtxos[0]); console.log(`new vtxo: ${newVtxo.id} (${newVtxo.amountSat} sat, expiry ${newVtxo.expiryHeight}, transitions: ${newVtxo.genesis.map((g) => g.transition.type).join(' -> ')})`); |
