Skip to content

build(deps)!: unify dig-node's three Chia diallers onto chia-query's light client - #339

Merged
MichaelTaylor3d merged 7 commits into
mainfrom
loop/2761-unify-chia-dialler
Aug 24, 2026
Merged

build(deps)!: unify dig-node's three Chia diallers onto chia-query's light client#339
MichaelTaylor3d merged 7 commits into
mainfrom
loop/2761-unify-chia-dialler

Conversation

@MichaelTaylor3d

@MichaelTaylor3dMichaelTaylor3d commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

DRAFT — DO NOT MERGE. Gate round has not run.

Closes nothing — deliberately. This PR was opened against #308 and #249 and does
NOT satisfy either; using the keyword would auto-close two tickets whose work is
unfinished.

Epics (a submodule PR cannot close a super-repo issue — these close manually):

What this does

dig-node held three Chia dialling stacks. This removes one and merges another,
leaving one owner — without reducing the number of independently-held peer sessions
NC-12 depends on.

1. A third stack that was never wired at all.dig-node-core carried a
chia-peer-backed subscribing light client, meant to register as a
ChainSourceProvider into a chia_query::ProviderRegistry. Nothing constructed it.
ProviderRegistry occurs in that crate only in the module's own import, one signature
and its tests — no production registry, no caller, no path to it from a running node.
It was also the SOLE consumer of chia-query in dig-node-core, and the reason for
the chia-query = "=0.5.1" exact pin the manifest itself named as what held the crate
off the chia 0.36 line. Deleted.

2. A second full peer pool on the live-send path.build_live_wallet called
ChiaQuery::new itself, so a node with DIG_WALLET_ENABLE_LIVE_BROADCAST on held two
independent five-peer pools — two sets of TLS sessions to two independently-chosen sets
of full nodes, each with its own notion of the peak. The wiring now takes the client
from ChainTransport. The second pool is unexpressible, not merely unused:
build_live_wallet has no way to construct one.

Held connections: before → after

beforeafter
ChainTransport pool (serves reads, peak, chia_peer_count)55
live-broadcast pool (when the flag is on)50 — shares the above
chia-peer light client0 (never constructed)removed
quorum corroborator + corroborated-read sampleindependent dialsunchanged

NC-12 plurality survives: still 5. What collapsed is two pools serving the SAME
role. The ChiaQuorumCorroborator and DialedPeerSample keep drawing their own
independently-dialled peers, which is the agreement mechanism — unifying the owner must
not collapse the voices, and it does not.

Version lines — verified from the resolved lock, not the carets

chia-query resolved to two lines (0.5.1 via dig-node-core, 0.6.2 via dig-wallet).
It now resolves once, at 0.6.2, and chia-peer is absent entirely. That is
dig_ecosystem#2227's deliberate split, closed.

dig-chainsource-interface still resolves to two lines (0.1.0, 0.2.0). Not caused by
this PR and not fixed by it
: 0.1.0 arrives via dig-merkle → dig-store, independently
of everything removed here. Reported, not chased — it is a different repo.

What is NOT here, and why

The chia 0.26 to 0.36.1 move is not in this PR — and that is now a deliberate scoping
call, not a blocker.

digstore-chain 0.28.0 / digstore-core 0.28.0 published while this PR was in the gate,
on chia-* ^0.36.1 / chia-wallet-sdk ^0.34, so the original blocker is gone. I measured
the move rather than assuming it, and it is a migration, not a manifest change:

  1. Only 2 of the 7 digstore crates are published.digstore-host, -remote, -stage,
    -crypto, -compiler are not, so this cannot become version deps — all seven have to
    move to the post-PR#50 rev together.
  2. chia = "0.26" cannot be bumped: the chia umbrella crate has NO 0.36 line. Its 55
    published versions step straight from 0.26 to 0.44.0 … 0.47.0. Every chia::* import
    must be rewritten onto individual facade crates — 68 call sites across 20 files, in 7
    sub-namespaces, with chia::bls::SecretKey (11 sites) being exactly where a partial
    rewrite produces an internally-split crate.
  3. It spans 4 crates, not one — 15 declarations across dig-wallet, dig-node-core,
    dig-chat-protocol and dig-node-service, which must move as one set.

