开奖验算

抽奖使用「先承诺、后揭晓」:报名开始时就把种子的 SHA-256 摘要公开,开奖后才公布种子原值。 只要你知道参与名单,就能在浏览器里把中奖名单重新算一遍,看是否与公示一致。

算法与后端实现逐行对应:commit = SHA-256(seed),score = HMAC-SHA256(seed, draw:v1:…)。

拉取并验算

填活动编号即可,不需要登录

活动详情页地址里的那一段:/activities/<code>/。

这套算法怎么算

与后端一字不差

算法:HMAC-SHA256 打分

每条报名取 score = HMAC-SHA256(seed, "draw:v1:<活动ID>:<报名ID>") ,输出小写十六进制后按 score 升序排(同分按报名 ID 升序),取前 prizeCount 名。

后端 drawScore()(src/lib/lottery.ts:759-761)、 hmacHex()(src/lib/crypto.ts:120-124)、 pickWinners()(src/lib/lottery.ts:779-795)。

种子:32 字节随机数

种子是 32 字节随机数的 base64url 编码,报名开始前只公开它的摘要,开奖后才公布原值。 换掉种子会让摘要对不上,所以「事后换种子」一定能被发现。

种子与活动一一对应,接口只下发公开字段。

commit:SHA-256(seed)

报名开始前的「种子承诺」就是 commit = SHA-256(seed);验算的第一步就是对新公布的种子重算一次 SHA-256,和当初公示的承诺逐字比对。

后端 commitOf()(src/lib/lottery.ts:747-749)。

SHA-256 与 HMAC-SHA256 都由浏览器自带的 WebCrypto 计算,页面不加载任何第三方算法库, 接口只提供公开字段(承诺、揭晓值、脱敏名单)。

为什么这样能说明「没偷改名单」

  • 种子在报名开始前就已固定:公开的是摘要,改种子会让摘要对不上,所以事后换种子一定能被发现。
  • 名单完全由种子和报名 ID 决定:加人、删人、调换顺序都会改变 score 排序,复算结果就和公示名单不一致。
  • 验算用的是你自己的浏览器:页面只把公示的承诺、揭晓种子与脱敏名单取回来,计算过程你可以在 开发者工具里逐行看。

已知边界,如实说明:① 后端支持「内定名额」置顶(runDraw 先放内定、再按算法补足),这部分无法用纯算法核验, 只能由活动规则事先说明;② 公开接口不提供完整报名清单(只有脱敏中奖名单),完整名单需要主办方导出后粘贴进来验算; ③ 承诺只能证明「种子没换」,不能证明「报名本身是真的」。