insufficient error handling in RistrettoRedeemer.redeemWithCounter
success = result.get(u"success", False)
if not success:
reason = result.get(u"reason", None)
if reason == u"double-spend":
raise AlreadySpent(voucher)
elif reason == u"unpaid":
raise Unpaid(voucher)
self._log.info(
"Redeemed: {public_key} {proof} {count}",
public_key=result[u"public-key"],
proof=result[u"proof"],
count=len(result[u"signatures"]),
)
Notice that the not success
case falls through the end if the reason is not one of the two given. Then the success-case code runs. And if it was really not success then the success-case code path will probably break.
This was probably observed in the real world due to disagreement between required token count on client and server. The report from ZKAPAuthorizer was:
2021-09-27T10:08:51-0400 [_zkapauthorizer.controller.PaymentController#error] Redeeming random tokens for a voucher (6kb... failed: KeyError(u'public-key',)
2021-09-27T10:08:51-0400 [_zkapauthorizer.controller.PaymentController#info] Temporarily suspending redemption of 6kb... after non-success result.