Expanding this PR to carry that would trade a green, complete change for a much larger one.
Measured and filed on #308 instead, with the corrected target set (chia-* 0.36.1,
chia-wallet-sdk/-sdk-test/-sdk-utils0.34 — not 0.36, which is minor-incompatible
with what chia-query 0.15 requires).

What this PR contributes to it: the =0.5.1 pin and the chia-peer line that forced it
are gone, and chia-query is down to one resolved line. That was the part that could not be
deferred; the remainder is now one coherent migration with no shim in it.

Blast radius checked

ProviderRegistry, connect_light_client, register_light_client_provider,
submit_spend, confirmation_depth, ChiaPeerSubscriptions,
CHIA_PEER_INDEPENDENCE_GROUP, dig_chainsource_interface — grepped across all crates;
every one had zero references outside the deleted module and its own mod.rs re-export.
Inverse dependency (cargo tree -i) run per resolved-lock version for chia-peer,
chia-query@0.5.1, chia-query@0.6.2, dig-chainsource-interface@0.1.0 and @0.2.0
that is what surfaced the dig-merkle holder above, which the caret declarations do not
show.

Risk note: this removes a public module from dig-node-core (BREAKING CHANGE
footer, minor bump on 0.x). No caller existed in this workspace; an out-of-workspace
consumer of dig_node_core::seams::chia_peer::light_client would break, and there is
none — dig-node-core is not published to crates.io.

Evidence

  • The one-pool test is revert-proved: restoring only build_live_wallet's own
    ChiaQuery::new fails it on the sharing assertion (not on the fixture precondition and
    not on a compile error). It asserts reference-count sharing, then drops the wiring and
    requires the count to return — so it cannot pass on references another holder
    contributed. Asserting the two pools merely agreed would have passed against the
    coincidence this ticket exists to remove.
  • cargo clippy --workspace --all-targets --all-features clean; cargo fmt --check clean.
  • Test counts posted below once the full run returns.

Not proven: no running node was exercised. The held-connection reduction is argued
from construction (the second pool is unexpressible), not observed on a live host. The
default install is unaffected either way, because enable_live_broadcast is default-OFF.

