aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-10 07:09:45 -0700
committerAdam Soltys <[email protected]>2026-09-10 07:09:45 -0700
commit6113cf567fa0830233d5c3d524361f24a3f78caa (patch)
treef5ce3bf5e45602547d983797b57b94c8d7ae7dc5 /tools
parent435e9886620deff3d66286e3b85e97ed8f5fae53 (diff)
downloadcoinosv3-6113cf567fa0830233d5c3d524361f24a3f78caa.tar.gz
coinosv3-6113cf567fa0830233d5c3d524361f24a3f78caa.zip
Ark: persist vtxo bytes as base64 (a third smaller than hex); hex still read, hex still on the wire.
The live coins' signed bytes are most of a deep wallet's state — 560KB of hex, which localStorage then holds as UTF-16. vtxoBytesToStr/FromStr in proto.js persist base64 and read either form, _addVtxo writes base64, and pruneArkState converts hex to base64 on every save, so an existing state migrates itself the first time it saves. Measured on a live deep wallet: 904KB → 665KB (-26%), all 41 coins decoding identically. The sync wire (slimArkForSync) keeps sending hex for now: a device still on an older build would otherwise fail to decode every coin merged from a newer one. Flip it once pre-2026-09-10 builds are gone. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Nw1Dijik1tLVKRM1WkU7xE
Diffstat (limited to 'tools')
-rw-r--r--tools/ark-prune-test.js41
1 files changed, 30 insertions, 11 deletions
diff --git a/tools/ark-prune-test.js b/tools/ark-prune-test.js
index e12b02e..fcf0166 100644
--- a/tools/ark-prune-test.js
+++ b/tools/ark-prune-test.js
@@ -8,12 +8,12 @@ const DAY = 86400_000;
const now = 1_800_000_000_000;
const state = {
vtxos: [
- { id: 'live', state: 'spendable', bytes: 'aa' },
- { id: 'oldspent', state: 'spent', bytes: 'bb' }, // spent 40d ago → bytes go
- { id: 'freshspent', state: 'spent', bytes: 'cc' }, // spent yesterday → kept
- { id: 'inflight', state: 'spent', bytes: 'dd' }, // named by a live action → kept
- { id: 'unnamed', state: 'spent', bytes: 'ee' }, // no date known → kept
- { id: 'pending', state: 'pending', bytes: 'ff' },
+ { id: 'live', state: 'spendable', bytes: 'LIVE' },
+ { id: 'oldspent', state: 'spent', bytes: 'OLD=' }, // spent 40d ago → bytes go
+ { id: 'freshspent', state: 'spent', bytes: 'NEW=' }, // spent yesterday → kept
+ { id: 'inflight', state: 'spent', bytes: 'FLY=' }, // named by a live action → kept
+ { id: 'unnamed', state: 'spent', bytes: 'UNK=' }, // no date known → kept
+ { id: 'pending', state: 'pending', bytes: 'PND=' },
],
actions: [
{ id: `send-${now - 40 * DAY}`, type: 'send', step: 'done', inputIds: ['oldspent'], destBytesList: ['hex'], changeBytesList: ['hex'], amountSat: 5 },
@@ -27,12 +27,12 @@ const state = {
const before = JSON.stringify(state).length;
pruneArkState(state, now);
const v = (id) => state.vtxos.find((x) => x.id === id);
-assert.equal(v('live').bytes, 'aa');
-assert.equal(v('pending').bytes, 'ff');
+assert.equal(v('live').bytes, 'LIVE');
+assert.equal(v('pending').bytes, 'PND=');
assert.equal(v('oldspent').bytes, undefined, 'old spend sheds its bytes');
-assert.equal(v('freshspent').bytes, 'cc', 'a recent spend keeps them');
-assert.equal(v('inflight').bytes, 'dd', 'an in-flight action pins them');
-assert.equal(v('unnamed').bytes, 'ee', 'no known spend date → keep');
+assert.equal(v('freshspent').bytes, 'NEW=', 'a recent spend keeps them');
+assert.equal(v('inflight').bytes, 'FLY=', 'an in-flight action pins them');
+assert.equal(v('unnamed').bytes, 'UNK=', 'no known spend date → keep');
const a = (i) => state.actions[i];
assert.ok(!('destBytesList' in a(0)) && !('changeBytesList' in a(0)) && a(0).amountSat === 5 && a(0).inputIds[0] === 'oldspent', 'old done action loses hex, keeps ids');
assert.equal(a(1).htlcBytesList[0], 'hex', 'a day-old done action still has its hex');
@@ -42,3 +42,22 @@ assert.equal(a(4).htlcBytesList[0], 'hex', 'a failed ln-pay keeps its HTLC bytes
assert.ok(JSON.stringify(state).length < before);
pruneArkState(state, now);
console.log('✓ ark state pruning is selective and idempotent');
+
+// ---- bytes encoding: base64 persisted, hex still readable, hex on the wire ----
+import { vtxoBytesToStr, vtxoBytesFromStr, vtxoBytesToHex, vtxoBytesNormalize } from '../src/ark/proto.js';
+import { slimArkForSync } from '../src/features/ark.js';
+const raw = new Uint8Array(300).map((_, i) => (i * 37) & 255);
+const hexStr = [...raw].map((b) => b.toString(16).padStart(2, '0')).join('');
+const b64 = vtxoBytesToStr(raw);
+assert.ok(b64.length < hexStr.length * 0.7);
+assert.deepEqual(vtxoBytesFromStr(b64), raw);
+assert.deepEqual(vtxoBytesFromStr(hexStr), raw, 'hex from older builds still decodes');
+assert.equal(vtxoBytesNormalize(hexStr), b64);
+assert.equal(vtxoBytesNormalize(b64), b64);
+assert.equal(vtxoBytesToHex(b64), hexStr);
+const st2 = { vtxos: [{ id: 'a', state: 'spendable', bytes: hexStr }, { id: 'b', state: 'spent', bytes: hexStr }], actions: [], movements: [] };
+pruneArkState(st2, now);
+assert.equal(st2.vtxos[0].bytes, b64, 'a save converts persisted hex to base64');
+assert.equal(st2.vtxos[1].bytes, b64);
+assert.equal(slimArkForSync(st2).vtxos[0].bytes, hexStr, 'the sync wire stays hex for older devices');
+console.log('✓ vtxo bytes persist as base64, read hex, ship hex');