Repository navigation
Use Airavata - #131
Use Airavata#131
Conversation
Code review of the Airavata switch found the fallback rarely triggered in practice: the transient-error matcher missed LWK's real failure shapes (a reachable backend returning a non-JSON error page, Electrum DNS failures), there was no request timeout (a blackholed host blocked ~75s before even trying), and testnet had no fallback at all. A retried broadcast could also surface "already in block chain" as a hard failure for a tx that was already on-chain. - Recognize LWK's actual transient error shapes (structural 5xx check, JsonFrom parse errors, Electrum DNS failures); drop dead HTTP-status string matches that LWK never produces. - Add an explicit Esplora request timeout so a dead primary fails fast. - Add liquid.network as a further fallback on both networks (testnet had none before). - Scale Esplora concurrency down for public backends; keep it high only for our own electrs. - Treat "already in block chain" / already-known errors on a retried broadcast as success, returning the known txid instead of raising. - Build fallback clients lazily inside the retry loop so one bad entry can't mask the primary's real error or block later ones. - lw_tx_status now walks the same backend list as the wallet (previously hardcoded to blockstream.info), so status reads reflect whatever backend a send actually went through; accepts liquid.network explorer URLs as input.
Compress block comments and docstrings in wallet.py/tools.py down to 1-3 lines each. Durable rationale that had no other home (backend order, concurrency, timeout, tx-status 404-retry) moved into docs/CONFIG.md's "Liquid chain backends" section; implementation-detail comments (lwk error shapes, Elements RPC codes) were compressed in place per this repo's own convention of keeping third-party protocol semantics in code, not docs.
TomasCast
left a comment
There was a problem hiding this comment.
LGTM, left 2 small comments found by cursor that did make sense to me.
| detail = "; ".join(failures) | ||
| if not_found: | ||
| raise _EsploraNotFound(detail) | ||
| raise ValueError(f"Could not reach any Liquid Esplora backend ({detail})") |
There was a problem hiding this comment.
CONFIG.md says a 404 is reported only when every backend answered 404. The code reports not-found if any backend answered 404, even when others failed.
Scenario: send goes out through Airavata, the user checks status immediately, Airavata is down/timed out, and Blockstream has not seen the tx yet → Transaction not found for a tx that exists.
test_mixed_404_and_unreachable_reports_not_found_with_detail locks the bug in.
Raise _EsploraNotFound only when not_found covers every backend and failures is empty.
There was a problem hiding this comment.
Good catch, fixed
| if isinstance(exc, lwk.LwkError.EsploraHttpError) and exc.status >= 500: | ||
| # The backend (or the proxy in front of it) failed, not the request. | ||
| return True | ||
| msg = str(exc).lower() | ||
| return any(marker in msg for marker in _TRANSIENT_MARKERS) |
There was a problem hiding this comment.
lw_tx_status treats HTTP 429 as a fallback (tests/test_tools.py covers it). LWK only treats status >= 500 as transient. A rate-limited Airavata makes send/sync fail without trying Blockstream / liquid.network.
Maybe it would be better to treat 429 as transient, same as in tools.
… transient in LWK fallback - lw_tx_status: a 404 while another backend was unreachable now raises 'Could not reach any Liquid Esplora backend' instead of a false 'Transaction not found' (matches docs/CONFIG.md). - _is_transient_backend_error: HTTP 429 falls back to the next backend, same as lw_tx_status already did.
…'url' argument (#138) * 🐛 fix: lightning_refund chain helpers after the Airavata backend switch get_block_height, get_transaction_hex and broadcast_raw_tx (added in #134) still called _get_client(network), whose signature #131 changed to (network, url), so every refund raised TypeError. Route them through the backend fallback, reuse the idempotent _broadcast, and read tx hex over HTTP for Esplora backends: lwk's EsploraClient has no get_tx, and every default Liquid backend is Esplora. Closes #137 * 🐛 fix: get_transaction_hex Electrum misses and non-hex 200 bodies Electrum: lwk's ElectrumClient.get_tx never returns None; a missing tx raises LwkError ("missing transaction", probed live on Blockstream's Liquid Electrum). The old None check was dead, so a miss escaped as a raw LwkError instead of the "Transaction ... not found" ValueError. Recognize the miss and treat it like an Esplora 404. The test now raises what lwk really raises instead of returning None. Esplora: a 200 whose body is not tx hex (a proxy HTML page, a JSON error, an empty or non-UTF-8 body) was returned as the lockup hex and failed later inside wally.tx_from_hex, without trying the next backend. Validate the body, record the backend as failed and fall back.
Walkthrough
Type:
fixSwitches Liquid's default chain backend from Blockstream to AQUA's own Airavata electrs, with
blockstream.infoandliquid.networkas ordered fallbacks (testnet previously had no fallback at all). A code review of the initial switch found the fallback logic itself was mostly dead: the transient-error matcher missed LWK's real failure shapes, there was no request timeout (~75s before even trying the next backend), and a retried broadcast could report an already-confirmed tx as a hard failure. This PR fixes those defects and extends the same ordered backend list tolw_tx_status, which previously stayed hardcoded to Blockstream.Changes
src/aqua/wallet.pyliquid.networkadded as a further fallback on both networks; per-backend concurrency (12 for Airavata, 3 for public backends); idempotent broadcast retry (already in block chain→ success with the known txid, not an error); fallback clients built lazily inside the retry loop so one bad entry can't mask the real errorsrc/aqua/tools.pylw_tx_statusnow walks the same ordered backend list as the wallet (was hardcoded toblockstream.info); acceptsliquid.networkexplorer URLs as inputsrc/aqua/cli/liquid.pytx-status --txhelp text now says "Liquid explorer URL" instead of naming Blockstream specificallydocs/CONFIG.mdelectrum_urloverride replaces the list with a single non-fallback backendtests/test_wallet.pytests/test_tools.pyFlow
sequenceDiagram participant Client as MCP client participant Wallet as WalletManager.send() participant Airavata participant Blockstream participant LiquidNet as liquid.network Client->>Wallet: send(...) Wallet->>Airavata: broadcast(tx) Airavata-->>Wallet: timeout (15s) Wallet->>Blockstream: broadcast(tx) alt already relayed by Airavata Blockstream-->>Wallet: HTTP 400 "already in block chain" Wallet-->>Client: txid (treated as success) else still unreachable Blockstream-->>Wallet: connection reset Wallet->>LiquidNet: broadcast(tx) LiquidNet-->>Wallet: 200 OK Wallet-->>Client: txid endlw_tx_statusfollows the same ordered fallback (Airavata → Blockstream → liquid.network on mainnet), so a status read reflects whatever backend a send actually went through.