MichaelTaylor3dand others added 5 commits August 24, 2026 08:55
dig-node holds three independent Chia peer-dialling stacks, each with its
own TLS sessions to its own chosen full nodes and its own notion of the
peak, with nothing reconciling them. Unify them onto the one crate that
already owns this, chia_query::peer::light_client.
Lane-opening stub so the work survives a session cap.
Co-Authored-By: Claude <noreply@anthropic.com>
…ond one
A node with live broadcast enabled held TWO independent chia_query peer
pools: `build_live_wallet` called `ChiaQuery::new` itself, so the live
sender and the wallet's chain reads dialled two independently-chosen sets
of full nodes, each with its own notion of the peak and nothing able to
reconcile them.
The wiring now takes the client from `ChainTransport`. The second pool is
unexpressible rather than merely unused: `build_live_wallet` has no way to
construct one. Building the transport before the live wiring is what makes
that possible, so the two are reordered.
This does NOT reduce the plurality NC-12 depends on. The quorum
corroborator and the corroborated-read sample keep drawing their own
independently-dialled peers; what collapses is two pools serving the SAME
role.
Refs: DIG-Network/dig_ecosystem#2761
Co-Authored-By: Claude <noreply@anthropic.com>
…field
The previous shape stored the client on `LiveWallet` purely so a test could
compare pointers. Nothing in production read it, so `-D warnings` failed the
Clippy gate on `field is never read` — and a field that exists only to be
asserted on is dead code wearing a test's clothes.
The property is unchanged and still distinguishing: seed the transport with a
known client, then watch its reference count. A wiring that built its own pool
leaves the count untouched. Dropping the wiring must return the count to its
starting value, so the assertion cannot pass on references some other holder
contributed.
Co-Authored-By: Claude <noreply@anthropic.com>
…ing chia-query
dig-node-core carried a `chia-peer`-backed subscribing light client, meant to
register as a `ChainSourceProvider` into a `chia_query::ProviderRegistry`.
Nothing ever constructed it. `ProviderRegistry` appears in this crate only
inside that module's own import, one signature and its tests; there is no
production registry, no caller, and no path from a running node to any of it.
So it was a third Chia dialling stack on paper only -- and an expensive one.
It was the SOLE consumer of `chia-query` in this crate, and the reason for the
`chia-query = "=0.5.1"` exact pin, which the manifest itself recorded as what
holds dig-node-core off the chia 0.36 line.
Removing it collapses the two `chia-query` lines the process resolved to
(0.5.1 via this crate, 0.6.2 via dig-wallet) down to one. Verified from the
resolved lock, not the caret declarations: `chia-query` now appears once at
0.6.2 and `chia-peer` not at all.
`dig-chainsource-interface` still resolves to two lines (0.1.0, 0.2.0). That is
NOT this crate's doing and does not change here: 0.1.0 arrives via
dig-merkle -> dig-store, independently of anything removed.
The node's real Chia peer tier is dig-wallet's `ChainTransport`, which is
untouched -- this deletes the unreachable twin and leaves the one that actually
serves reads standing. If the subscribing light client is wanted again its home
is `chia_query::peer::light_client`, which `chia-peer` is now a deprecated
re-export facade over.
BREAKING CHANGE: `dig_node_core::seams::chia_peer::light_client` is removed,
along with `connect_light_client`, `register_light_client_provider`,
`submit_spend`, `confirmation_depth`, `ChiaPeerSubscriptions` and
`CHIA_PEER_INDEPENDENCE_GROUP`. None had a caller.
Refs: DIG-Network/dig_ecosystem#2761
Refs: DIG-Network/dig_ecosystem#3152
Co-Authored-By: Claude <noreply@anthropic.com>
Root 0.147.0 -> 0.148.0, dig-node-core 0.55.0 -> 0.56.0, dig-wallet
0.32.0 -> 0.33.0. Minor rather than patch on all three: dig-node-core drops
a public module, and on 0.x a breaking change is a minor bump.
Also corrects the chia-query comment in dig-wallet, which told readers the pin
was deliberately split from dig-node-core's `=0.5.1` and must not be aligned.
That pin no longer exists, so the note now describes state that cannot occur --
and a stale warning against touching something is worse than no note, because
it stops the next reader looking.
Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d

Copy link
Copy Markdown
ContributorAuthor

loop-security — audit IN PROGRESS (interim, not a verdict)

