Skip to content

fix: report on-chain broadcast outcomes - #119

Open
ovitrif wants to merge 4 commits into
mainfrom
codex/112-explicit-broadcast-outcome
Open

ovitrif wants to merge 4 commits into
mainfrom
codex/112-explicit-broadcast-outcome

Conversation

@ovitrif

@ovitrif ovitrif commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #112
Refs:

Description

Source fixes and published 0.7.0-rc.68 canonical artifacts are verified. Both apps now consume matching distributed packages and pass the affected tests and current fixed-amount/send-all native journeys.

  • Adds fixed-amount and send-all APIs that return OnchainSendResult::{Accepted, Rejected, Unknown} with the locally computed transaction ID, so callers can distinguish backend acknowledgement from transaction creation.
  • Submits explicit user sends directly through one backend route, reusing transaction preparation and existing backend clients. Esplora accepts only an exact txid response. The LDK package queue and legacy send signatures remain available.
  • Fixes Electrum's nested task/RPC result handling and checks returned transaction IDs in normal execution. Recognized non-final refusals are reported narrowly; transport failures, timeouts, mismatched IDs and unclassified responses remain unknown.
  • Generates matching Swift, Kotlin and Python bindings in 0.7.0-rc.68; canonical regeneration leaves tracked binding signatures unchanged.

Contract and limitations

  • Err is limited to a failure that proves this invocation never initiated broadcast. Current-thread Tokio callers receive PaymentSendingFailed before wallet preparation. Once submission may have started, the returned report carries the transaction ID.
  • Accepted means the configured backend acknowledged this transaction; it does not guarantee confirmation or propagation.
  • Rejected reports a recognized refusal response. Earlier delivery may still have happened, so neither rejection nor an unknown result authorizes creating another payment.
  • Apps must save a durable attempt guard before calling the node and preserve it after cancellation, a missing result, rejection or ambiguity. Success and payment proof require acknowledgement or independent positive observation of the exact transaction ID.
  • Refused or unknown results can leave an attempt blocked indefinitely when the transaction never becomes positively observed. This API has no durable outcome lookup or recovery mechanism and provides no exactly-once guarantee across devices or restored backups.

Out of Scope

  • Transaction recovery: journals, raw transaction storage, automatic rebroadcast, replacement, RBF recovery and abandonment.
  • Chain handling: reorg and event-delivery redesign.

QA Notes

Manual Tests

  • Matching Android/iOS consumer → accepted fixed-amount or send-all → one unconfirmed send completes normally.
  • Non-final refusal or lost response → no sent-success UI or payment proof → reopening or switching payment methods cannot create another payment.
  • Accepted result followed by local storage/activity/proof failure → local follow-up can resume without another node send.

Automated Checks

  • added integration_tests_rust.rs — funded direct fixed-amount/send-all across Electrum, Esplora and bitcoind, exactly one submission, dead-backend unknown result, pre-dispatch stopped-node error, and the legacy txid limitation.
  • added electrum.rs — actual TCP refusal responses cannot become acceptance after a successful task join; a timed-out blocking RPC may remain in flight and returns unknown.
  • added bitcoind.rs, esplora.rs, electrum.rs — acceptance, mismatched transaction IDs, verified non-final responses and conservative unknown classification.
  • ran funded fixed-amount/send-all regtest checks across all three backends, one-submission checks, lost-response unknown handling and stopped-node checks. The corrected source also passed the six focused response/runtime regressions described below before canonical publication.
  • ran five focused backend classifier, Electrum refusal and held-open timeout checks on 0.7.0-rc.67; canonical ./bindgen.sh, Swift and compiled Kotlin native initialization, both new stopped-node methods, framework slices, all three Android ABI checks and 16 KB alignment passed.
  • ran distributed rc68 consumer validation: Android remote/resolved AAR and installed native bytes match the canonical artifact, 95 affected tests and app/test APK builds passed; iOS archive/checksum/resolved framework match, its simulator build and 101 affected tests passed. Both apps completed funded fixed-amount/send-all native journeys with exact UI transaction IDs independently observed on their backends. iOS send-all used Manual coin selection.
  • ran previous rc67 validation: Android 621 focused tests, iOS 93 affected tests and both apps' native fixed-amount/send-all journeys. Those results are historical; the Android pending-screen component preview uses injected state.
  • ran two new regressions red → green: real Esplora HTTP replies require the exact returned txid and preserve configured client headers; current-thread Tokio callers fail before wallet preparation. The fixed batch passed six focused tests, including 10 HTTP reply cases and funded fixed/send-all acceptance across all three backends.
  • A funded stale-tip/non-final end-to-end mobile reproduction remains unrun; refusal protocol fixtures cover the verified server response shapes.

