fix(sdk): surface ARC broadcast errors as Err and parse cached tx bytes in get_transaction#104
Open
E-Jacko wants to merge 1 commit into
Open
Conversation
…es in get_transaction
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two-part fix to
packages/runar-rs/src/sdk/wallet.rs:WalletProvider::broadcastreturnsErr(...)on ARC HTTP non-2xx and on network failure, instead of swallowing both and returningOk(synthetic_txid). The caller can now distinguish "ARC accepted" from "ARC rejected" or "ARC unreachable."WalletProvider::get_transactionparses cached transaction bytes (hex from the localtx_cache) and populatesTransactionData.inputs/outputs, instead of always returning empty arrays. Cache-miss semantics are preserved unchanged (stillOk(TransactionData { raw: None, .. })— locked by an explicit regression test).Both fixes are local-minimal — no trait-signature changes, no new pub-surface besides what was strictly required to write the regression tests through public API.
Problem
R1 — silent broadcast failure
Upstream HEAD (
packages/runar-rs/src/sdk/wallet.rs):Both branches return
Ok(...). On an HTTP non-2xx response (e.g. ARC HTTP 461 — "Script evaluated without error but finished with a false/empty top stack element" — the canonical NULLFAIL / preimage-mismatch rejection), the synthetic locally-computed txid is returned and the caller treats the broadcast as successful. On a network failure (connection refused, DNS error, TLS handshake failure) the same path runs — the SDK pretends a never-attempted broadcast succeeded.This is a textbook silent-failure bug. The caller has no signal that the transaction is not on chain.
R2 — empty inputs/outputs on get_transaction
Upstream HEAD (
packages/runar-rs/src/sdk/wallet.rs:323-345):On both cache hit and cache miss,
inputsandoutputsare returned as emptyVec. The SDK's higher-levelRunarContract::from_txid(txid)then callsprovider.get_transaction(txid)and indexestx.outputs[output_index]to find the contract UTXO — indexing into an emptyVecpanics, or (when wrapped in a.get(...)check) returns "output index 0 out of range (tx has 0 outputs)."How this surfaced
R1: surfaced during a mainnet broadcast where the SDK reported
Ok(txid), but a subsequent post-broadcast audit against chain state showed the transaction was never accepted by ARC — ARC had returned an HTTP 461 that the SDK silently swallowed. Caller-side reconciliation eventually caught the divergence, but only after multiple downstream actions had been keyed off the (non-existent) "broadcast" txid.R2: surfaced when reloading a deployed stateful contract by its on-chain txid —
RunarContract::from_txid(deployed_txid)could not retrieve the contract output, blocking every method call on every reloaded stateful contract. The SDK hasparse_raw_txin the same file; populating from it is straightforward.Fix
R1 — surface broadcast failures:
R2 — populate inputs/outputs from cached bytes:
The cache-miss branch is preserved verbatim from upstream — same
Ok(TransactionData { raw: None, .. })shape. A regression test (r2_get_transaction_cache_miss_preserves_upstream_fallback) locks this in so future refactors can't silently change cache-miss semantics.Cross-language parity
TS
runar-sdk/src/providers/wallet-provider.ts:196-217parsestx_cachehex viaTransaction.fromHex()and maps inputs/outputs onto the result — exactly the shape this PR ports into Rust. The TS broadcaster path already returns the equivalent error shape via thrown exceptions on non-2xx HTTP.The strict canonical ecosystem shape for broadcast errors across
@bsv/sdk(Broadcaster.ts:24-34) andb1narydt/bsv-rust-sdk(src/transaction/broadcaster.rs:33-56) is a structuredBroadcastFailure { status, code, description, txid?, more? }. The runar-rsWalletProvider::broadcasttrait signature today isResult<String, String>— adopting the structured shape would require a trait-signature change. This PR keeps the trait signature stable and surfaces the HTTP status + ARC body in theErrstring. See ISSUE-02 —WalletProvider::with_broadcaster injectionfor the architectural follow-up that would letWalletProviderdelegate to the canonicalBroadcastertrait directly (and thus inherit the structured failure shape upstream).Verification
Reproducible against upstream HEAD on a fresh clone in under a minute:
Passes with patch:
Fails without patch (clean
git stashofwallet.rs):The one passing pre-patch test (
r2_get_transaction_cache_miss_preserves_upstream_fallback) intentionally locks upstream's unchanged behavior on cache miss — its post-patch pass proves the patch did NOT regress that fallback path.Backwards compatibility
WalletProvider::broadcaststill returnsResult<String, String>,get_transactionstill returnsResult<TransactionData, String>.Ok(_)continue to compile. Callers that ignored theErrarm (relying on the silent-Ok behaviour to mask failures) will now see real errors propagate — which is the intended behaviour change.get_transactioncache-miss path is unchanged; a regression test (r2_get_transaction_cache_miss_preserves_upstream_fallback) locks the upstreamOk(TransactionData { raw: None, .. })shape so future refactors can't silently regress it.TxInput,TxOutputwere already exported fromsuper::types).Related
WalletProvider::with_broadcaster injection: the architectural follow-up that would adoptbsv-rust-sdk::Broadcasterupstream and inherit the canonical structuredBroadcastFailureshape. R1 here is the local-minimal fix; ISSUE-02 is the long-term direction.CallOptions::verify_inputs_on_chain: defensive opt-in pre-broadcast probe — addresses a related cross-broadcaster failure class that R1 helps surface (now that errors are visible, the probe can fail fast before submitting an orphan-bound child).