Repository navigation
feat: sign LUD-25 address proofs on a pinned ADDRESS PROOF card - #218
Merged
Merged
Conversation
Since LUD-25 50d740a a mint changes or clears a name's cx1 only with a
BIP-340 signature by the purpose-0 index-0 key of the branch on file, so a
name registered on the superseded m/139'/1' branch could not be moved.
heartwood_note_address_proof {host, name, action, cx1} (wire command
cash_address_proof, capability note_address_proof_v1) signs
sha256("LNURLcash:<action>:<domain>:<name>") with whichever of the served
identity's two branches at the mint has that exact cx1, aux_rand zero,
and refuses any other. Graded against all four addressProofs vectors.
Validation runs before the card on the relay precheck and in the
dispatcher alike; the method is pinned ButtonRequired, scoped to the served
key, and an ask never shares a card. Answers the signature and the index-0
pubkey, never a key.
This was referenced Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Since LUD-25
50d740a(moneyer 0.17) a mint changes or clears a name'scx1only with"sig": a BIP-340 signature by the purpose-0 index-0 key of the branch CURRENTLY on file, oversha256(utf8("LNURLcash:<register|unregister>:<domain>:<name>"))(moneyerregisterName/addressProofVerifies). The board could not make that signature, so a name registered on the supersededm/139'/1'branch could not be moved to the current one.What
common/src/cash_key.rs:AddressAction,BranchKind,valid_username(moneyer'sNAME_RULE, exact, never folded),address_proof_digest,sign_address_proof(purpose-0 index-0 key, aux_rand zero),branch_kind_of, andaddress_proof_for(identity, host, cx1, action, username), which signs with whichever of this identity's two branches athosthas that exactcx1(compared as decoded bytes, so all-uppercase bech32m matches) and refuses any other. Domain isspend_domain(host).heartwood_note_address_proof, params[{host, name, action, cx1}], wire commandcash_address_proof. Answers{ok, host, domain, name, action, cx1, branch, sig, pubkey}: the 64-byte signature hex and the index-0 x-only pubkey, never a key.note_cmd::address_proof_ask) shared by the relay precheck and the dispatcher: valid mint host, name rule, action,cx1decodes,cx1is one of ours.note_cmd::address_proof_card, two lines):ADDRESS PROOF/<name>@<domain>/<action>, current keysor<action>, old keys. The address is middle-elided past one card line (12..8, as the trust and mint cards do).always_requires_button+is_note_method),method_uses_served_key, advertised inheartwood_capabilitiesand asnote_address_proof_v1in get_status.notes::relay_card/relay_prechecknow take the served key. relay.rsnote_card_headergives the deferred card its header.approval_queue::never_shares_card/unshared_kind_key: an address-proof ask never joins another ask's card. Without that, two proofs from one client would collapse and drawnote_batch_card("ADDRESS PROOF 2 NOTES / 0 sats"), and one hold would sign a proof the owner was never shown.cash_address_proofrefuses before the card, ascash_addressdoes. The hook is still wired to draw the same card.Vectors
All four
addressProofsinlud25-part2.jsonmatch: digest, the index-0 key (= mint.example branch purpose 0 index 0), pubkey, and the signature byte for byte with aux_rand zero. That is what the conformance generator uses (SCHNORR_AUX = new Uint8Array(32)). Graded on both backends.Trade-offs worth a look
verdict_may_answer_card()(pinned and not device-press-only), and the guardian's notice carries the same card text. That keeps it consistent with the note set. A proof is a reusable, non-expiring signature for one (action, domain, name), but on its own it does nothing for an existing name: moneyer also wants the owner's NIP-98, which the device signs on its own card. For a fresh name, a leaked register proof can only point a free name at this owner's own branch. If you'd rather it were device-press-only, it's a one-line change indevice_press_only.show_master_sign_requestcuts the preview to one small line's worth of characters, so the address shows but the action is cut toregi.... Every note card is already cut this way on that path (trust loses "notes skip the hold"), so it isn't a regression. A follow-up could put the titled card on that path for all note cards.unauthoriseduntil its policy compiler (Notecase/Signet) addsheartwood_note_address_proof. Legacy slots get the card.address_proof_askderives both branches, and a relay request runs it on the precheck,relay_cardand the dispatcher, so a card costs a handful of BIP-32 walks. That's fine at the relay loop's pace.errorcode (bad_request) reaches the client, so Notecase cannot tell "not our branch" from a bad name. The message is in the JSON on the cable surface.heartwood_note_trustasks from one client for different keys can collapse onto one card, which then draws as a note batch ("TRUST SENDER 2 NOTES / 0 sats"). The samenever_shares_cardwould fix it.Tests
cargo test --manifest-path common/Cargo.toml --no-default-features --features mnemonic-gen,cash: 300 passedcargo test --manifest-path common/Cargo.toml --features nip44,nip46,nip04,ota-sign,device-identity,seed-encrypt,cash,compact-hashes: 964 passedui-preview: 22 passed. It has no coverage of the titled note cards, so nothing was added there.heartwoodd: 52 passedfirmware-build.Not flashed, not bench-run.