Skip to content

wallet: Ask for the record fingerprint before the cards - #104

Draft
BenWestgate wants to merge 13 commits into
codex/30-existing-fingerprint-before-sharesfrom
claude/new-issue-fixes-fvhbl3-91
Draft

BenWestgate wants to merge 13 commits into
codex/30-existing-fingerprint-before-sharesfrom
claude/new-issue-fixes-fvhbl3-91

Conversation

@BenWestgate

Copy link
Copy Markdown
Owner

Requested by Ben · project thread

Before: ms32 wallet and ms32 create --existing hid the recovered master fingerprint while a correction was being confirmed (#57). The wallet record was typed only after the cards, so a wrong correction was caught after the operator had accepted it.

After: both commands ask for the record fingerprint first, before the cards in wallet and before the secret in create --existing. A suggested correction that completes the secret says "Master fingerprint matches your wallet record" or "does not match", without showing the value, and the record picks between equally likely corrections. The final identity check, the re-ask on mismatch and Enter for no record behave as before. With no record, nothing is shown until the recordless gate. Ctrl-C at the moved prompt still says the existing cards are valid.

How: cli._record asks for the record and is called before input. _recorded_fingerprint replaces the post-input prompt with the verify-and-re-ask loop. _cli_input._completed provisionally recovers the secret when the candidate is the last needed ms share, and _fingerprint_matcher and _confirm_correction compare it with the record. The user guide's restore step and create --existing paragraph follow the new order.

Needs a security review before ready (recovery). What to check: the record preview prints only matches or does not match, never the fingerprint (test_record_preview_and_ranking_never_show_the_fingerprint, test_wallet_checks_a_corrected_final_share_against_the_typed_record). The provisional secret is used only for that comparison, and a candidate the operator accepts still goes through verify_identity before Bitcoin Core is changed. The record is the last tie-breaker in _best, after rank, Hamming weight and CRC padding, so it can only choose among equally likely corrections and never promotes a less likely one.

Budget: the first commit raises it from < 5200 to < 5250 at Ben's request (tests/test_cli.py, docs/developer/api.md, AGENTS.md). This branch is at 5224. On #98's tip it merges cleanly at 5210, and 5217 with #99 and #100. 936 tests pass; ruff, mypy and the touched files under -O are clean.

Conflicts with #101 in two _confirm_correction calls, where each PR adds one argument. Keep both: _confirm_correction(candidates[0], accepted, basis, str(error), fingerprint, record), and the same in _creation_source.

Closes #91

🤖 Generated with Claude Code

https://claude.ai/code/session_015CuLXqAvAovfoVcUmmogwa


Generated by Claude Code

BenWestgate and others added 13 commits September 30, 2026 21:35
Apply the established majority-case interpretation to standalone correction while preserving immutable context, entered edit semantics, and disclosure accounting. Account the normalized retry frontier even when the first optional search reaches its deadline after finding a candidate, so cumulative capture mass remains fail-closed.

Fixes #37.
Give standalone correction a stable status contract: 0 for already-valid input, 1 when a suggestion is emitted, 2 for command or input syntax errors, and 3 when no usable suggestion is emitted. Keep incomplete best-effort suggestions at status 1 and document status 3 only for incomplete searches without a usable suggestion.

Fixes #39.
Remove unreachable creation guards and the permanently false correction ambiguity field, align the CLI test Core stub with production, and move reference-only correction helpers out of the installed package.\n\nSecurity: fail-closed correction and wallet behavior are unchanged.\n\nRefs #38.
Gate restore and existing-seed wallet initialization on the independently recorded BIP32 master fingerprint before any Bitcoin Core wallet mutation. Keep the correction path from disclosing or reusing a fingerprint derived from the candidate being authenticated.

Fixes #30.
When RIPEMD-160 is unavailable, a valid standard Bails identifier cannot be checked. Preserve the Bails-alpha SHA-256 result and distinguish that inconclusive state from a completed identifier mismatch, so the no-record restore prompt does not claim the cards are wrong. Keep the independent fingerprint and explicit operator-confirmation boundary unchanged.

Refs #79
The no-record restore flow can no longer claim a standard Bails identifier mismatch when RIPEMD-160 is unavailable. Record that platform-dependent inconclusive outcome in both the security model and invariant so reviewers can distinguish it from a completed comparison.

Refs #79
An existing hex seed or codex32 master secret previously reached the wallet-record fingerprint check only after new recovery cards had been generated and confirmed. Check the typed record immediately after parsing the source, before any card output or ceremony. Preserve the explicit recordless path at the same early decision point, and pass the checked result through to wallet initialization so it is not prompted twice. Cover matching, mismatching, and recordless flows for both source encodings. Refs #30.
Translate Ctrl-C or EOF at the early wallet-record gate for ms32 create --existing into the existing wallet-setup interruption path. This keeps an operator from being told to invalidate a pre-existing recovery card before any new share ceremony has started.

Add a focused regression proving the interruption occurs before share creation or output and preserves the valid-backup message.
Reassign the expected_fingerprint argument instead of copying it into a
local, and give existing_secret its None default before the source
checks instead of in an else branch.

Behavior is unchanged. The installed package drops from 5161 to 5159
logical review lines, which keeps the integrated #7/#42/#57/#46/#80/#81
tip under the <5200 budget.

Security: the record gate still runs before any card is generated or
shown, and interrupts at that gate still raise _WalletSetupInterrupted.

Validation: ruff check, ruff format --check, mypy src/codex32, and
pytest (918 passed, with and without -O).

Refs #81, #38.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018az69UX4773mYohXAtE8kD
A raw seed imported with create --existing was assigned a temporary random identifier for the no-record safety screen, then assigned a different random identifier when the new share set was created. Reuse the first identifier as the share-set identifier so the safety screen describes the backup that will actually be produced.\n\nExtend the recordless-creation regression to require the displayed, emitted, and imported identifiers to agree.\n\nRefs #30
Backup creation rejects a noninteractive terminal before this branch, so the later stdin.isatty() rejection can never run. Removing it preserves the interactive behavior and leaves the integrated source under its strict review line budget. Refs #46 and #81.
Ben authorized raising the budget so #91 fits. The stack tip with the
open fix PRs was at 5,197 of 5,200, and #91 adds 26 lines. Update the
enforcing test and both places that document the number.

Claude-Session: https://claude.ai/code/session_015CuLXqAvAovfoVcUmmogwa
`ms32 wallet` and `ms32 create --existing` hid the recovered master
fingerprint while confirming a correction (#57), so a wrong correction
was caught only after the operator accepted it and typed the record.

Ask for the record first: before the shares in `ms32 wallet` and before
the seed in `ms32 create --existing`. A correction that completes the
secret then says whether it matches the record, without showing the
fingerprint, and the record picks between equally likely corrections.
The final identity check, the retry on mismatch and the Enter path for
no record work as before; without a record nothing is shown until the
recordless gate. Ctrl-C at the moved prompt still says the existing
cards are valid.

Closes #91

Claude-Session: https://claude.ai/code/session_015CuLXqAvAovfoVcUmmogwa
@BenWestgate BenWestgate self-assigned this Oct 1, 2026
@BenWestgate
BenWestgate force-pushed the codex/30-existing-fingerprint-before-shares branch 4 times, most recently from 9f88b21 to 1d5b6f5 Compare October 2, 2026 08:59

This branch has not been deployed

No deployments
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.

2 participants