Spend a channel hashcash stamp in one statement (closes #150)
check / check (push) Waiting to run
check / check (push) Waiting to run
checkChannelHashcash looked a stamp up with one statement and recorded it with another, so concurrent requests carrying the same stamp could all pass the lookup before any of them recorded it, and each message was sent. RecordSpentHashcash now reports whether its INSERT OR IGNORE added a row, and the message is refused when it did not. A single statement is atomic in SQLite. IsHashcashSpent had no other caller and is removed. A failed write of the stamp now refuses the message instead of being logged and ignored. Model: opus-5-5
This commit was merged in pull request #163.
This commit is contained in:
@@ -1285,8 +1285,9 @@ func (s *Service) requireOper(
|
||||
// checkChannelHashcash returns ERR_CANNOTSENDTOCHAN unless
|
||||
// the channel has no +H, or meta carries a hashcash stamp
|
||||
// for this channel and body with enough bits that has not
|
||||
// been used before. A stamp that passes is recorded as
|
||||
// spent.
|
||||
// been used before. Recording the stamp as spent is the
|
||||
// check that it is unused, so a stamp sent in several
|
||||
// concurrent requests passes in only one of them.
|
||||
func (s *Service) checkChannelHashcash(
|
||||
ctx context.Context,
|
||||
chID int64,
|
||||
@@ -1324,12 +1325,12 @@ func (s *Service) checkChannelHashcash(
|
||||
|
||||
stampKey := hashcash.StampHash(stamp)
|
||||
|
||||
spent, err := s.db.IsHashcashSpent(ctx, stampKey)
|
||||
recorded, err := s.db.RecordSpentHashcash(ctx, stampKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check spent hashcash: %w", err)
|
||||
return fmt.Errorf("record spent hashcash: %w", err)
|
||||
}
|
||||
|
||||
if spent {
|
||||
if !recorded {
|
||||
return &IRCError{
|
||||
irc.ErrCannotSendToChan,
|
||||
[]string{channel},
|
||||
@@ -1337,13 +1338,6 @@ func (s *Service) checkChannelHashcash(
|
||||
}
|
||||
}
|
||||
|
||||
err = s.db.RecordSpentHashcash(ctx, stampKey)
|
||||
if err != nil {
|
||||
s.log.Error(
|
||||
"record spent hashcash", "error", err,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user