diff options
| author | Adam Soltys <[email protected]> | 2026-10-27 12:28:33 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-27 12:28:33 -0700 |
| commit | a788054f0dcbdd17e2df7614d3156ad053afaa82 (patch) | |
| tree | ad83b834ed6cd738603cbe84c3a864fcfda44a7a /names | |
| parent | c2f8c3281f02f29c19025553addfbd7638256b74 (diff) | |
| download | coinosv3-server-a788054f0dcbdd17e2df7614d3156ad053afaa82.tar.gz coinosv3-server-a788054f0dcbdd17e2df7614d3156ad053afaa82.zip | |
names: a float send that threw is looked up before the payment is tried again
The float wallet keeps every send as an action. forward() records the
send it is about to make (destination, amount, start time) before making
it; a later try finds that action: done = delivered, failed or never
started = go again, anything else = still unresolved, wait. Before, a send
that threw after its cosign landed (the coins moved, the answer didn't)
was queued and paid a second time.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
Diffstat (limited to 'names')
| -rw-r--r-- | names/server.js | 22 |
1 files changed, 20 insertions, 2 deletions
diff --git a/names/server.js b/names/server.js index f8fa378..f2a9f41 100644 --- a/names/server.js +++ b/names/server.js @@ -440,7 +440,24 @@ async function forwardOnce(name, sat, hash, pubkey) { // 2026-10-27 ("crit1 … double payout", ~5M). So: done is done, still out // is wait, and only a payment that really failed is tried again. const sent = hash && state.inflight?.[hash]; - if (sent) { + // ...and the same for a float send: one that threw can still have moved + // the coins (the cosign landed, the answer didn't). The float wallet keeps + // every send as an action; the one made for this payment says what it did. + if (sent?.float) { + const acts = (fwd?.state?.actions || []).filter((a) => a.type === 'send' && a.destAddress === sent.dest + && a.amountSat === sent.sat && Number(String(a.id).slice(5).replace(/-+$/, '')) >= sent.after); + if (acts.some((a) => a.step === 'done')) { + delete state.inflight[hash]; + recordLnReceipt(name, sat, hash); + persist(); + log(`delivered ${sat} sat to ${name}: the earlier float send completed`); + return; + } + if (acts.some((a) => a.step !== 'failed')) throw new Error('float send for this payment still unresolved — not paying again'); + log(`forward: the earlier float send for ${name} ${acts.length ? 'failed' : 'never started'} — trying again`); + delete state.inflight[hash]; + persist(); + } else if (sent) { const pays = (await ln.call('listpays', { payment_hash: sent.paymentHash })).pays || []; if (pays.some((x) => x.status === 'complete')) { delete state.inflight[hash]; @@ -465,7 +482,8 @@ async function forwardOnce(name, sat, hash, pubkey) { const viaFloat = async () => { if (!ownServer || fwd.balance().spendableSat < sat) return false; - try { await fwd.send(dest, sat); } + if (hash) { (state.inflight ||= {})[hash] = { float: true, dest, sat, name, after: Date.now() }; persist(); } + try { await fwd.send(dest, sat); settled(); } catch (e) { // enough sats, too many coins: merge them, and the queue's next try goes if (/too many coins/.test(e.message)) consolidateFloat().catch((x) => log('float consolidate failed: ' + x.message)); |
