Skip to content

feat: verify the Tari payout address checksum host-side, both forms - #882

Merged
VijitSingh97 merged 1 commit into
develop-v2from
fix/845-tari-checksum
Aug 4, 2026
Merged

feat: verify the Tari payout address checksum host-side, both forms#882
VijitSingh97 merged 1 commit into
develop-v2from
fix/845-tari-checksum

Conversation

@VijitSingh97

Copy link
Copy Markdown
Collaborator

Closes #845

What broke

The Monero half of this gate shipped in #846; tari.wallet_address still had no equivalent. A well-formed Tari address with one mistyped character sailed through setup, the wizard, and the control channel — and the first honest verdict came from the Tari node at merge-mine time, or never visibly at all. Merge-mine rewards silently lost.

The fix — one gate, all surfaces (the #846 pattern)

tari_address_type verifies the address the way the Tari node does: both forms (base58 with the network and features bytes each encoded alone, then the rest; the 256-emoji alphabet), both layouts (35-byte single, 67-byte dual plus up to 256 payment-id bytes), the 1-byte DammSum checksum, the mainnet network byte, and the feature bits — mirroring from_bytes in tari's own source. A checksum-valid address for a Tari testnet gets its own verdict and message (a real address from the wrong wallet is not "invalid", it's the wrong network). No usable python3 degrades to unchecked — accepted, the pre-gate behaviour, never a false reject.

Every config path already routes through parse_and_validate_config, so one implementation covers setup/apply, the appliance wizard spool, and the dashboard control runner. The interactive wizard additionally gets the same inline retry the Monero question has, and doctor gets the verdict.

Provenance — nothing invented

  • The DammSum algorithm, address layouts, base58 scheme, network bytes, and feature bits were all read from tari-project/tari source (dammsum.rs, tari_address/mod.rs, dual_address.rs, single_address.rs, network.rs).
  • The 256-emoji alphabet was extracted programmatically from emoji.rs and spliced in by script — never transcribed by hand — then re-verified char-for-char after embedding. (Split across short concatenated source lines: Python ≤3.9's tokenizer rejects a >1 KB non-ASCII line, which would have silently degraded old-python hosts to unchecked.)
  • The checksum-valid fixtures are the dual mainnet address hardcoded in tari's own test suite and its byte-derived emoji form; the invalid-emoji and bad-checksum fixtures are also tari's own test vectors. Derived fixtures (single form, testnet, bad feature bits) reuse the reference keys with recomputed checksums.
  • Additionally verified against the bench appliance's live, payout-proven mainnet address (accepted; one flipped character fails as checksum). The address itself stays out of the repo.

Tests (stack suite, owning tier)

11 new tari_address_type unit assertions covering every verdict, plus apply-abort tests for the checksum and wrong-network cases. Suite fixtures that used placeholder Tari addresses ("T", TARITARI…, wizard answers) now use the reference-blessed vectors — the same swap #846 made for the Monero dummies. Full suite: 2130 passed, 0 failed; make test green end-to-end; make lint green. Patch coverage is vacuous by construction (bash — the stack suite owns it).

Adversarially verified in a fresh context: DammSum mask cross-checked (a wrong mask fails the reference vectors), fail-open path exercised with python3 removed, gate ordering (whitespace → placeholder → checksum) confirmed, no remaining placeholder fixtures on parse paths.

🤖 Generated with Claude Code

The Monero half of this gate shipped earlier: a well-shaped but
checksum-invalid address is rejected before anything launches. The Tari
payout address still had no equivalent — one mistyped character sailed
through, and the first honest verdict came from the Tari node at
merge-mine time, or never visibly at all.

tari_address_type mirrors tari's own from_bytes: Bitcoin base58 (network
and features bytes each encoded alone, then the rest) or the 256-emoji
alphabet, then length (35 single / 67..323 dual with payment id), the
1-byte DammSum checksum, the mainnet network byte, and the feature bits.
The algorithm, layouts, alphabet, and every test vector come from the
tari repository's own source and test suite; the alphabet was extracted
programmatically, never transcribed. A checksum-valid address for a Tari
testnet gets its own verdict and message. No usable python3 degrades to
"unchecked" — accepted, never a false reject.

One gate, all surfaces: parse_and_validate_config (setup/apply, the
wizard spool, the control runner all route through it), an inline retry
in the interactive wizard, and a doctor verdict. Suite fixtures that
used placeholder Tari addresses now use the reference-blessed vector.

Verified additionally against the bench appliance's live payout-proven
mainnet address (accepted; one flipped character fails as checksum) —
the address itself stays out of the repo.

Closes #845

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@VijitSingh97
VijitSingh97 merged commit 52ae9fe into develop-v2 Aug 4, 2026
15 checks passed
Sign up for free to 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