checksum: signal the method, weight by position, reduce mod 31 #21
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "refs/pull/21/head"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes three defects in the terminal checksum, each of which let a corrupted phrase validate. Verified across the spec, the Guile reference, and the web app.
What was wrong
Dual acceptance. The spec required a validator to compute both the CRC-8 and the additive checksum and accept either. Two independent chances to pass roughly doubles the residual false-negative rate — measured 6.01% vs 3.03% over 200k random substitutions.
Transposition was never detected. The additive checksum was a plain letter sum, which is order-invariant, so every permutation of a phrase validated. Measured detection: 0.00%. This sat directly on the emergency-radio path the additive method exists to serve.
The modulus was wrong. With position weights
1..6,mod 32leavesgcd(2,32)=2,gcd(4,32)=4,gcd(6,32)=2, so a substitution in the 4th word went undetected 12.65% of the time.The fixes
MUST accept eitheris replaced by an explicitMUST NOT.index = (Σ i·lettersum(wordᵢ)) mod M. Transposition detection goes 0% → ~97%.M = 31. Every weight1..6and every weight difference is invertible, so no hole exists to move. (All-odd weights fix substitution but make every difference even, relocating the hole onto transposition.)Detection is now ~3.2%, uniform across substitution, transposition and omission, at every word position.
Vocabularies rebuilt
The old set was screened for spelling disjointness only. 23 of its 32 words sat within one phoneme of a BIP-39 location word — including
gray/grape, andgrapeis the location word in every worked example — plusquail/whaleandseal/sea.BIP-39's four-letter identity rule is deliberately not applied. It bundles a written hazard (
crow/crowd, which a phoneme test misses) with a spoken one (stork/story, which a prefix test misses), catches neither cleanly, and across six Latin-script wordlists rejects Romance cognates likeemeraldandcrimsonthat are neither. SayWhere never truncates a word, so the rule buys nothing and costs most of the usable vocabulary. It's replaced by an explicit prefix rule and a phoneme-distance rule, plus spelling and familiarity criteria.scripts/checksum_vocab.py(new) builds, audits and verifies the vocabularies against all ten official BIP-39 wordlists, the CMU Pronouncing Dictionary, and a spoken-English frequency corpus. It reports 0/32 and 0/31 failures for the adopted sets, and scores the superseded ones so the regression is visible.Verification
CHECKSUM_ADD_31index-for-index.Known residual, documented on purpose
sum("column") − sum("grape") = 31exactly, so swapping the first two words shifts the index by0 mod 31andcolumn.grape.hip.tremorvalidates. It's the 1-in-31 residual landing on our own example. Rather than switch tomod 29to make the demo look clean, the spec carries it as a labelled test vector and Rust pins it as a test. A terminal checksum reduces the chance of accepting a corrupted phrase; it does not eliminate it.Breaking change
Location-only phrases are unaffected (
grape.column.hipstill resolves). Previously-shared checksummed phrases break:grape.column.hipgrape.column.hip.toadkit.puzzle.rubyrubymoved index; CRC is nowburgundyThe
kit.puzzle.rubycase surfaces as "checksum failed" rather than "unknown word", which reads like corruption rather than a spec revision. Worth a note in release comms.Canonical example is now
grape.column.hip.hermit(CRC-8) orgrape.column.hip.tremor(additive).Scope note
This branch also carries two pre-existing UI commits (
171ee6amobile wayfinding overlay,bf0a689onboarding card) that predate the checksum work and were already onorigin/geo-qr-pin. They touchweb/index.htmland the UI layer ofweb/js/app.js, not the checksum layer.Companion PR:
anuna/saywhere-rs—checksum-and-altitude-conformance.🤖 Generated with Claude Code
https://claude.ai/code/session_017R8AwqxwAq8Av8GMZvLEPT