Auditing head efc87f2a7a65ed198db799f64a4f6b50a3ab38e2 against merge-base
251b4c101738daab0f534fda21d768cb63c39090 (the #327 custody-removal commit). 8 files,
+161/-468. Read from git objects only; the shared checkout holds another lane's uncommitted
work and was not touched.

Interim findings so far (each still being pressed):

1. The deleted stack was genuinely unreachable — refuted nothing, confirmed the claim.
git grep over the ENTIRE base tree for connect_light_client, register_light_client_provider,
submit_spend, ChiaPeerSubscriptions, CHIA_PEER_INDEPENDENCE_GROUP, confirmation_depth
returns exactly ONE hit each outside the deleted file itself, and it is the pub use in
crates/dig-node-core/src/seams/chia_peer/mod.rs:19-22 that this PR removes. No trait object,
no #[cfg] arm, no feature-gated caller. Still to check: out-of-workspace consumers of
dig-node-core (it is a pub API surface), and whether the mod tests inside the deleted
file was the only thing keeping it compiling.

2. §908 direction of travel is correct, not merely neutral. The removal DELETES a
submit_spend relay leg from the chain-read seam. Nothing in the diff adds a signer, a
key-holding struct, or a new broadcast route. Still verifying the wallet-side LiveWallet
wiring carries no key.

Still open and being attacked: the independent-dial count before/after (NC-12 plurality),
trust-flag hygiene on the shared client, and the failure DIRECTION of the broadcast path when
the shared client is unavailable.

@MichaelTaylor3dMichaelTaylor3d left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Correctness gate - CHANGES-REQUIRED

Head reviewed: efc87f2 (resolved from the remote, not from the dispatch).

One gating finding, and it is a doc/contract one - the code change itself is correct and I could not break it. Everything the PR claims about the dialling collapse, the plurality, the lock and the revert-proof, I re-measured independently and it holds.

What I verified myself

1. NC-12 plurality survived - independent dial-holders counted by hand.

dial holderbefore (251b4c1)after (efc87f2)
ChainTransport pool (chain.rs:131, lazy, one per transport)11
live-broadcast pool (service.rs:310, flag-on)10 - unexpressible
chia-peer light client0 (never constructed)removed
DialedPeerSample::mainnet() (chain.rs:116)independentindependent, untouched
ChiaQuorumCorroborator::mainnet() (service.rs:244)independentindependent, untouched

Production ChiaQuery::new call sites: 2 -> 1. The remaining occurrences at head are sage/fallback.rs:636 and sage/service.rs:561, both inside #[cfg(test)] modules. Neither the corroborator nor the peer sample takes a ChiaQuery, so nothing routes their reads through the shared client - the two collapsed pools served the same role, and the voices are intact.

2. The deleted stack was genuinely dead, not a rival.git grep at head for ProviderRegistry, connect_light_client, register_light_client_provider, ChiaPeerSubscriptions, CHIA_PEER_INDEPENDENCE_GROUP returns zero production references - only CHANGELOG.md, DEVELOPMENT_LOG.md and SPEC.md prose (see the finding). No trait-object or #[cfg] construction path exists: the only impl lived in the deleted file and nothing named its type. control.rs:3890the_light_client_chain_reads_survive_the_custody_carve_out is a different sense of "light client" (the control-surface wallet reads) and is unrelated.

3. Mutation re-run as written - it fires, and for the right reason. Own worktree, build_live_wallet restored to calling ChiaQuery::new(ChiaQueryConfig::default()) itself:

test ...the_live_wiring_reuses_the_transports_client_instead_of_opening_a_second_pool ... FAILED
panicked at crates/dig-wallet/src/sage/service.rs:590
test result: FAILED. 1 passed; 1 failed; 0 ignored; 607 filtered out

It failed on the sharing assertion at line 590, not on the fixture precondition (before == 2) and not on a compile error - so the revert-proof is not firing for the wrong reason. Restored: 2 passed; 0 failed. The drop-and-recount half is real: the second assertion requires the extra references to vanish with live, so a count inflated by any other holder fails it. Asserting that two pools merely agreed would have passed against the coincidence this ticket exists to remove.

4. Lock collapse holds, read from Cargo.lock and not the carets.

  • chia-query: 0.5.1 + 0.6.2 -> one line at 0.6.2
  • chia-peer: 0.1.3 -> absent
  • dig_ecosystem#2227 is genuinely closed by this - the deliberate two-line split is gone.
  • dig-chainsource-interface still resolves to two lines (0.1.0, 0.2.0); attribution confirmed - the 0.1.0 dependents in the lock are exactly dig-merkle and dig-store, independent of everything removed here. Correctly reported, correctly not chased.

5. The two removed Closes keywords - confirmed, and the highest-value catch in the lane. The body closes nothing and says why for both #308 and #249. Merging the original form would have auto-closed two tickets whose work is unfinished. This must not regress on a later body edit.

The two claims to record, both confirmed:

  • dig_ecosystem#2790 gap-2 is vacuously satisfied. "No path constructs its own peer fabric outside the registry" holds because there is no registry - ProviderRegistry had no production construction site before this PR and none after. A clause that passes because the thing it governs never occurs is vacuous, not satisfied.
  • dig-node#249 prescribed remedy is not executable as written. It says to inject "the node’s provider registry"; no such object exists in dig-node-core. Re-scoping onto ChainTransport::peer_tier().peak_height is the correct reading.

dig-constants check (both questions, per HARD RULE)

  • Does anything here BELONG in dig-constants? No. The diff introduces no shared constant, port, address, asset id or version pin. max_peers: 0 is a test-local fixture value with no second consumer.
  • Should anything here be USING dig-constants? No. The only literals are ChiaQueryConfig fields, owned by the chia-query defaults; importing them would move ownership away from the crate that defines the type.

Out of scope, agreed

The chia 0.26 -> 0.36.1 uplift (blocked on digstore-chain / dig_ecosystem#3149) and the dig-nat / dig-peer-selector deferral (two chia-bls lines in a crate still on 0.26) were both correctly not attempted. Blast radius via grep plus cargo tree -i per resolved-lock version is the sanctioned fallback and is declared in the body; the analysis is sound.

Verdict

CHANGES-REQUIRED, on the single doc-coherence finding below. It is cheap - a section deletion and a paragraph - and it is the one thing that leaves the ecosystem half-migrated: this crate’s own normative SPEC still specifies the seam the code no longer has.

(Recorded as a comment review: GitHub returns 422 for any verdict-bearing review event from the PR author identity, which the loop shares. The open inline thread is what bars the merge.)

Comment threadcrates/dig-node-core/src/seams/chia_peer/mod.rs
Comment threadcrates/dig-wallet/src/sage/service.rs Outdated
@MichaelTaylor3d

Copy link
Copy Markdown
ContributorAuthor

loop-security: PASS

Audited head: efc87f2a7a65ed198db799f64a4f6b50a3ab38e2 (re-resolved from the remote at the end of the audit; unmoved during it). Merge-base 251b4c101738daab0f534fda21d768cb63c39090 — the #327 custody-removal commit. 8 files, +161/-468.

Read entirely from git objects (git show / git diff / git grep <rev>). The shared dig-node checkout was NOT touched — it holds another lane uncommitted work (12 staged/modified paths). No worktree was cut, no mutating git command was run, git fetch only.

Direct answer to the gating question: NO. A single dishonest peer gains no capability it did not have before this PR. Reasoning and the measured counts follow.


Independent Chia dial stacks, measured before and after

Production chia_query::ChiaQuery::new call sites (git grep over the whole tree at each rev; fallback.rs:636 and service.rs:561 sit inside #[cfg(test)] modules and are excluded):

base 251b4c10head efc87f2a
ChainTransport pool (chain.rs:131, max_peers: 5)5 peers5 peers
build_live_wallet own pool (base service.rs:310, max_peers: 5)5 peersgone
ChiaPeerSessionFactory::mainnet (service.rs:240)own dialsown dials, byte-identical
ChiaQuorumCorroborator::mainnet (service.rs:244, QUORUM_SAMPLE = 4)own dialsown dials, byte-identical
DialedPeerSample::mainnet (chain.rs:116)own dialsown dials, byte-identical
dig-node-core chia-peer light client0 — never constructeddeleted

Independent dial stacks 5 to 4. Peer sessions on a live-broadcast node 10 to 5. Every stack carrying an NC-12 agreement property is byte-identical between the two revs — verified by comparing blob SHAs, not by reading the diff:

IDENTICAL crates/dig-wallet/src/sage/quorum.rs
IDENTICAL crates/dig-wallet/src/sage/peer_reads.rs
IDENTICAL crates/dig-wallet/src/sage/sync_supervisor.rs
IDENTICAL crates/dig-wallet/src/sage/rpc.rs
IDENTICAL crates/dig-wallet/src/sage/custody.rs
IDENTICAL crates/dig-wallet/src/sage/fallback.rs

Why the pool that WAS removed carried no agreement property

The chia-query pool is not a voting mechanism. peer/pool.rs:136 select_peer is plain round-robin (next_idx.fetch_add(1) % entries.len()) — one peer answers each read. So pool A and pool B were each single-voice-per-read, and I found no code path anywhere comparing an answer from one against the other. The one composite the supervisor uses, FallbackChainTip::new(fallback) (service.rs:247), was already built on the transport (pool A), never on the live pool; its corroborator is ChiaQuorumCorroborator::mainnet(), which dials for itself.

So: no read previously required two independent sources and now requires one. The second pool was an unreconciled duplicate, not a witness.

The one real delta, and why it is not a gain for the attacker

Pre-PR an attacker needed a slot in pool A to lie about coin reads and a slot in pool B to lie about confirmation/lineage; post-PR one slot in pool A reaches both. That correlation is genuinely new. It does not amount to a gain, for two measured reasons:

  1. select_peer is round-robin over 5, so one dishonest peer answers about 1 in 5 pool reads and cannot choose which. A composite lie (bogus coin read, then false confirmation) requires winning the rotation twice.
  2. The node now makes half the random full-node dials it used to (10 to 5), so the attacker gets half as many chances to be admitted at all. pool.rs admit already rejects a duplicate address, so slots cost distinct addresses either way.

The two costs move in opposite directions and neither dominates. I could not construct a scenario where one dishonest peer does something it previously could not.


The five named attack surfaces

1. The deleted stack was genuinely unreachable — independently confirmed, not taken on trust.git grep --fixed-strings at the BASE rev for connect_light_client, register_light_client_provider, submit_spend, ChiaPeerSubscriptions, CHIA_PEER_INDEPENDENCE_GROUP and confirmation_depth returns exactly one hit each outside the deleted file, and it is the pub use at crates/dig-node-core/src/seams/chia_peer/mod.rs:19-22 that this PR removes. No trait object, no #[cfg] arm, no feature gate. Two things close the loophole a workspace grep alone would leave:

  • The workspace is the whole consumer set.Cargo.toml members are dig-node-core, dig-chat-protocol, dig-node-service, dig-runtime, dig-wallet — all covered by the grep.
  • dig-node-core is NOT published: index.crates.io/di/g-/dig-node-core returns 404. There is no registry consumer, so the removed pub surface had no out-of-workspace reachability either.

This is a dead-code deletion, not a capability deletion on the chain-read path.

2. unexpressible vs unused — the claim is correctly scoped, with one caveat.build_live_wallet(chain: &ChainTransport) (service.rs:318) genuinely cannot construct a pool: it has no config, no constructor, only a borrowed transport. That narrow claim holds. ChainTransport::shared_client is pub(crate) (chain.rs:146) so it does not widen the dig-wallet public surface, and with_client is #[cfg(test)] (chain.rs:154).

Caveat, non-gating: ChainTransport::new() and its Default impl remain pub inside a pub mod chain under a pub mod sage. A second pool is therefore still constructible by a consumer — it is one-per-process by call-site discipline (exactly one production site, service.rs:173), not by construction. The PR text does not overclaim this, but a later reader might; worth a sentence in the PR body.

3. Trust-flag hygiene — clean, and nothing inherits a grant. Both pools used ChiaQueryConfig::default() before and after; the config is unchanged. PeerOrigin::Priority is reachable only from an operator TRUSTED_FULLNODE env var or a co-resident node (peer/connect.rs:210), and independent_peer_count (pool.rs:187) already filters to PeerOrigin::Discovered only. Nothing in the diff sets, forwards or inherits a trusted flag. The chia-peerconfig.trusted escape hatch that eco#2790 says must stay shut is now removed from the tree entirely — the crate is gone from the lock.

4. Failure direction — the money path REFUSES. Correct direction, at file:line.

  • Client unbuildable: chain.rs:127-135 returns Err("no chain source could be reached") and does not cache the failure, so an offline boot is recoverable.
  • service.rs:324-326: Err(e) => { warn_chain_source_unavailable(&e); None } — live wiring disabled, no broadcaster attached.
  • service.rs:283: None => NodeTipSpender::new(spender_backend, None, None) — a tip reports NotExecutable; nothing is spent.
  • Push: ChainTransport::push returns Err when it cannot reach a mempool (a mempool refusal is the Ok(accepted: false) case). Failing to reach is never read as acceptance.

Honest qualifier, so nobody misreads the unification: "its peers disagree" has no branch on this path, before or after. The shared pool does not vote; disagreement detection lives in quorum.rs and peer_reads.rs, both byte-identical and both on their own dials. This PR neither adds nor removes an agreement gate on the broadcast path. Unified onto one pool does not mean corroborated.

5. IPv6-first (CLAUDE.md 5.2) — not implicated. The diff adds no dial path, no dial-back, and no address selection. The surviving dial is the pre-existing chia-query DNS-introducer discovery (peer/connect.rs:140-190), unchanged; and it is the Chia full-node network, not the DIG peer layer that 5.2 scopes to.


Section 908 — the custody boundary holds, and moves in the safe direction

  • crates/dig-wallet/src/sage/custody.rs and rpc.rs are byte-identical to base, so the push_signed_bundle guard (refusing a bundle that spends a coin at a puzzle hash the node holds a key for, while live broadcast is off) is untouched.
  • The diff introduces no signer, no key-holding struct, and no new broadcast route. The only new code is a pub(crate) accessor, a #[cfg(test)] constructor, one moved statement and a changed function signature.
  • It deletes a submit_spend relay leg (light_client.rs) from the chain-read seam. That is a reduction in money-path surface, not an addition.
  • SignedBundlePusher (chain.rs:73) still takes a complete SpendBundle and nothing else.

Dependencies — purely subtractive, no pin loosened

Cargo.lock removes chia-peer 0.1.3 and chia-query 0.5.1, and drops dig-chainsource-interface 0.1.0 from the dig-node-core dep set. No dependency added, no version lowered, no registry+ source changed to git. The chia-query 0.6.2 to chia-query rename is the lock collapsing a now-unique package name. Removing the duplicate line also removes a duplicate chia-wallet-sdk 0.30 / openssl / tokio-tungstenite build.


Non-gating findings (report, do not hold the merge)

S1 — crates/dig-node-core/SPEC.md:402-431 still documents the deleted light client as shipped. Section 4.1a describes a ChainSourceProvider "registered into chia-query aggregating ProviderRegistry at priority 20", states "It is constructed on node start", and documents the submit_spend write leg plus a CHIA_PEER_INDEPENDENCE_GROUP. None of that exists at this head. Security-relevant because a reader auditing the node chain-source trust composition from the normative SPEC would conclude a priority-20 peer provider sits ahead of coinset on a money-path read.

Not a vulnerability, and it was already false at base (nothing constructed the client then either), so it is not a regression this PR introduces — but the PR is already editing the module doc directly above it, so section 4.2 says fix it here. crates/dig-node-core/SPEC.md:1323 (the seam table) and DEVELOPMENT_LOG.md:295 (the =0.5.1 pin rationale) carry the same drift. The correctness gate owns this call; I do not gate on it.

S2 — the unified "one notion of the peak" is a THIRD-PARTY notion (pre-existing, outside the diff).ChainTransport::peak_height calls ChiaQuery::peak_height_opt, which in chia-query 0.6.2 is router.rs:364 to get_blockchain_state, and router.rs:711-717 tries coinset first (api.coinset.org), using the peer-observed peak only as a fallback. So the peak the transport reports on a node holding five peers is a single third-party HTTPS oracle figure.

This does not weaken the PR — both pools already read the peak from that same oracle, which is why they would rarely have disagreed, and the real benefit (halving the dials, removing a second unregistered trust domain) is unaffected. It matters because the PR narrative is "two notions of the peak becomes one"; the one is the coinset notion. Recommend recording it on eco#2790 rather than acting here — it is squarely that ticket "one peak measurement" goal.

S3 — the money-path chain reads remain single-peer and uncorroborated. Round-robin, one peer per read, no agreement threshold on the ChainTransport / broadcast path. Unchanged by this PR and already tracked (eco#2790 gap 2, dig-node#249). Named here so "unified" is not later read as "hardened".


The two claims I was asked to confirm

CONFIRMED — the eco#2790 gap-2 clause is VACUOUSLY satisfied, and must not read as discharged. The clause is "no path constructs its own peer fabric outside the registry". After this PR ProviderRegistry appears nowhere in dig-node except deleted code and stale docs — the PR removes the only registry-capable path the tree ever had. The clause holds because there is no registry to be outside of, which is not a property.

One precision worth putting on #2790: its 2026-08-23 update widened gap 2 to "two chia-query lines means two provider registries". This PR does close that widening — Cargo.lock now resolves chia-query once — but it closes it by deleting the second line, not by unifying into a registry. The original gap 2 (the transport pool is a trust domain the operator provider config cannot reach) stands, and is now the only trust domain. Net: the widening is undone, the gap is not.

CONFIRMED — the dig-node#249 remedy is uncarryable as written. It prescribes "Inject the node provider registry into ChainTransport at chain.rs:104-108". There is no node provider registry — there was none at base either, and this PR removes the last code referencing the type. The remedy needs rewriting from inject the node registry to construct one and inject it. chia_query::provider_registry does ship in the 0.6.2 line dig-wallet is on, so a rewritten remedy is carryable; the one on the ticket is not.


Areas checked, and why each is clear

  • Secrets / credentials — clear. No key, token, projectId or credential added, logged or printed. The only new log line (service.rs:307) is the pre-existing live-broadcast warning, moved verbatim.
  • Custody / privilege — clear. custody.rs and rpc.rs byte-identical; no signer, no key struct, no new broadcast route; a submit_spend leg deleted. Section 908 holds.
  • Input / boundary — clear. No parsing, no deserialization, no path handling, no network input handled in the diff. No new unwrap on adversarial input (the two expect calls are #[cfg(test)]).
  • Crypto / protocol — clear. No downgrade: ChiaQueryConfig::default() unchanged on the surviving path, TLS identity handling untouched, no store-format or wire change.
  • AuthZ / exposure — clear. No new endpoint, RPC method or permission. Tracing every remote entry point: the changed code is WalletService::build_with start-up wiring, reachable only by process start, plus a crate-private accessor. Nothing peer-reachable and nothing anonymously triggerable was added.
  • Amplification / cost asymmetry — improves. Outbound random full-node dials on a live-broadcast node halve (10 to 5); one fewer TLS session set, one fewer background refill loop. No new request-triggered work.
  • Persisted state — clear. No new on-disk state. with_client sets peer_reads: None but is test-only.
  • Dependencies — clear. Purely subtractive; nothing added, nothing loosened, no git source.
  • Test hygiene — clear, and worth stating: offline_client() uses max_peers: 0, and pool.rs:80 (for _ in 0..max_peers) means zero dials and zero DNS. The new tests cannot reach mainnet from CI.

Verdict: PASS. No live security defect in this diff. S1 is a section-4.2 completeness defect for the correctness gate to rule on; S2 and S3 are pre-existing, outside the diff, and belong on eco#2790.

MichaelTaylor3dand others added 2 commits August 24, 2026 11:14
…ired
SPEC.md §4.1a specified the `chia-peer` light-client provider in the present
tense, marked "Boundary (LOCKED, #1314)", instructing a reimplementer to build
a `ProviderRegistry` the crate no longer has -- and contradicting the module
doc added alongside the deletion. A normative sentence that describes absent
code is worse than a missing one: it manufactures false defect reports and has
already caused wrong fixes in this ecosystem. Deleted rather than softened.
DEVELOPMENT_LOG.md carried the same `=0.5.1` blocker text removed from both
Cargo.toml copies. Rewritten rather than deleted, because the durable lesson is
the opposite of what it said: the pin was never load-bearing, and its articulate
comment is exactly what made it look like it was. A blocker is verified against
the resolved lock and a reference search, never against the note explaining it.
Co-Authored-By: Claude <noreply@anthropic.com>
… fmt
`cargo fmt` reflowed the backslash continuations into one long literal and
baked the source indentation into the string, so the sentence a human reads
when the guard fires carried two 14-space runs mid-clause. `concat!` keeps each
piece a separate literal, which fmt cannot join.
Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3d marked this pull request as ready for review August 24, 2026 18:57
@MichaelTaylor3d
MichaelTaylor3d merged commit 4e0c3bb into mainAug 24, 2026
16 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the loop/2761-unify-chia-dialler branch August 24, 2026 18:57
Sign up for freeto 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.

1 participant

@MichaelTaylor3d