feat(admin): 新增管理后台前端资源文件 feat(api): 实现获取用户统计数据的API接口 - 添加获取用户道具卡数量、优惠券数量和积分余额的接口 - 实现设置默认地址和删除地址的接口 feat(service): 新增用户统计服务方法 - 实现GetUserStats方法查询用户统计数据 - 添加地址管理相关服务方法 fix(core): 修复静态资源路由问题 - 调整静态资源路由配置 - 优化404路由处理逻辑 chore: 更新前端构建配置 - 添加Windows平台构建命令 - 更新README构建说明
127 lines
7.5 KiB
Markdown
127 lines
7.5 KiB
Markdown
/Users/win/code2025/bindbox_game/docs/抽奖一致性方案/TECH_DESIGN_抽奖一致性.md
|
||
# 抽奖一致性与离线验证技术方案
|
||
|
||
## 1. 背景与目标
|
||
- 背景:当前系统具备活动/期/奖励配置与日志查询,但缺少“抽奖随机一致性与离线验证”的闭环
|
||
- 目标
|
||
- 离线验证:用户拿一份回执单 (`receipt.json`) 在本地无网络环境重算并比对抽奖结果
|
||
- 不可作假:平台在抽前预承诺随机源与奖池,抽后揭示并提供签名,用户可核验
|
||
- 不可预测:一次一密、不提前泄露服务端随机,用户无法事前预测下一次结果
|
||
- 便携性:单文件验证工具,零依赖、零配置,命令行运行即可
|
||
|
||
## 2. 总体方案
|
||
- 方案A(MVP):Commit-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`
|
||
- macOS(amd64/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`
|