aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-08 13:44:24 -0700
committerAdam Soltys <[email protected]>2026-10-08 13:44:24 -0700
commit6206eaa76431d0bb09ffa88abb3f02b8976a9ed3 (patch)
tree296fb5774810002fa27e795268a2f418726736a3 /tools
parentfcd023fb4b33404eb62afceba0c0232f8b25f200 (diff)
downloadcoinosv3-6206eaa76431d0bb09ffa88abb3f02b8976a9ed3.tar.gz
coinosv3-6206eaa76431d0bb09ffa88abb3f02b8976a9ed3.zip
Renewals show on every device: the sync snapshot carries each device's own renewals as display-only slivers (step, amounts, fee, round txid; no signing material), merged into remoteRenewals furthest-step-wins, never as actions; the coins page lists another device's renewal in progress with its round transaction until it finishes there or a 'Renewed' row syncs (two-day cap)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'tools')
-rw-r--r--tools/ark-renewal-sync-test.js53
1 files changed, 53 insertions, 0 deletions
diff --git a/tools/ark-renewal-sync-test.js b/tools/ark-renewal-sync-test.js
new file mode 100644
index 0000000..af31cde
--- /dev/null
+++ b/tools/ark-renewal-sync-test.js
@@ -0,0 +1,53 @@
+// A renewal started on one device shows on the others as in progress (a phone
+// knew nothing while the laptop's round ran and the balance sat locked), is
+// never adopted as an action there, and disappears once it finishes.
+// Run: bun tools/ark-renewal-sync-test.js
+import assert from 'node:assert/strict';
+import { slimArkForSync, mergeArkStates, remoteRenewalsInFlight } from '../src/features/ark.js';
+
+const now = Date.now();
+const laptop = (step, extra = {}) => ({
+ serverPubkey: 'srv', vtxos: [], movements: [], actions: [{
+ id: `refresh-${now - 60_000}`, type: 'refresh', step, inputIds: ['a:0'], outKeyIndex: 9,
+ outAmountSat: 62212, feeSat: 125, unlockHash: 'c0'.repeat(32), fundingTxHex: 'deadbeef', outputVtxos: ['00'], ...extra,
+ }],
+});
+let phone = { serverPubkey: 'srv', vtxos: [], movements: [], actions: [{ id: 'ln-recv-1', type: 'ln-recv', step: 'awaiting' }] };
+
+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');
+phone = mergeArkStates(phone, snap);
+assert.equal(phone.actions.length, 1, 'never adopted as an action of this device');
+assert.equal(remoteRenewalsInFlight(phone).length, 1);
+assert.equal(remoteRenewalsInFlight(phone)[0].outAmountSat, 62212);
+assert.ok(!('renewals' in phone), 'the snapshot field is not stored as is');
+console.log('✓ The other device sees the renewal in progress, as display only');
+
+phone = mergeArkStates(phone, slimArkForSync(laptop('submitted', { fundingTxid: 'e9'.repeat(32) })));
+assert.equal(remoteRenewalsInFlight(phone)[0].fundingTxid, 'e9'.repeat(32));
+// an older snapshot (another slot, still at 'created') never moves it back
+phone = mergeArkStates(phone, slimArkForSync(laptop('created')));
+assert.equal(phone.remoteRenewals[0].step, 'submitted');
+console.log('✓ It learns the round transaction, and a staler snapshot never moves it back');
+
+phone = mergeArkStates(phone, slimArkForSync(laptop('done', { fundingTxid: 'e9'.repeat(32) })));
+assert.equal(remoteRenewalsInFlight(phone).length, 0);
+console.log('✓ Finished there, it is gone here');
+
+{
+ let p2 = mergeArkStates({ serverPubkey: 'srv', vtxos: [], movements: [], actions: [] }, slimArkForSync(laptop('issued')));
+ p2 = mergeArkStates(p2, { serverPubkey: 'srv', vtxos: [], actions: [], movements: [{ id: 'm', type: 'refresh', status: 'complete', unlockHash: 'c0'.repeat(32) }] });
+ assert.equal(remoteRenewalsInFlight(p2).length, 0);
+ console.log('✓ A synced "Renewed" row settles it even before the final telling arrives');
+}
+
+{
+ const own = laptop('submitted');
+ const merged = mergeArkStates(own, slimArkForSync(laptop('submitted')));
+ assert.equal((merged.remoteRenewals || []).length, 0, 'its own renewal never shows twice');
+ const old = laptop('submitted'); old.actions[0].id = `refresh-${now - 3 * 86_400_000}`;
+ 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');
+}
+console.log('\n✅ renewals sync across devices for display');