Skip to content

feat(wallet)!: move the reservation codes off -32044, which dig-node already owns - #28

Merged
MichaelTaylor3d merged 1 commit into
mainfrom
fix/error-code-32044-collision
Aug 25, 2026
Merged

feat(wallet)!: move the reservation codes off -32044, which dig-node already owns#28
MichaelTaylor3d merged 1 commit into
mainfrom
fix/error-code-32044-collision

Conversation

@MichaelTaylor3d

@MichaelTaylor3dMichaelTaylor3d commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Outcome

-32044 meant two opposite things across the repo boundary. The contract moves; dig-node does not.

codesymboldisposition
-32044WALLET_NODE_SPEND_DISABLEDterminal — registered here with dig-node's semantics
-32046WALLET_COINS_RESERVEDtransient wait — was -32044
-32047WALLET_RESERVATIONS_UNAVAILABLEunknown — was -32045

This reallocation is deliberately NOT additive, and should not read as a routine bump: the
numeric wire codes of two codes published in 0.20.0 change. That is the right trade because 0.20.0
is ~40 minutes old with zero implementors (grepped: neither symbol nor either numeric appears in
dig-node or dig-app, both of which pin 0.17/0.19), while dig-node's -32044 is live on every
default install and its refusal is a custody decision — the node relays bundles others signed,
but spending its own coins is default-OFF, because a caller could otherwise sign through the node
and hand the bundle straight back. Leaving -32044 ambiguous is worse than renumbering two codes
nothing implements.

-32045/-32046/-32047 are all unused in dig-node (checked against every -320xx literal in
dig-node/crates/); -32045 is vacated only so the pair stays adjacent as one coherent block.

WALLET_NODE_SPEND_DISABLED's wording is copied from dig-node SPEC.md:3027 so the two catalogues
agree verbatim on the remedy.

The root cause, and the durable half

dig-node minted a code inside the shared -3204x range that was never declared in the shared
catalogue
, so nothing could see the clash at allocation time.

  • every_catalogued_code_is_numerically_unique — pairwise across the whole catalogue, plus a
    from_code round-trip. The pre-existing per-variant assertions only compared ONE code against the
    rest, so a collision between two codes neither of which was under test was invisible to them.
  • a_terminal_custody_refusal_and_a_transient_wait_never_share_a_code — pins each number
    together with its disposition, so a future re-collapse is visible. Asserting only that the
    numbers differ would pass for a catalogue that had quietly given both variants the same meaning.
  • The range-ownership rule is now normative in SPEC.md §5 and the error.rs module docs: the
    -3204x band is owned by this document, and a node or client MUST NOT mint into it privately.
  • SPEC.md also no longer claims "all four wallet codes mean the answer is UNKNOWN" — false since
    0.20.0 and doubly so now. It states per-code dispositions and requires branching on the symbol,
    not the band.

Guard proof (a uniqueness test that passes against the colliding version proves nothing). With
WalletRateLimited moved to -32044, the named test fails with the exact clash:

thread 'kats::every_catalogued_code_is_numerically_unique' panicked at src/kats.rs:1965:
assertion `left != right` failed: WALLET_RATE_LIMITED and WALLET_NODE_SPEND_DISABLED both claim -32044
test result: FAILED. 0 passed; 1 failed; 151 filtered out

Reverted; tree restored to the committed state before finalizing.

Expressibility, and what dig-node owes

Only half the guard is expressible here. This crate cannot see dig-node's sources, so uniqueness
within the catalogue is all a test here can assert; the cross-repo half is the range-ownership
rule, which only dig-node can honour. dig-node owes the reciprocal check — assert every
-3204x it mints is declared in this crate's ControlErrorCode::ALL, which it can express because
it already depends on this crate. Related finding (comment, not a ticket): dig-node also mints
-32033 CONTROL_INGRESS_LIMITED and -32060 PEER_PING_REFUSED, plus -32001..-32017 and
-32050..-32052, none declared here. Those are outside -3204x and none currently clash, so they
are not fixed in this PR.

Blast radius checked

Grep-based (gitnexus fallback, per §2.0 bound 2 — a ~10-minute per-worktree analyze is not
justifiable on a lane two others are blocked on, and the radius here is fully enumerable). Targets:
ControlErrorCode::{code,name,origin,description,ALL} and the two moved variants. Every reference
in-crate: error.rs, kats.rs, and doc-links in params.rs/results.rs/traits.rs/method.rs
(symbolic only, no numerics). Out of crate: zero — no consumer references either symbol or
either numeric.

Verification

  • cargo test --all-features152 lib passed, 9 doc passed, 0 failed (baseline 150 + 9; +2 are
    the new guards). Parsed from the test result: lines, one cargo process at a time.
  • cargo clippy --all-features --all-targets -- -D warnings — clean.
  • §2.4b is a no-op, confirmed not assumed: Cargo.toml declares no dig-* and no chia-*
    dependency (only serde/serde_json/semver/async-trait/futures); there is no
    package.json, so no @dignetwork/* deps either.

Version

0.20.0 → 0.21.0, titled feat(wallet)!:. Note this crate's cliff.toml does not render
breaking as breaking (#26), so the ! is for git log and this body, not the changelog.

Progress: https://github.com/DIG-Network/dig_ecosystem/issues/3127

…already owns
`-32044` meant two OPPOSITE things. This contract minted it in 0.20.0 as
`WALLET_COINS_RESERVED` ("this is a wait -- retry"); dig-node has shipped it on
every default install as `WALLET_NODE_SPEND_DISABLED` ("retrying cannot help").
A client conflating them either retries forever against a permanent refusal or
abandons a transient one.
The contract moves and dig-node does not. dig-node's meaning is live on every
install and its refusal is a custody decision (dig-node SPEC 18.12); this
crate's is 40 minutes old with ZERO implementors, so moving it costs nobody.
-32044 WALLET_NODE_SPEND_DISABLED registered, with dig-node's semantics
-32046 WALLET_COINS_RESERVED was -32044
-32047 WALLET_RESERVATIONS_UNAVAILABLE was -32045
BREAKING CHANGE: the numeric wire codes of `WalletCoinsReserved` and
`WalletReservationsUnavailable`, published in 0.20.0, change. Deliberately NOT
additive: leaving `-32044` ambiguous is worse than renumbering two codes that
nothing implements yet.
Root cause, and the durable half: dig-node minted a code INSIDE the shared
`-3204x` range without declaring it here, so nothing could detect the clash at
allocation time.
@MichaelTaylor3d
MichaelTaylor3dforce-pushed the fix/error-code-32044-collision branch from ca62677 to a5be663CompareAugust 25, 2026 01:42
@MichaelTaylor3dMichaelTaylor3d changed the title fix(error): resolve the -32044 collision with dig-nodefeat(wallet)!: move the reservation codes off -32044, which dig-node already ownsAug 25, 2026
@MichaelTaylor3d
MichaelTaylor3d marked this pull request as ready for review August 25, 2026 01:45
@MichaelTaylor3d
MichaelTaylor3d merged commit eca2d91 into mainAug 25, 2026
9 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the fix/error-code-32044-collision branch August 25, 2026 01:45
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