aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/ark/custody-test.js34
1 files changed, 34 insertions, 0 deletions
diff --git a/tools/ark/custody-test.js b/tools/ark/custody-test.js
index f8106b7..214ab45 100644
--- a/tools/ark/custody-test.js
+++ b/tools/ark/custody-test.js
@@ -93,4 +93,38 @@ const coin = (id, amountSat, expiryHeight, extra = {}) => ({ id, amountSat, expi
await assert.rejects(validateVtxo(v, { serverPubkey: serverPub, chain, acceptExpired: true }), (e) => e instanceof VtxoValidationError && /not confirmed/.test(e.message), 'past the expiry check, on to the anchor');
console.log('✓ validateVtxo: acceptExpired lets a coin past its timelock through the expiry check');
}
+
+// deep coins: the server's depth cap and its base-fee waiver, mirrored
+{
+ const m = mk(true);
+ m.info.refreshFees.deepWaiverDepth = 60;
+ m.info.maxVtxoExitDepth = 100;
+ m.exitDepth = (v) => v.depth || 0;
+ m._tipMemo = async () => 1000;
+ const shallow = coin('s', 5000, 2000, { depth: 7 }), deep = coin('d', 4000, 2000, { depth: 60 });
+ const capped = coin('x', 3000, 2000, { depth: 100 }), near = coin('n', 2500, 2000, { depth: 92 });
+ const expiredDeep = coin('e', 6000, 900, { depth: 70 });
+ m.state.vtxos = [shallow, deep, capped, near, expiredDeep];
+ assert.equal(m.refreshFee([deep], 1000), 0, 'a refresh of deep coins alone pays no base fee');
+ assert.equal(m.refreshFee([deep, expiredDeep], 1000), 0);
+ assert.equal(m.refreshFee([deep, shallow], 1000), 300, '…one shallow coin in the set and it pays');
+ assert.equal(m.refreshFee([shallow], 1000), 300);
+ assert.deepEqual(m.deepCoins().map((v) => v.id).sort(), ['d', 'e', 'n', 'x'], 'deep = at the waiver depth or within 8 hops of the cap, expired or not');
+ assert.deepEqual(m._selectInputs(2900).map((v) => v.id), ['d'], 'a coin at the cap is never an input (the cosign would be refused)');
+ m.state.vtxos = [capped];
+ assert.throws(() => m._selectInputs(1000), /waiting to be renewed/, 'and the shortfall says why');
+ m.state.vtxos = [shallow, deep, capped, near, expiredDeep];
+ // the worker's orders: one per deep coin still inside its timelock, two blocks out
+ const orders = [];
+ m.scheduleRefresh = async (ids, h) => { orders.push([ids.join(), h]); (m.state.scheduled ||= []).push({ inputIds: ids, unlockHash: 'u' + ids }); };
+ assert.equal(await m.ensureDeepRefreshes(3), 3);
+ assert.deepEqual(orders, [['x', 1002], ['n', 1002], ['d', 1002]], 'deepest first, at tip + 2; the expired one waits for the app');
+ assert.equal(await m.ensureDeepRefreshes(3), 0, 'covered coins are not ordered twice');
+ // no waiver and no cap advertised: nothing is deep
+ const plain = mk(true); plain.exitDepth = () => 999; plain.state.vtxos = [coin('p', 5000, 2000)];
+ assert.deepEqual(plain.deepCoins(), []);
+ assert.equal(plain.refreshFee(plain.state.vtxos, 1000), 300, 'no waiver advertised: the base fee stands however deep');
+ 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');
+}
+
console.log('\n✅ custody tiers behave');