Release

  • v0.7.0-rc.68 — prerelease from 773792d, including the response/runtime fixes at f148c15; not marked latest.
  • Swift release SHA-256: 1955a179fdb6acc5159ead68225dd27029d2f2a74c4b8b13164038eb9f95462f, matching Package.swift and the uploaded asset digest.
  • Android com.synonym:ldk-node-android:0.7.0-rc.68 was published from the same verified canonical build. AAR SHA-256: 0cee2079291260aea12bf60ac9c8af8f459d9f24c3327d4cb021e5686035aa2f. The existing local Gradle publishing task exited successfully.
  • Canonical rc68 build, Swift/JVM native smokes, generated signatures, framework slices, all three Android ABIs, symbols and 16 KB alignment passed. Matching distributed rc68 consumer validation passed as described above.
  • The automatic release workflow failed before compilation during Android SDK setup (tools package unavailable). The release assets and Maven package were published through the existing local route from the verified canonical build; this is not a successful hosted workflow run.
  • Previous rc67 artifacts remain unchanged; its consumer results above are historical. Latest remains rc66.

@ovitrif ovitrif self-assigned this Sep 30, 2026
@chatgpt-codex-connector

This comment has been minimized.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Comment thread src/chain/esplora.rs
@ovitrif
ovitrif marked this pull request as draft September 30, 2026 01:40
@ovitrif

ovitrif commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed f148c15: Esplora now requires the exact txid acknowledgement using the existing configured client, and current-thread Tokio callers return a pre-dispatch error before wallet preparation. This addresses the two current broadcast-boundary findings.

Both regressions failed on the old source and passed on the fix. Six focused tests passed, including 10 real HTTP reply cases, unchanged wallet/mempool state for both unsupported-runtime calls, and funded fixed/send-all acceptance across all three backends. Public API shapes and the legacy queue are unchanged.

This remains draft: rc67 is immutable and lacks these corrections. Corrected artifacts and affected Android/iOS consumer validation are required before readiness.

@ovitrif

ovitrif commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

I pushed 773792d with the rc68 version and matching Swift checksum. Canonical artifacts include the broadcast response and runtime fixes at f148c15. The build, native Swift/Kotlin smokes, ABI/signature/hash checks, Android 16 KB alignment and symbol checks passed.

The previous rc67 remains unchanged. Remote rc68 publication and repeated affected consumer checks are still pending, so this PR remains draft.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@ovitrif
ovitrif requested a review from ben-kaufman September 30, 2026 03:24

@ben-kaufman ben-kaufman left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One optional simplification: Rejected and Unknown have the same retry contract here. Both retain the transaction ID and must not authorize another payment. Could we collapse them into a single uncertain result and keep the backend refusal details in logs? That would remove the backend-specific non-final parsing and reduce the generated binding surface. I don't think this blocks the PR.

@ovitrif

ovitrif commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

Both retain the transaction ID and must not authorize another payment.

@ben-kaufman Agreed: the retry contract is the same. Rejected currently distinguishes a recognized backend refusal for diagnostics; it does not establish that an earlier request was never delivered or make another payment safe. I am keeping the validated three-outcome API and the matching rc.68 consumers for this PR. Collapsing the outcomes would change the generated bindings and require another matching prerelease and consumer validation; your optional simplification remains outside this batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: On-chain send returns before broadcast result

2 participants