Uh oh!
There was an error while loading. Please reload this page.
fix(wallet): report an honest phase when the node has no addresses to watch - #212
Conversation
MichaelTaylor3d
commented
Aug 10, 2026
Real-machine verification — measured, not inferredTwo nodes, one machine, the same chain, seconds apart. Both have no wallet enrolled (empty BEFORE — installed AFTER — this branch, The peak is genuinely advancing under the new phase, so this is a live replica and not a latched The invariant that must NOT move, checked directly in the DB
The anti-conflation guard, caught working on real hardwareThe first run of the new binary reported: An empty watched set ( Live node undisturbedThe installed service was never stopped and its binaries were never swapped — the new build ran |
Fix the wallet sync phase never settling on a default install.
An empty puzzle-hash set never runs a catch-up, so initial_sync_complete cannot latch while the peak advances. Add SyncPhase::NoAddressesToWatch, gated on a writing peer AND a measured-zero watched set.
MichaelTaylor3d
left a comment
There was a problem hiding this comment.
CHANGES-REQUIRED — independent correctness gate, read at head 8c7c98f3e5049c2bf9eff36d58d005a96afbe6dd.
The fix itself is right and I verified it by mutation in my own worktree, not by reading:
| probe | result |
|---|---|
drop the session_may_write condition from the ladder | a_refused_writer_is_not_reported_as_nothing_to_watchFAILS — real guard |
delete the handle.set_subscription(...) wiring line in Supervisor::run | a_default_install_with_no_wallet_settles_on_nothing_to_watchFAILS — real wiring proof |
replace observed.watched == Some(0) with observed.watched.unwrap_or(0) == 0 | all 30 supervisor tests stay GREEN — see finding 1 |
#2568 is untouched: no line of this diff reaches quorum corroboration, common_height(), Verdict::Insufficient or the by-name coin confirmation, and all of a_split_quorum_writes_nothing, an_unreachable_quorum_refuses_rather_than_defaulting_to_allow, a_single_lying_writer_cannot_move_the_replica, the_writer_is_examined_at_a_height_it_did_not_choose pass unchanged. The three-fact gate is sound for every state I could construct — Resubscribe clears the facts via set_connected(0) before reconnecting, the catch-up-error path clears them too, and no path sets peers=1 while carrying a previous session's facts.
Two findings block, both inline. Also note for the orchestrator: mergeStateStatus=DIRTY and all five required contexts are ABSENT on 8c7c98f (check-merge-preconditions.sh → BLOCKED) — rebase and re-run before any merge, independent of this verdict.
Non-gating, resolved by me: the sibling dig-app surface is filed as dig_ecosystem#2617; the PR body should cite it by number rather than "tracked separately".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…eason field Bump the workspace version to 0.113.0 (0.112.0 is claimed by the open #210 branch).
8c7c98f to
6c28557CompareCo-authored-by: MichaelTaylor3d <5665004+MichaelTaylor3d@users.noreply.github.com>
Review measured that replacing == Some(0) with unwrap_or(0) == 0 left the whole suite green: the only unmeasured test also left may_write false, so the trust condition rejected it a step earlier. Split set_subscription into set_trust + set_watched so the real in-between state (trust settled, set not yet resolved) is reachable, and add a test for it. The mutation now fails that test.
Uh oh!
There was an error while loading. Please reload this page.
Closes DIG-Network/dig_ecosystem#2609
The defect, and which of the three candidates it actually is
Cause 2, confirmed in code and on the machine. The phase could never settle on a default install.
Supervisor::runcomputes the subscription set, then:catch_upis the only caller ofcomplete_catch_up, which is the only writer ofinitial_sync_complete. So with zero puzzle hashes the flag can never latch — whilerun_update_loop'sNewPeakWalletarm keeps advancingsync_state.peak_heightwith the chainfor an authoritative peer. The old ladder read
initial_sync_complete == falseasSyncing.That is exactly the measured shape: replica at the tip, advancing, phase
syncingforever.Candidate 1 (a missing terminal
is_finished) is excluded: the terminal response is never awaitedbecause the catch-up never starts. Candidate 3 is excluded: the phase already consults live peer
state, not
initial_sync_completealone.The fix
A fourth phase,
no_addresses_to_watch, and awatched_addressesfield that reports the reason.The phase is NOT flipped on an empty set.
initial_sync_completestays false, because latchingit would flip §18.7 routing to
Source::Dband read a funded wallet as empty. The new variant says"the chain replica is current AND no wallet-scoped claim is being made" — which
syncedcannot say.Three facts are required, and each excludes a different lie:
0default announcing itself as a factThe trust condition is the load-bearing one.
nothing_subscribedis true both when custody isempty (benign) and when corroboration refused the writer (not benign — the replica is deliberately
not being written). Keying the phase on it would have told a user everything was fine while their
node silently stopped following the chain.
watchedisOption<u32>, notu32, so"not resolved yet" cannot be spelled the same way as "resolved, and empty".
Verification
Red first, on the assertion rather than a compile error — the API was added with the ladder left
unchanged, so the new tests failed
left: Syncing, right: NoAddressesToWatch. The three guardtests passed while red (they expect
Syncing), so they genuinely constrain an over-broad fix.Six new tests: the default-install case, the refused-writer anti-conflation guard, the
unmeasured-set guard, fact-clearing on disconnect, an enrolled wallet mid-catch-up, and an
end-to-end run through the real supervisor loop that fails if the ladder is fixed but the wiring
is forgotten.
cargo test -p dig-wallet --lib: 452 passed, 0 failed. Every #2568 quorum test still passes.Real-machine before/after with the installed binary is posted on the issue.
Blast radius
control.wallet.syncStatusgains a token and a field — both additive. The method is not indig-node-control-interface, so no release-first cross-repo change is needed. dig-app must learnthe new token; that surface is tracked separately.
Version
0.113.0(minor — additive capability).0.112.0is claimed by the open #210 branch.