Skip to content

a zap request with a comment loses its description_hash #2570

Description

@davotoula

Observed: 2026-08-30, reproducible on demand at the time of writing.

Reported by: the operator of a self-hosted NWC wallet service that verifies, before storing it, that a client-supplied zap request actually belongs to the invoice its node paid.

Summary

The callback mints a correct NIP-57 zap invoice when the zap request's content is empty, and an ordinary comment invoice with no description_hash at all when content is non-empty. The zap request is otherwise identical: same signer, same p, amount, relays and lnurl tags, same amount parameter.

Since NIP-57 binds a zap receipt to its invoice through description_hash = sha256(zap request JSON), an invoice without that field cannot carry a verifiable zap. The commitment is simply absent.

Reproduction

Two requests differing only in the content field of the zap request.

PK=...   # the address owner
LN=lnurl...
CB=https://getalby.com/lnurlp/.../callback

# A — comment-free zap request (content: "")
ZR=$(nak event -k 9734 -c "" -t p=$PK -t amount=28000 -t relays=wss://nos.lol -t lnurl=$LN --sec <key>)
curl -s "$CB?amount=28000&nostr=$(urlencode "$ZR")"

# B — identical, except content: "Nice test"
ZR=$(nak event -k 9734 -c "Nice test" -t p=$PK -t amount=21000 -t relays=wss://nos.lol -t lnurl=$LN --sec <key>)
curl -s "$CB?amount=21000&nostr=$(urlencode "$ZR")"

Decode each returned pr with lncli decodepayreq or any bolt11 decoder:

A — content ""            description:      ''
                          description_hash: 01d653fcf76be1f0ed0ebb5fdac84e38dfd17a22f4c099eb13ae0f339d28282f
                          sha256(zap request bytes sent):
                                            01d653fcf76be1f0ed0ebb5fdac84e38dfd17a22f4c099eb13ae0f339d28282f
                          -> CORRECT: commits to the exact zap request

B — content "Nice test"   description:      'Nice test'
                          description_hash: (absent)
                          -> the comment was lifted out of the zap request and used as the
                             invoice memo, and no commitment was made

Note that no comment query parameter was sent in either case. The text came only from the zap request's own content, so the fallback is triggered by parsing the zap request, not by the LUD-12 comment parameter.

The endpoint advertises zap support in both cases:

allowsNostr: true
nostrPubkey: ...
commentAllowed: 255

Expected

Per NIP-57, when a valid nostr parameter is supplied, the invoice's description_hash must be the SHA-256 of that zap request JSON, whether or not the request carries content. A comment in a zap request is normal: it is how a zap's message is conveyed, and it ends up in the zap receipt's content.

Impact

Zaps carrying a message cannot produce a verifiable zap receipt for addresses on this service. Anything that checks the binding must reject them; anything that does not check is accepting an unbound claim.

It is silent. The payment succeeds, the invoice looks normal, and only a verifier notices.

Zaps without a message are unaffected, which makes the failure look intermittent and user-specific. In our case it presented as "the default zap works, but a zap with a comment shows no sender".

How it was found

Five real payments from a phone wallet to Alby-hosted addresses, all with comments, were refused by our node because the invoice had nothing to bind the zap request to. Isolating the variable took the two curl calls above: it is the comment, not the pairing, the client, or the amount.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions