aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-08 13:48:00 -0700
committerAdam Soltys <[email protected]>2026-10-08 13:48:00 -0700
commit175e513273bfb2a362f9a08e3da5d5460a1d91c0 (patch)
tree1220829278076ee110fc4e81db124590674c0a34 /tools
parent6206eaa76431d0bb09ffa88abb3f02b8976a9ed3 (diff)
downloadcoinosv3-175e513273bfb2a362f9a08e3da5d5460a1d91c0.tar.gz
coinosv3-175e513273bfb2a362f9a08e3da5d5460a1d91c0.zip
Coins in another device's renewal are busy here too: the renewal sliver names its inputs, and ArkManager._free / freeCoins exclude coins an in-flight remote renewal holds from balance (counted pending), send/LN/offboard/exit selection, auto-renew, the backstop and the coins page (greyed with the round). Freed the moment that renewal reads finished, failed or lapsed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'tools')
-rw-r--r--tools/ark-renewal-sync-test.js21
1 files changed, 20 insertions, 1 deletions
diff --git a/tools/ark-renewal-sync-test.js b/tools/ark-renewal-sync-test.js
index af31cde..b29f832 100644
--- a/tools/ark-renewal-sync-test.js
+++ b/tools/ark-renewal-sync-test.js
@@ -4,6 +4,7 @@
// Run: bun tools/ark-renewal-sync-test.js
import assert from 'node:assert/strict';
import { slimArkForSync, mergeArkStates, remoteRenewalsInFlight } from '../src/features/ark.js';
+import { ArkManager } from '../src/ark/manager.js';
const now = Date.now();
const laptop = (step, extra = {}) => ({
@@ -16,7 +17,8 @@ let phone = { serverPubkey: 'srv', vtxos: [], movements: [], actions: [{ id: 'ln
const snap = slimArkForSync(laptop('submitted'));
assert.equal(snap.renewals.length, 1);
-assert.ok(!('fundingTxHex' in snap.renewals[0]) && !('outputVtxos' in snap.renewals[0]) && !('inputIds' in snap.renewals[0]), 'display fields only');
+assert.ok(!('fundingTxHex' in snap.renewals[0]) && !('outputVtxos' in snap.renewals[0]), 'no signing material');
+assert.deepEqual(snap.renewals[0].inputIds, ['a:0'], 'names the coins it holds');
phone = mergeArkStates(phone, snap);
assert.equal(phone.actions.length, 1, 'never adopted as an action of this device');
assert.equal(remoteRenewalsInFlight(phone).length, 1);
@@ -50,4 +52,21 @@ console.log('✓ Finished there, it is gone here');
assert.equal(mergeArkStates(phone, slimArkForSync(old)).remoteRenewals.filter((r) => r.id === old.actions[0].id).length, 0);
console.log('✓ The device that owns it never sees itself as remote; a renewal begun days ago is dropped');
}
+{
+ // the phone holds the laptop's renewal coin as spendable in its own records
+ const coin = (id, amountSat) => ({ id, amountSat, state: 'spendable', expiryHeight: 999_999, bytes: 'AA==' });
+ let st = { serverPubkey: 'srv', vtxos: [coin('a:0', 62337), coin('z:0', 1000)], movements: [], actions: [] };
+ st = mergeArkStates(st, slimArkForSync(laptop('submitted')));
+ const mgr = new ArkManager({ storage: { save() {} }, arkUrl: 'http://test.invalid' });
+ mgr.state = st; mgr._tipH = 1000;
+ assert.deepEqual(mgr.balance(), { spendableSat: 1000, pendingSat: 62337, boardingSat: 0, expiredSat: 0 });
+ assert.deepEqual(mgr._selectInputs(500).map((v) => v.id), ['z:0']);
+ assert.throws(() => mgr._selectInputs(2000));
+ await assert.rejects(mgr.refresh(['a:0']), /no spendable/);
+ console.log('✓ Its coins count as pending here and are never offered for a send or a renewal');
+ st = mergeArkStates(st, slimArkForSync(laptop('failed')));
+ mgr.state = st;
+ assert.equal(mgr.balance().spendableSat, 63337, 'a renewal that failed there frees them here');
+ console.log('✓ Failed there, its coins are free again here');
+}
console.log('\n✅ renewals sync across devices for display');