#!/usr/bin/env python3.14# -*- coding: utf-8 -*-"""Proof of work for the unauthenticated forms that make the service send mail.`/forgot` and `/request-invite` each cost the operator something per request — a mail, a queueentry — and anyone can post them. The mail budgets (`policy.MAIL_*`) cap the damage; this makeseach request cost the *sender* something first, so a script cannot spend those budgets for free.The scheme is hashcash. The page carries a **stamp** the server minted and signed —`<issued>.<random>.<hmac>` — and the browser (`assets/pow.js`) searches for a nonce such that`SHA-256("<stamp>:<nonce>")` begins with :data:`~euler_site.auth.policy.POW_BITS` zero bits,then submits both. The server checks the signature (so a stamp cannot be invented), its age (soone cannot be stockpiled), the work, and that it has not been spent (so one solve buys onerequest). Nothing is stored per stamp until it is spent, and spent stamps are forgotten whenthey would have expired anyway.A person waits about a second; the cost scales with the request count, which is the point."""from__future__importannotations__all__=['ProofOfWork','leading_zero_bits']importhmacimportsecretsimporttimefromhashlibimportsha256defleading_zero_bits(digest:bytes)->int:"""How many zero bits *digest* starts with."""bits=0forbyteindigest:ifbyte:returnbits+8-byte.bit_length()bits+=8returnbitsclassProofOfWork:"""Mints stamps and checks solved ones, each stamp good for one request."""def__init__(self,secret:bytes,bits:int,ttl_seconds:int)->None:self._secret=secretself.bits=bitsself._ttl=ttl_seconds#: Spent stamps → when they expire; dropped once past it (the age check covers them).self._spent:dict[str,float]={}def_sign(self,body:str)->str:returnhmac.new(self._secret,body.encode(),sha256).hexdigest()[:32]defstamp(self)->str:"""A fresh stamp for a form to carry."""body=f'{int(time.time())}.{secrets.token_hex(8)}'returnf'{body}.{self._sign(body)}'defcheck(self,stamp:str,nonce:str)->bool:"""Whether *nonce* is a valid, unspent solution of *stamp* — spending it if so."""now=time.time()self._spent={key:expiryforkey,expiryinself._spent.items()ifexpiry>now}issued,_,rest=stamp.partition('.')_random,_,signature=rest.partition('.')ifnot(issued.isdigit()andsignatureandlen(nonce)<=32):returnFalseifnothmac.compare_digest(signature,self._sign(stamp.rsplit('.',1)[0])):returnFalseifnot0<=now-int(issued)<=self._ttlorstampinself._spent:returnFalseifleading_zero_bits(sha256(f'{stamp}:{nonce}'.encode()).digest())<self.bits:returnFalseself._spent[stamp]=int(issued)+self._ttlreturnTrue