A hashcash stamp is spent before the request can still fail, so a refused request costs the client a new proof #158

Closed
opened 2026-10-20 17:14:53 +02:00 by clawbot · 0 comments
Collaborator

handleCreateSession in internal/handlers/api.go calls validateHashcash first, and Validator.Validate (internal/hashcash/hashcash.go) records the stamp as spent as soon as it is valid. The nick check, the username check and CreateSession's unique-nick check come after. A request answered 400 invalid nick format or 409 nick already taken has spent its stamp, and the same stamp retried gets 402 stamp already used: at the default 20 bits the user pays another half second to two seconds of proof-of-work for a nick collision. On a +H channel, checkChannelHashcash (internal/service/service.go) records the stamp before the membership, ban and +m checks in SendChannelMessage, so a message refused with 404 has spent a stamp on nothing. README.md (Why Hashcash) says a legitimate user "pays once"; the error tables say nothing about a refused request consuming the stamp. Verified by reading the order of calls; cost to the client, not corruption.

Fix: check the proof first and claim the stamp last. For session creation, split Validate into a check and a claim, claim just before CreateSession and release the claim if the insert fails; for channel messages, do the membership, ban and +m checks before the claim. With #150, which makes the claim the replay check, the claim is then the last step before the message is stored.

Definition of done

  1. A POST /api/v1/session refused with 400 or 409 leaves its stamp usable, and the same stamp then creates a session.
  2. A +H PRIVMSG refused for membership, ban or +m leaves its stamp usable.
  3. Tests for both.
  4. One reviewed PR against next.

Model: fable-5-1

`handleCreateSession` in `internal/handlers/api.go` calls `validateHashcash` first, and `Validator.Validate` (`internal/hashcash/hashcash.go`) records the stamp as spent as soon as it is valid. The nick check, the username check and `CreateSession`'s unique-nick check come after. A request answered `400` `invalid nick format` or `409` `nick already taken` has spent its stamp, and the same stamp retried gets `402` `stamp already used`: at the default 20 bits the user pays another half second to two seconds of proof-of-work for a nick collision. On a `+H` channel, `checkChannelHashcash` (`internal/service/service.go`) records the stamp before the membership, ban and `+m` checks in `SendChannelMessage`, so a message refused with 404 has spent a stamp on nothing. `README.md` (Why Hashcash) says a legitimate user "pays once"; the error tables say nothing about a refused request consuming the stamp. Verified by reading the order of calls; cost to the client, not corruption. Fix: check the proof first and claim the stamp last. For session creation, split `Validate` into a check and a claim, claim just before `CreateSession` and release the claim if the insert fails; for channel messages, do the membership, ban and `+m` checks before the claim. With https://git.eeqj.de/sneak/neoirc/issues/150, which makes the claim the replay check, the claim is then the last step before the message is stored. ## Definition of done 1. A `POST /api/v1/session` refused with 400 or 409 leaves its stamp usable, and the same stamp then creates a session. 2. A `+H` PRIVMSG refused for membership, ban or `+m` leaves its stamp usable. 3. Tests for both. 4. One reviewed PR against `next`. Model: fable-5-1
clawbot self-assigned this 2026-10-20 17:14:53 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/neoirc#158