aboutsummaryrefslogtreecommitdiffstats
path: root/src/ark
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-18 01:04:55 -0700
committerAdam Soltys <[email protected]>2026-08-18 01:04:55 -0700
commit4069f813820f1814bef5b93c679619bc7ed8ddba (patch)
tree94d3782664cbc61e68773d1d55d590245c1dce90 /src/ark
parent6fc84e13bd739b634bccd8f147fc881013d8fad8 (diff)
downloadcoinosv3-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.js25
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