Skip to content

[feat] CallOptions::verify_inputs_on_chain — opt-in pre-broadcast ARC probe of input parents #108

Description

@E-Jacko

Use case

Defensive pre-broadcast verification for transaction chains that touch multiple broadcasters within one session. When a parent transaction is broadcast through one broadcaster and a spend-the-parent child is then submitted through a different broadcaster, the second broadcaster can arrive before the first has propagated the parent across its mempool. The second broadcaster stamps the child SEEN_IN_ORPHAN_MEMPOOL — sticky for 5+ blocks; child eventually drops; downstream state has to be reconciled after the fact.

The architectural fix is single-broadcaster injection (see ISSUE-02). This issue proposes a defensive opt-in probe that catches the same class reactively when the architectural fix isn't available: before broadcasting, the SDK ARC-pings each input's parent txid and bails if any parent isn't visible yet.

How this surfaced

Surfaced during cross-broadcaster transaction chains on mainnet — parent broadcast through one broadcaster, child broadcast through another, parent visibility hadn't propagated to the second broadcaster's mempool yet, child stamped SEEN_IN_ORPHAN_MEMPOOL and held for 5+ blocks before eventually dropping. The downstream impact was a recurring reconcile-after-the-fact tax on every chained broadcast where the deployment topology happened to route the two through different ARC instances.

Current behavior

packages/runar-rs/src/sdk/wallet.rs::broadcast POSTs directly to ARC without any pre-flight chain-state check. If the broadcaster doesn't recognize a parent's txid, it accepts the child into its orphan mempool and waits for the parent — but the parent may never arrive if the parent broadcaster is a separate ARC deployment.

There is no CallOptions flag today that gates broadcast on parent visibility. A consumer wanting this behaviour has to write its own probe outside the SDK and short-circuit before calling into RunarContract::call(...).

Proposed shape (design question)

Add verify_inputs_on_chain: bool to CallOptions, default false. When true, before broadcasting:

  1. For each input on the call transaction, look up the parent txid (already known — every input has source_txid).
  2. Probe ARC's /v1/tx/{parent_txid} status endpoint for each.
  3. If any parent returns SEEN_IN_ORPHAN_MEMPOOL, 404, or otherwise unhealthy → bail with Err(OrphanInputs { parents: Vec<String> }) or similar; do not submit the child.
  4. If all parents are visible → broadcast normally.
pub struct CallOptions {
    pub satoshis: Option<i64>,
    pub locktime: Option<u32>,
    // ...
    pub verify_inputs_on_chain: bool,    // ← new, defaults to false
}

Trade-offs:

  • Latency: one extra round-trip per input. Significant for many-input transactions; trivial for the common 1-2-input case. Opt-in default-off so consumers who know their topology is single-broadcaster skip the tax.
  • Coupling to ARC's status surface: the probe depends on ARC's status response shape. If the consumer's deployment uses a non-ARC broadcaster, the probe can't be issued. Suggest gating the probe on whether the configured broadcaster implements a status_probe capability (a future trait method) and silently no-op when it doesn't.
  • Symptom vs cause: this is symptom-treatment. The architectural fix is shared broadcaster injection (ISSUE-02) — once both wallet-toolbox and runar broadcast through the same instance, parent visibility is guaranteed at the broadcaster layer and the probe becomes unnecessary. Suggest landing both: this issue as the defensive measure for consumers that can't unify broadcaster configuration yet; ISSUE-02 as the structural follow-up.

An optional patch (runar-input-chain-truth-verification.patch) sketches one possible implementation, but the design call should land first.

Alternatives considered

Shape Strengths Weaknesses
CallOptions::verify_inputs_on_chain (proposed) Opt-in, default off. Catches cross-broadcaster orphan class reactively. Per-broadcast latency cost. Symptom-treatment, not root cause.
Always-on probe No per-call config; consumers can't forget to enable. Latency tax on every broadcast even in single-broadcaster topologies.
Broadcaster injection (ISSUE-02) Root-cause fix — single broadcaster means single mempool, no orphan class. Larger surface change. May warrant trait-signature break.
Document the deployment matching requirement Zero code. Punts on the consumer side; consumers in multi-tenant environments can't always match.

Backwards compatibility

Purely additive. Existing callers that don't set verify_inputs_on_chain see byte-identical behaviour. Default-off ensures no latency regression for current consumers. The new error variant (OrphanInputs or equivalent) is reachable only when the flag is set.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions