diff options
| author | Adam Soltys <[email protected]> | 2026-08-14 15:24:17 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-14 15:24:17 -0700 |
| commit | 4bd22c0c45ae35388b2a24e673bedcde5df77961 (patch) | |
| tree | 74eed71c3b477dd14be848f5f4d9d72814a04f47 /src/ark/manager.js | |
| parent | 7a7b2c61fe8394844fa306debb84917454ade7b0 (diff) | |
| download | coinosv3-4bd22c0c45ae35388b2a24e673bedcde5df77961.tar.gz coinosv3-4bd22c0c45ae35388b2a24e673bedcde5df77961.zip | |
A mailbox backlog claimed at sign-in reads as the old payments they are, not a wall of seconds-ago windfalls; a gift send knows itself in history — status shown, link recopyable, revoke at hand
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
Diffstat (limited to 'src/ark/manager.js')
| -rw-r--r-- | src/ark/manager.js | 28 |
1 files changed, 27 insertions, 1 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js index b2c2837..143242a 100644 --- a/src/ark/manager.js +++ b/src/ark/manager.js @@ -150,6 +150,28 @@ export class ArkManager { _movement(m) { this.state.movements.push({ id: `${Date.now()}-${this.state.movements.length}`, ts: Date.now(), ...m }); } + + // When did a mailbox vtxo come into being? Its expiry counts down from the + // server's vtxo lifetime, so (lifetime - blocksLeft) blocks have passed + // since the sender created it. At ~10 minutes a block that dates a backlog + // claimed at first sign-in as the old payments they are — instead of a wall + // of "seconds ago" receives and a bogus celebration. + _estimateReceiveTs(v, tip) { + const life = (this.info && this.info.vtxoExpiryDelta) || 0; + if (!life || !tip || !v.expiryHeight) return Date.now(); + const age = life - (v.expiryHeight - tip); + if (age <= 0) return Date.now(); + return Date.now() - age * 600_000; + } + + async _tipMemo() { + if (this._tipAt && Date.now() - this._tipAt < 60_000) return this._tipH; + try { + this._tipH = await this.chain.tipHeight(); + this._tipAt = Date.now(); + } catch { /* keep whatever we had */ } + return this._tipH || 0; + } _vtxo(id) { return this.state.vtxos.find((v) => v.id === id); } _addVtxo(decoded, bytes, keyIndex, state = 'spendable') { if (this._vtxo(decoded.id)) return false; @@ -264,6 +286,7 @@ export class ArkManager { } if (m.kind !== 'arkoor') return changed; const ourKeys = [hex.encode(this._key(0).pubkey)]; + const tip = await this._tipMemo(); for (const v of m.vtxos) { if (this._vtxo(v.id)) continue; try { @@ -280,7 +303,10 @@ export class ArkManager { // same message concurrently (validation awaits in between), and only the // copy that actually added the vtxo may record the receive movement. if (!this._addVtxo(v, v._raw.bytes, 0)) continue; - this._movement({ type: 'receive', amountSat: v.amountSat, status: 'complete', vtxoId: v.id }); + this._movement({ + type: 'receive', amountSat: v.amountSat, status: 'complete', vtxoId: v.id, + ts: this._estimateReceiveTs(v, tip), + }); changed = true; } return changed; |
