Parent epic: https://github.com/DIG-Network/dig_ecosystem/issues/3166
Parent ticket: #412
Sibling: #421 (the create half's missing piece)
Related: #410 (the same class in the served WalletBackend)
The mirror lifecycle has a signer but no broadcaster, so no reclaim can reach chain
spawn_mirror_passes (crates/dig-node-service/src/server.rs) builds NodeMirrorEffects with
lifecycle::production_broadcaster(), which is None today. sign_and_broadcast refuses on that
None before it signs anything, so MirrorEffects::reclaim — the only half of the lifecycle that is
reachable at all, since create refuses pending #421 — cannot complete on a real node.
The reporting is honest about this as of PR #419: open_signer reports
SpendCapability::BroadcasterUnwired rather than Available, the bring-up log says the lifecycle
observes only, and the refusal names this ticket. A test asserts the capability and the broadcaster
cannot disagree. So this is a stated gap, not a silent one — but the gap is real and the lifecycle
cannot settle any money until it closes.
What closing it needs
ChiaQueryBroadcaster (crates/dig-wallet/src/sage/spend.rs:210) is the production implementation
and takes an Arc<chia_query::ChiaQuery>. The node's ChainTransport already holds one, but
ChainTransport::shared_client is pub(crate) to dig-wallet, so dig-node-service cannot reach
it. Closing this means deciding how that client is exposed — a ChainTransport::broadcaster()
constructor in dig-wallet is the smaller surface than making the client public, and it keeps the
"attaching a broadcaster turns on node-custodied sending" decision inside the crate that owns
DIG_WALLET_ENABLE_LIVE_BROADCAST (see the deliberate non-impl note at sage/chain.rs:578).
production_broadcaster() is the single seam to change: open_signer derives the reported
capability from it, and the scheduler passes the same value into the effects, so wiring it turns the
capability to Available and the reclaim path live in one edit.
Done when
- A real node with
DIG_WALLET_ENABLE_LIVE_BROADCAST set reports SpendCapability::Available.
- A reclaim planned by a pass reaches the mempool on mainnet and is observed confirmed on a later
pass (the TargetCoinId derived by reclaimed_coin_id appears on chain).
- The §25.4.4 zero-fee behaviour is unchanged: a reclaim not admitted under fee pressure is retried
next pass rather than gated on selectable XCH.
Parent epic: https://github.com/DIG-Network/dig_ecosystem/issues/3166
Parent ticket: #412
Sibling: #421 (the create half's missing piece)
Related: #410 (the same class in the served
WalletBackend)The mirror lifecycle has a signer but no broadcaster, so no reclaim can reach chain
spawn_mirror_passes(crates/dig-node-service/src/server.rs) buildsNodeMirrorEffectswithlifecycle::production_broadcaster(), which isNonetoday.sign_and_broadcastrefuses on thatNonebefore it signs anything, soMirrorEffects::reclaim— the only half of the lifecycle that isreachable at all, since
createrefuses pending #421 — cannot complete on a real node.The reporting is honest about this as of PR #419:
open_signerreportsSpendCapability::BroadcasterUnwiredrather thanAvailable, the bring-up log says the lifecycleobserves only, and the refusal names this ticket. A test asserts the capability and the broadcaster
cannot disagree. So this is a stated gap, not a silent one — but the gap is real and the lifecycle
cannot settle any money until it closes.
What closing it needs
ChiaQueryBroadcaster(crates/dig-wallet/src/sage/spend.rs:210) is the production implementationand takes an
Arc<chia_query::ChiaQuery>. The node'sChainTransportalready holds one, butChainTransport::shared_clientispub(crate)todig-wallet, sodig-node-servicecannot reachit. Closing this means deciding how that client is exposed — a
ChainTransport::broadcaster()constructor in dig-wallet is the smaller surface than making the client public, and it keeps the
"attaching a broadcaster turns on node-custodied sending" decision inside the crate that owns
DIG_WALLET_ENABLE_LIVE_BROADCAST(see the deliberate non-impl note atsage/chain.rs:578).production_broadcaster()is the single seam to change:open_signerderives the reportedcapability from it, and the scheduler passes the same value into the effects, so wiring it turns the
capability to
Availableand the reclaim path live in one edit.Done when
DIG_WALLET_ENABLE_LIVE_BROADCASTset reportsSpendCapability::Available.pass (the
TargetCoinIdderived byreclaimed_coin_idappears on chain).next pass rather than gated on selectable XCH.