diff options
| author | Adam Soltys <[email protected]> | 2026-08-18 01:04:55 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-18 01:04:55 -0700 |
| commit | 4069f813820f1814bef5b93c679619bc7ed8ddba (patch) | |
| tree | 94d3782664cbc61e68773d1d55d590245c1dce90 /src/ark | |
| parent | 6fc84e13bd739b634bccd8f147fc881013d8fad8 (diff) | |
| download | coinosv3-4069f813820f1814bef5b93c679619bc7ed8ddba.tar.gz coinosv3-4069f813820f1814bef5b93c679619bc7ed8ddba.zip | |
The board drive gets a second witness: the wallet's own chain view vouches for confirmations when the ark esplora is rate-limited or walled off, and chain errors finally surface instead of masquerading as unconfirmed
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JtaatFfAbx91Nh3xqKVqKz
Diffstat (limited to 'src/ark')
| -rw-r--r-- | src/ark/manager.js | 25 |
1 files changed, 21 insertions, 4 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js index 01e257a..3a73e0b 100644 --- a/src/ark/manager.js +++ b/src/ark/manager.js @@ -63,11 +63,14 @@ const EMPTY_STATE = () => ({ }); export class ArkManager { - constructor({ account, storage, arkUrl, esploraUrl, network = 'regtest', onUpdate, lnQuoteUrl }) { + constructor({ account, storage, arkUrl, esploraUrl, network = 'regtest', onUpdate, lnQuoteUrl, statusHint }) { this.account = account; // HDKey node; ark keys derived beneath it this.storage = storage; // { load(): obj|null, save(obj): void } this.arkUrl = arkUrl; this.esploraUrl = esploraUrl; + // Optional second witness for tx confirmations (the wallet's own chain + // view) — consulted when the esplora above can't answer. + this.statusHint = statusHint || null; this.network = network; // where to ask what a lightning payment's route will cost (names /lnquote) this.lnQuoteUrl = lnQuoteUrl !== undefined ? lnQuoteUrl @@ -188,7 +191,12 @@ export class ArkManager { const base = this.esploraUrl; return { tipHeight: async () => Number(await fetch(`${base}/blocks/tip/height`).then((r) => r.text())), - getTxStatus: async (txid) => fetch(`${base}/tx/${txid}/status`).then((r) => r.ok ? r.json() : null), + getTxStatus: async (txid) => { + const r = await fetch(`${base}/tx/${txid}/status`); + if (r.ok) return r.json(); + if (r.status === 404) return null; // unknown tx is an answer + throw new Error(`chain status ${r.status}`); // rate limits etc must SURFACE + }, getTxHex: async (txid) => fetch(`${base}/tx/${txid}/hex`).then((r) => r.ok ? r.text() : null), broadcastTx: async (txHex) => { const r = await fetch(`${base}/tx`, { method: 'POST', body: txHex }); @@ -1345,7 +1353,15 @@ export class ArkManager { const keys = this._key(action.keyIndex); if (action.step === 'funded') { const need = this.info.requiredBoardConfirmations; - const status = await this.chain.getTxStatus(action.fundingTxid); + let status = null, statusErr = null; + try { status = await this.chain.getTxStatus(action.fundingTxid); } catch (e) { statusErr = e; } + // The wallet's own chain view can vouch when the ark esplora can't + // (rate limits, bot walls) — both watch the same network. + if (!status?.confirmed && this.statusHint) { + const hint = this.statusHint(action.fundingTxid); + if (hint?.confirmed) { status = hint; statusErr = null; } + } + if (!status && statusErr) throw statusErr; // recorded as lastError → visible notice // record progress so the UI can say WHERE the wait is (n/need confs). // A confirmed status proves at least ONE confirmation on its own — the // tip fetch only refines the count, so its failure (rate limits) must @@ -1353,7 +1369,8 @@ export class ArkManager { let confs = 0; if (status?.confirmed) { const tip = await this.chain.tipHeight().catch(() => null); - confs = tip != null && Number.isFinite(tip) ? tip - status.block_height + 1 : 1; + const delta = tip != null ? tip - status.block_height + 1 : NaN; + confs = Number.isFinite(delta) && delta > 0 ? delta : 1; } if (confs !== action.confs || need !== action.needConfs) { action.confs = confs; action.needConfs = need; this._save(); } if (!(confs >= need)) return; // retried on sync |
