bindbox-game/抽奖算法.md
邹方成 00452cba59 feat: 添加用户统计功能及相关API接口
feat(admin): 新增管理后台前端资源文件

feat(api): 实现获取用户统计数据的API接口
- 添加获取用户道具卡数量、优惠券数量和积分余额的接口
- 实现设置默认地址和删除地址的接口

feat(service): 新增用户统计服务方法
- 实现GetUserStats方法查询用户统计数据
- 添加地址管理相关服务方法

fix(core): 修复静态资源路由问题
- 调整静态资源路由配置
- 优化404路由处理逻辑

chore: 更新前端构建配置
- 添加Windows平台构建命令
- 更新README构建说明
2025-11-15 03:08:53 +08:00

127 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/Users/win/code2025/bindbox_game/docs/抽奖一致性方案/TECH_DESIGN_抽奖一致性.md
# 抽奖一致性与离线验证技术方案
## 1. 背景与目标
- 背景:当前系统具备活动/期/奖励配置与日志查询,但缺少“抽奖随机一致性与离线验证”的闭环
- 目标
- 离线验证:用户拿一份回执单 (`receipt.json`) 在本地无网络环境重算并比对抽奖结果
- 不可作假:平台在抽前预承诺随机源与奖池,抽后揭示并提供签名,用户可核验
- 不可预测:一次一密、不提前泄露服务端随机,用户无法事前预测下一次结果
- 便携性:单文件验证工具,零依赖、零配置,命令行运行即可
## 2. 总体方案
- 方案AMVPCommit-Reveal + HMAC-SHA256
- 服务端为每个期 `issue_id` 预承诺高熵主种子 `serverSeed_master`,公开 `SHA256(serverSeed_master)`(记为 `serverSeedHash`
- 每次抽奖派生一次性子种子:`serverSubSeed = HMAC_SHA256(serverSeed_master, encode(issueId|drawId))`;抽后在回执揭示 `serverSubSeed`,不泄露主种子
- 抽奖随机:`entropy = HMAC_SHA256(serverSubSeed, encodedMessage)`;拒绝采样,映射到权重区间
- 权重与奖池:抽奖前对奖励配置做规范化快照并哈希承诺 `itemsRoot`,确保各用户池一致
- 优点:实现简单、使用标准库、双平台易构建;满足不可作假与便携校验
- 方案B升级VRF可验证随机函数
- 服务端用 VRF 私钥对消息生成随机与证明;用户用公钥离线验真,无需揭示任何种子
- 优点:一次一密且天然不可预测;缺点:需引入并审计成熟库,跨平台打包复杂度更高
- 方案C审计增强透明日志与库存状态证明
- 使用追加型透明日志的 Merkle 根约束 `drawId` 顺序与库存变更;可提供单位级“包含/非包含证明”
- 适合二期提升全局可追溯性与防“双花”
## 3. 核心数据与回执规范
- 回执字段(自包含模式)
- `algoVersion`: 算法/编码版本
- `roundId`: 期ID`issue_id`
- `drawId`: 抽奖唯一ID递增或全局唯一
- `clientId`: 用户ID
- `timestamp`: 抽奖时间戳
- `serverSeedHash`: `SHA256(serverSeed_master)` 的十六进制/Base64
- `serverSubSeed`: 本次子种子(十六进制/Base64
- `clientSeed`: 用户种子(可系统生成)
- `nonce`: 用户在该期内的递增计数(防重)
- `items`: 奖池快照(按 `id` 排序)`[{id, name, weight, quantity_before}]`
- `itemsRoot`: `SHA256(canonical_items_json)`
- `weightsTotal`: 权重总和(整数)
- `selectedIndex`: 选中项索引
- `selectedItemId`: 选中奖励ID
- `randProof`: 可选,记录一次性 `entropy` 的十六进制
- `signature`: 平台签名(覆盖整份回执,推荐 ed25519
- 双文件模式(大池可选)
- 回执移除 `items`,仅保留 `itemsRoot``weightsTotal`;本地需提供 `pool.json`,其哈希必须等于 `itemsRoot`
## 4. 消息编码与随机选取规范
- 编码规则(用于 HMAC 消息)
- 固定顺序:`algoVersion | roundId | drawId | clientId | clientSeed | nonce | itemsRoot | weightsTotal`
- 字符串:`uint32`(大端)长度 + UTF-8 字节
- 整数:`uint64`(大端)
- 哈希:原始 32 字节
- 随机选取(拒绝采样)
-`R = uint64(entropy[0:8])`
-`W = weightsTotal``M = floor(2^64 / W) * W`
-`R >= M`:以计数器扩展重新计算 `entropy = HMAC_SHA256(serverSubSeed, encodedMessage || counter++)`,直到命中
- 位置 `pos = R % W`;以权重累加区间定位 `selectedIndex`
## 5. 有库存的验证与扣减
- 抽奖前集合:仅在“`quantity_before > 0`”的奖励集合中参与权重抽取
- 两阶段(可选):
- 阶段1类别选择权重拒绝采样
- 阶段2单位选择该类别剩余单位内均匀或约定次序
- 事务处理(服务端)
- 扣减 `ActivityRewardSettings.Quantity`(乐观检查>0
- 写入 `ActivityDrawLogs`(抽奖记录)
- 如需生成用户资产/订单,参考现有逻辑(`internal/service/user/reward_grant.go`
- 二期增强Merkle证明
- 维护稀疏 Merkle 树(键=`unitId`,值=状态位),回执包含“抽前包含证明 + 抽后非包含/状态更新证明”与版本号单调
## 6. 服务端接口与集成点
- APP端新增
- `POST /api/app/activities/:activity_id/issues/:issue_id/draw`:执行抽奖,返回回执 JSON
- 集成位置:`internal/router/router.go` 的 APP认证组与用户接口相同组
- 管理端新增
- `POST /api/admin/activities/:activity_id/issues/:issue_id/commit_random`:生成/轮换期的随机承诺
- `GET /api/admin/activities/:activity_id/issues/:issue_id/commit_random`:查看承诺
- 现有点位复用
- 奖励配置与权重:`ListIssueRewards``internal/service/activity/rewards_list.go`
- 抽奖日志查询:`ListDrawLogs``internal/api/activity/draw_logs_app.go`
- 用户资产/订单:参考系统发放(`internal/service/user/reward_grant.go`
## 7. 随机承诺存储设计
- 新增表:`issue_random_commitments`
- 字段:`issue_id, algo_version, server_seed_master, server_seed_hash, items_root, weights_total, state_version, created_at`
- 用途:管理期的随机承诺与池快照,支持版本化(`state_version` 递增)
- 过渡方案若不立刻加表可用配置存储KV/配置表/文件),但建议最终落库便于审计
## 8. 离线验证工具CLI
- 用法:`bindbox_verify.exe receipt.json`(返回码 `0` 通过、`1` 失败)
- 验证步骤
- 验签:用公钥验证 `signature`
- 验承诺:`SHA256(serverSeed_master)` 是否等于 `serverSeedHash`;并校验子种子派生规则
- 验奖池:重算 `itemsRoot``weightsTotal`
- 重算随机:按编码规范 HMAC 与拒绝采样
- 映射比对:`selectedIndex/selectedItemId` 是否一致
- 构建(参考已有命令)
- Windows`CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -trimpath -o build/bindbox_verify.exe ./cmd/verify`
- macOSamd64/arm64同上构建现有参考命令见 `README.md:17`
## 9. 安全与公平
- 不可预测
- 每次抽奖一次一密(`serverSubSeed`),不提前揭示
- `drawId/nonce` 单调递增并纳入消息编码
- 不可操控
- 抽前公开承诺:`serverSeedHash``itemsRoot`
- 回执签名覆盖所有字段;日志保留顺序与上下文
- 无偏性
- 严格使用拒绝采样;权重与数量为非负整数;总和与整型范围校验
## 10. 实施步骤与时间节点
- 第1周协议固化与承诺表设计管理端承诺接口服务层 `ExecuteDraw` 实现(无库存/有库存)
- 第2周APP路由与处理器接入离线验证 CLI 首版;测试向量与集成测试
- 第3周文档与用户指引压测极端权重/库存可选VRF调研与二期规划
## 11. 验收标准
- 抽前承诺已公开且与回执一致
- 同一回执在 Windows/macOS 离线复验结果一致
- 有库存时仅在剩余>0集合中抽取事务扣减与日志记录完整
- 随机输出在所有构建版本中一致;拒绝采样性能达标
## 12. 参考与代码位置
- 构建命令Windows`README.md:17`
- 奖励配置查询:`internal/service/activity/rewards_list.go:7`
- 抽奖日志查询:`internal/api/activity/draw_logs_app.go:35``internal/service/activity/draw_logs_list.go:7`
- 用户资产发放参考:`internal/service/user/reward_grant.go:136`