aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ark
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ark')
-rw-r--r--tools/ark/custody-test.js14
-rw-r--r--tools/ark/custody-ui-test.js25
2 files changed, 22 insertions, 17 deletions
diff --git a/tools/ark/custody-test.js b/tools/ark/custody-test.js
index da1079c..f8106b7 100644
--- a/tools/ark/custody-test.js
+++ b/tools/ark/custody-test.js
@@ -9,18 +9,16 @@ import { ArkManager, renewPolicy, CUSTODY_TIERS, MAX_SEND_INPUTS } from '../../s
import { validateVtxo, VtxoValidationError } from '../../src/ark/validate.js';
// policies
-assert.deepEqual(CUSTODY_TIERS, ['custodial', 'self', 'early']);
+assert.deepEqual(CUSTODY_TIERS, ['custodial', 'self', 'exit']);
assert.equal(renewPolicy('custodial', 'mainnet'), null, 'the default tier renews nothing');
assert.equal(renewPolicy('nonsense', 'mainnet'), null);
assert.deepEqual(renewPolicy('self', 'mainnet'), { window: 96, margin: 36, grid: 48 });
-assert.deepEqual(renewPolicy('early', 'mainnet'), { window: 1008, margin: 1008, grid: 144 });
+assert.deepEqual(renewPolicy('exit', 'mainnet'), { window: 96, margin: 36, grid: 48, maxDepth: 4 }, 'exit = self + a depth trigger');
+assert.deepEqual(renewPolicy('early', 'mainnet'), renewPolicy('exit', 'mainnet'), 'the old name still means the exit tier');
assert.deepEqual(renewPolicy('self', 'regtest'), { window: 24, margin: 6, grid: 1 });
-// the snap never leaves the bracket: self stays under 96 blocks out, early under 2160
-for (let h = 100000; h < 100200; h++) {
- const s = h - 36; assert.ok(h - (s - (s % 48)) < 96);
- const e = h - 1008; assert.ok(h - (e - (e % 144)) < 2160 && h - (e - (e % 144)) >= 199);
-}
-console.log('✓ renewal policies: custodial renews nothing; self and early aim inside one fee bracket');
+// the snap never leaves the free bracket: under 96 blocks out
+for (let h = 100000; h < 100200; h++) { const s = h - 36; assert.ok(h - (s - (s % 48)) < 96); }
+console.log('✓ renewal policies: custodial renews nothing; self and exit aim inside the free bracket');
const mk = (honorsExpired) => {
const mgr = new ArkManager({ account: null, storage: { load: () => null, save() {} }, arkUrl: 'http://x', esploraUrl: 'http://y', network: 'mainnet', honorsExpired });
diff --git a/tools/ark/custody-ui-test.js b/tools/ark/custody-ui-test.js
index d85cd36..20035ac 100644
--- a/tools/ark/custody-ui-test.js
+++ b/tools/ark/custody-ui-test.js
@@ -48,14 +48,15 @@ try {
window.coopManager = {
honorsExpired: true,
state: { vtxos: [
- { id: 'coin-0', amountSat: 20000, state: 'spendable', expiryHeight: 973921 }, // 29 days left
- { id: 'coin-1', amountSat: 5000, state: 'spendable', expiryHeight: 960000 }, // past its timelock
+ { id: 'coin-0', amountSat: 20000, state: 'spendable', expiryHeight: 973921, depth: 5 }, // 29 days left, five hops deep
+ { id: 'coin-1', amountSat: 5000, state: 'spendable', expiryHeight: 960000, depth: 1 }, // past its timelock
+ { id: 'coin-2', amountSat: 800, state: 'spendable', expiryHeight: 973921, depth: 6 }, // deep but not worth 300 sats
], actions: [], movements: [], scheduled: [] },
info: { network: 'regtest', vtxoExpiryDelta: 8640, roundIntervalSecs: 120, refreshFees: { baseFeeSat: 300, ppmExpiryTable: table } }, _tipH: 969751,
chain: { tipHeight: async () => 969751 },
vtxos() { return this.state.vtxos; },
- _decoded() { throw new Error('no exit transaction fixture'); },
- balance() { return { spendableSat: 25000, pendingSat: 0, boardingSat: 0, expiredSat: 0 }; },
+ _decoded(v) { return { genesis: new Array(v.depth || 1).fill({}), policy: { type: 'pubkey' } }; },
+ balance() { return { spendableSat: 25800, pendingSat: 0, boardingSat: 0, expiredSat: 0 }; },
refreshFee(inputs, tip) {
let u = 0;
for (const v of inputs) u += v.amountSat * (table.filter((e) => e.thresholdBlocks <= v.expiryHeight - tip).pop()?.ppm ?? 0);
@@ -78,7 +79,8 @@ try {
assert.match(tx, /Custody/);
assert.match(tx, /coinos holds my coins[\s\S]*no fees/);
assert.match(tx, /Keep my own exit[\s\S]*300 sats per renewal/);
- assert.match(tx, /Renew a week early[\s\S]*0\.1% \+ 300 sats per renewal/);
+ assert.match(tx, /Keep my exit cheap[\s\S]*300 sats near expiry, 0\.2% \+ 300 sats when early/);
+ assert.match(tx, /4 hops deep/);
const checked = await page.$$eval('.ark-custody input[type=radio]', (els) => els.filter((e) => e.checked).map((e) => e.value));
assert.deepEqual(checked, ['custodial'], 'coinos holds the coins by default');
assert.equal(await page.evaluate(() => coopFeature.getCustody()), 'custodial');
@@ -94,7 +96,7 @@ try {
assert.deepEqual(exitBtn, [false]);
await page.evaluate(() => [...document.querySelectorAll('button')].find((b) => b.textContent === 'Exit now').click()); await sleep(200);
tx = await text();
- assert.match(tx, /Coins\s*1/); assert.match(tx, /Amount\s*20,000 sats/);
+ assert.match(tx, /Coins\s*2/); assert.match(tx, /Amount\s*20,800 sats/);
await page.evaluate(() => [...document.querySelectorAll('button')].find((b) => b.textContent === 'Cancel').click()); await sleep(200);
console.log('✓ a unilateral exit counts only the coin still inside its timelock');
@@ -108,11 +110,16 @@ try {
assert.match(tx, /renews coins automatically once they are close to expiring/);
const calls = await page.evaluate(() => coopFeature.autoRefreshCalls);
assert.deepEqual(calls[0], ['ensure', 6, 8, 1], 'backstop orders registered at once (regtest margin 6)');
- assert.deepEqual(calls[1], ['refresh', ['coin-1']], 'the coin past its timelock is renewed now; the healthy one is left alone');
+ assert.deepEqual(calls[1], ['refresh', ['coin-1', 'coin-2']], 'the coin past its timelock is renewed now with the small change; the deep healthy one is left alone');
console.log('✓ switching to "Keep my own exit" persists, reprices the page and starts renewing');
- await page.click('.ark-custody-opt[data-tier=early] input'); await sleep(300);
- assert.equal(await page.evaluate(() => coopFeature.getCustody()), 'early');
+ // the exit tier also flattens the deep coin — but not the deep 800-sat one on its own merits
+ await page.evaluate(() => { coopFeature.autoRefreshCalls.length = 0; });
+ await page.click('.ark-custody-opt[data-tier=exit] input'); await sleep(400);
+ assert.equal(await page.evaluate(() => coopFeature.getCustody()), 'exit');
+ const calls2 = await page.evaluate(() => coopFeature.autoRefreshCalls);
+ assert.deepEqual(calls2[1], ['refresh', ['coin-1', 'coin-0', 'coin-2']], 'expired + deep (worth it) + small change ride one round');
+ console.log('✓ "Keep my exit cheap" renews the deep coin too, when the fee is under a tenth of it');
await page.click('.ark-custody-opt[data-tier=custodial] input'); await sleep(300);
assert.equal(await page.evaluate(() => coopFeature.getCustody()), 'custodial');
assert.match(await text(), /nothing renews on its own/);