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:
+10
-22
@@ -1783,45 +1783,33 @@ func (database *Database) SetChannelHashcashBits(
|
||||
}
|
||||
|
||||
// RecordSpentHashcash stores a spent hashcash stamp hash
|
||||
// for replay prevention.
|
||||
// for replay prevention. It returns false when the hash was
|
||||
// already stored. The one INSERT is both the check and the
|
||||
// record, so of several concurrent callers with the same
|
||||
// hash exactly one gets true.
|
||||
func (database *Database) RecordSpentHashcash(
|
||||
ctx context.Context,
|
||||
stampHash string,
|
||||
) error {
|
||||
_, err := database.conn.ExecContext(ctx,
|
||||
) (bool, error) {
|
||||
res, err := database.conn.ExecContext(ctx,
|
||||
`INSERT OR IGNORE INTO spent_hashcash
|
||||
(stamp_hash, created_at)
|
||||
VALUES (?, ?)`,
|
||||
stampHash, time.Now())
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
return false, fmt.Errorf(
|
||||
"record spent hashcash: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsHashcashSpent checks whether a hashcash stamp hash
|
||||
// has already been used.
|
||||
func (database *Database) IsHashcashSpent(
|
||||
ctx context.Context,
|
||||
stampHash string,
|
||||
) (bool, error) {
|
||||
var count int
|
||||
|
||||
err := database.conn.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM spent_hashcash
|
||||
WHERE stamp_hash = ?`,
|
||||
stampHash,
|
||||
).Scan(&count)
|
||||
inserted, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf(
|
||||
"check spent hashcash: %w", err,
|
||||
"record spent hashcash: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
return inserted > 0, nil
|
||||
}
|
||||
|
||||
// PruneSpentHashcash deletes spent hashcash tokens older
|
||||
|
||||
Reference in New Issue
Block a user