mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-07-12 16:13:29 +08:00
431 lines
12 KiB
Markdown
431 lines
12 KiB
Markdown
# RedPacket Web3 接入设计文档
|
||
|
||
本文档描述红包系统在当前 OpenIM 架构中的 Web3 接入设计。内容以当前代码为准,覆盖前端、钱包、API 网关、RPC 服务、MongoDB、EVM/TRON 合约交互与事件回写。
|
||
|
||
## 1. 设计目标
|
||
|
||
业务目标:
|
||
|
||
- 支持固定红包、拼手气红包、待领取转账
|
||
- 支持 EVM 链红包创建、领取签名、事件解析
|
||
- 支持 TRON 链配置预留与部分管理交易能力
|
||
- 支持用户钱包绑定,领取前强制校验“OpenIM 用户 ID + 钱包地址”的绑定关系
|
||
- 通过 API 网关对外提供 HTTP 接口,内部保持标准 OpenIM gRPC 服务形态
|
||
|
||
安全目标:
|
||
|
||
- 钱包归属必须先绑定后领取
|
||
- claim 授权必须绑定 `packetId + claimer + authNonce + randomSeed + deadline`
|
||
- 同一用户、同一钱包对同一红包都不能重复领取
|
||
- signer 私钥只用于 claim 授权,不用于合约配置
|
||
- 管理类交易与高频签名职责分离
|
||
|
||
工程目标:
|
||
|
||
- RPC 服务接入 OpenIM 的配置、服务发现、日志和 MongoDB 体系
|
||
- HTTP API 只做参数解析和 RPC 转发
|
||
- MongoDB 保存业务状态、签名授权、领取记录和钱包绑定关系
|
||
- 链上事件作为最终一致性的依据
|
||
|
||
## 2. 当前系统边界
|
||
|
||
已经实现:
|
||
|
||
- `openim-rpc-redpacket` 标准 RPC 入口
|
||
- `/redpacket/*` API 网关路由
|
||
- MongoDB 存储模型和 DAO
|
||
- EVM claim digest 获取、裸签名、事件解析
|
||
- EVM 钱包绑定验签
|
||
- TRON 钱包绑定 challenge 生成
|
||
- TRON admin transaction 调用框架
|
||
- 创建回写、领取回写、详情查询
|
||
|
||
仍需补齐:
|
||
|
||
- EVM admin 写链当前是 mock
|
||
- TRON 钱包绑定签名验签未实现
|
||
- TRON 交易事件解析未完整实现
|
||
- refund API 当前未对外暴露,仅有退款模型与事件预留
|
||
- 管理接口未做独立管理员鉴权
|
||
- 自动 indexer loop 当前只是配置和代码结构预留,主要回写仍依赖 callback / parse
|
||
|
||
## 3. 总体架构
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
User[OpenIM 用户] --> App[App / H5 / Web]
|
||
App --> Wallet[钱包]
|
||
App --> API[openim-api /redpacket]
|
||
API --> RPC[openim-rpc-redpacket]
|
||
RPC --> DB[(MongoDB)]
|
||
RPC --> EVM[EVM RPC Client]
|
||
RPC --> TRON[TRON FullNode Client]
|
||
Wallet --> Contract[RedPacket Contract]
|
||
EVM --> Contract
|
||
TRON --> Contract
|
||
```
|
||
|
||
模块职责:
|
||
|
||
- App/H5/Web:连接钱包、发起链上交易、调用后端接口、展示红包状态
|
||
- 钱包:签名交易、签名绑定 challenge、广播交易
|
||
- openim-api:解析 HTTP 请求、注入登录用户上下文、调用 gRPC client
|
||
- openim-rpc-redpacket:业务鉴权、签名、存储、链上 receipt 解析
|
||
- MongoDB:保存业务状态和审计数据
|
||
- RedPacket 合约:维护链上红包状态、验签、防重放、转账结算
|
||
- EVM/TRON client:链上读写、事件解析、管理交易预留
|
||
|
||
## 4. 核心数据模型
|
||
|
||
### 4.1 红包主记录
|
||
|
||
collection: `red_packet`
|
||
|
||
保存内容:
|
||
|
||
- `biz_id`: 后端业务单号,唯一
|
||
- `chain_type`: `EVM` 或 `TRON`
|
||
- `packet_id`: 链上红包 ID
|
||
- `chain_id`: 链 ID
|
||
- `contract_address`: 合约地址
|
||
- `creator_user_id`: OpenIM 发红包用户 ID
|
||
- `creator_wallet`: 发红包钱包地址
|
||
- `group_id`: 群红包所属群
|
||
- `scope_type`: `GROUP`、`DIRECT`、`PUBLIC`
|
||
- `receiver_user_id` / `receiver_user_ids`: 转账红包目标用户
|
||
- `packet_type`: `0` 固定、`1` 拼手气、`2` 转账
|
||
- `token`: token 地址
|
||
- `total_amount` / `total_shares`: 总金额与总份数
|
||
- `claimed_amount` / `claimed_shares`: 已领取进度
|
||
- `expiry_at`: 过期时间
|
||
- `tx_hash`: 创建交易 hash
|
||
- `status`: `PENDING`、`ACTIVE`、`COMPLETED`、`REFUNDED`
|
||
|
||
### 4.2 领取授权
|
||
|
||
collection: `red_packet_claim_auth`
|
||
|
||
保存每次签名发放:
|
||
|
||
- `packet_id`
|
||
- `claimer`
|
||
- `auth_nonce`
|
||
- `random_seed`
|
||
- `deadline`
|
||
- `signature`
|
||
- `used`
|
||
- `created_at`
|
||
|
||
`auth_nonce` 建唯一索引,用于防止重复授权 nonce。
|
||
|
||
### 4.3 领取记录
|
||
|
||
collection: `red_packet_claim`
|
||
|
||
保存链上领取回写结果:
|
||
|
||
- `packet_id`
|
||
- `user_id`
|
||
- `claimer_wallet`
|
||
- `auth_nonce`
|
||
- `claim_tx_hash`
|
||
- `claimed_amount`
|
||
- `block_number`
|
||
- `status`
|
||
|
||
重复领取判断同时检查:
|
||
|
||
- `packet_id + user_id`
|
||
- `packet_id + claimer_wallet`
|
||
|
||
### 4.4 钱包绑定
|
||
|
||
collection:
|
||
|
||
- `wallet_binding_challenge`
|
||
- `wallet_binding`
|
||
|
||
绑定流程先保存 challenge,验签通过后 upsert active binding。领取签名前必须存在 `ACTIVE` 绑定。
|
||
|
||
## 5. 创建红包流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant App as App/H5
|
||
participant API as openim-api
|
||
participant RPC as redpacket RPC
|
||
participant DB as MongoDB
|
||
participant Wallet as Wallet
|
||
participant C as Contract
|
||
|
||
App->>API: POST /redpacket/create_order
|
||
API->>RPC: CreateOrder
|
||
RPC->>RPC: 校验登录用户和 scope
|
||
RPC->>DB: insert red_packet(PENDING)
|
||
RPC-->>App: biz_id
|
||
App->>Wallet: 发起 create transaction
|
||
Wallet->>C: createFixed/createRandom/createTransfer
|
||
C-->>Wallet: tx hash + receipt
|
||
App->>App: 解析 PacketCreated.packetId
|
||
App->>API: POST /redpacket/created_callback
|
||
API->>RPC: CreatedCallback
|
||
RPC->>C: 可选解析 receipt 并校验事件
|
||
RPC->>DB: update red_packet(ACTIVE)
|
||
RPC-->>App: ok
|
||
```
|
||
|
||
关键规则:
|
||
|
||
- `biz_id` 由后端生成,链上没有该字段
|
||
- `packet_id` 的可信来源是 `PacketCreated`
|
||
- 业务单先落 `PENDING`,链上确认后更新为 `ACTIVE`
|
||
- 如果 EVM client 可用,后端会校验 receipt 事件与业务单参数是否一致
|
||
- `creator_user_id` 从 token 上下文获取,不能由前端传入
|
||
|
||
scope 规则:
|
||
|
||
- `PUBLIC`: 公开红包,不要求 `group_id`
|
||
- `GROUP`: 群红包,必须传 `group_id`
|
||
- `DIRECT`: 指定用户红包,必须传 `receiver_user_id` 或 `receiver_user_ids`
|
||
|
||
## 6. 钱包绑定流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant App as App/H5
|
||
participant Wallet as Wallet
|
||
participant API as openim-api
|
||
participant RPC as redpacket RPC
|
||
participant DB as MongoDB
|
||
|
||
App->>API: POST /redpacket/wallet_bind/challenge
|
||
API->>RPC: IssueWalletBindChallenge
|
||
RPC->>DB: insert challenge(PENDING)
|
||
RPC-->>App: message + sign_method
|
||
App->>Wallet: 对 message 签名
|
||
Wallet-->>App: signature
|
||
App->>API: POST /redpacket/wallet_bind/confirm
|
||
API->>RPC: ConfirmWalletBind
|
||
RPC->>RPC: 验签并 recover 钱包地址
|
||
RPC->>DB: challenge=VERIFIED, upsert binding(ACTIVE)
|
||
RPC-->>App: binding detail
|
||
```
|
||
|
||
EVM 绑定:
|
||
|
||
- 使用 SIWE 风格 message
|
||
- `sign_method=personal_sign`
|
||
- 后端使用 Ethereum Signed Message 前缀 recover 地址
|
||
- recover 地址必须等于 challenge 的 `wallet_address`
|
||
|
||
TRON 绑定:
|
||
|
||
- 当前仅生成 challenge
|
||
- `sign_method=signMessageV2`
|
||
- confirm 阶段尚未实现验签
|
||
|
||
安全边界:
|
||
|
||
- challenge 默认 10 分钟过期
|
||
- challenge 只能从 `PENDING` 确认一次
|
||
- 领取签名前必须查询到 active binding
|
||
|
||
## 7. 领取红包流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant App as App/H5
|
||
participant API as openim-api
|
||
participant RPC as redpacket RPC
|
||
participant DB as MongoDB
|
||
participant C as Contract
|
||
participant Wallet as Wallet
|
||
|
||
App->>API: POST /redpacket/detail
|
||
API->>RPC: GetDetail
|
||
RPC->>DB: 查询红包和领取记录
|
||
RPC-->>App: detail
|
||
App->>API: POST /redpacket/issue_claim_sign
|
||
API->>RPC: IssueClaimSign
|
||
RPC->>DB: 校验红包、绑定、重复领取、scope
|
||
RPC->>C: getSignMessage(...)
|
||
RPC->>RPC: signer 私钥裸签名
|
||
RPC->>DB: insert claim_auth
|
||
RPC-->>App: authNonce + randomSeed + deadline + signature
|
||
App->>Wallet: claim(...)
|
||
Wallet->>C: claim transaction
|
||
C-->>Wallet: tx hash + receipt
|
||
App->>API: POST /redpacket/claim_result
|
||
API->>RPC: ClaimResult
|
||
RPC->>C: 可选解析 PacketClaimed
|
||
RPC->>DB: 保存 claim,更新领取进度
|
||
RPC-->>App: ok
|
||
```
|
||
|
||
领取前后端校验:
|
||
|
||
- 登录用户存在
|
||
- 红包存在
|
||
- 红包状态为 `ACTIVE`
|
||
- 红包未过期
|
||
- 当前用户绑定了 `claimer` 钱包
|
||
- 当前用户未领取
|
||
- 当前钱包未领取
|
||
- 群红包必须有关联群
|
||
- 转账红包必须匹配指定接收用户
|
||
|
||
claim 签名字段:
|
||
|
||
- `packetId`
|
||
- `claimer`
|
||
- `authNonce`
|
||
- `randomSeed`
|
||
- `deadline`
|
||
|
||
前端必须原样把后端返回的参数传给合约 `claim(...)`。任一字段变化都会导致验签失败。
|
||
|
||
## 8. 管理配置流程
|
||
|
||
管理员接口位于:
|
||
|
||
```text
|
||
/redpacket/admin/*
|
||
```
|
||
|
||
当前对外接口:
|
||
|
||
- `set_signer`
|
||
- `set_token`
|
||
- `set_expiry`
|
||
- `set_allow_all_tokens`
|
||
- `set_native_token_enabled`
|
||
- `parse_tx_events`
|
||
|
||
设计分权:
|
||
|
||
- owner / 多签:最高权限
|
||
- config admin:低频参数配置
|
||
- signer:高频 claim 授权签名
|
||
|
||
当前实现边界:
|
||
|
||
- EVM 管理接口是 mock,只返回成功 message
|
||
- TRON 管理接口会尝试通过 FullNode 发交易
|
||
- API 层未做独立管理员角色校验,生产必须补齐
|
||
|
||
## 9. 事件与最终一致性
|
||
|
||
核心事件:
|
||
|
||
- `PacketCreated`: 创建成功,获得链上 `packetId`
|
||
- `PacketClaimed`: 领取成功,获得真实领取金额
|
||
- `PacketRefunded`: 退款成功,获得退款目标与金额
|
||
|
||
当前一致性策略:
|
||
|
||
- 创建阶段由 `created_callback` 回写,并在 EVM client 可用时解析 receipt 校验
|
||
- 领取阶段由 `claim_result` 先保存 `PENDING`,能解析 receipt 时立即确认
|
||
- 后续 indexer 可基于 `indexer.pollInterval` 扩展为后台轮询与补偿
|
||
|
||
幂等建议:
|
||
|
||
- 以 `tx_hash` 做领取回写幂等
|
||
- 以 `biz_id` 做创建业务单幂等
|
||
- 以 `packet_id + user_id` 和 `packet_id + claimer_wallet` 做重复领取判断
|
||
- 事件重复消费时,只允许状态向前推进,不回退已确认状态
|
||
|
||
## 10. API 设计摘要
|
||
|
||
用户侧:
|
||
|
||
- `POST /redpacket/create_order`: 创建业务单
|
||
- `POST /redpacket/created_callback`: 创建交易回写
|
||
- `POST /redpacket/detail`: 查询红包详情
|
||
- `POST /redpacket/issue_claim_sign`: 领取签名发放
|
||
- `POST /redpacket/claim_result`: 领取交易回写
|
||
- `POST /redpacket/wallet_bind/challenge`: 钱包绑定 challenge
|
||
- `POST /redpacket/wallet_bind/confirm`: 钱包绑定确认
|
||
- `POST /redpacket/wallet_bind/detail`: 查询当前用户的钱包绑定
|
||
|
||
管理员侧:
|
||
|
||
- `POST /redpacket/admin/set_signer`
|
||
- `POST /redpacket/admin/set_token`
|
||
- `POST /redpacket/admin/set_expiry`
|
||
- `POST /redpacket/admin/set_allow_all_tokens`
|
||
- `POST /redpacket/admin/set_native_token_enabled`
|
||
- `POST /redpacket/admin/parse_tx_events`
|
||
|
||
## 11. 前端接入建议
|
||
|
||
创建页:
|
||
|
||
1. 用户选择红包类型、金额、份数、过期时间和链
|
||
2. 调用 `create_order`
|
||
3. 钱包发起链上创建交易
|
||
4. 从 receipt 解析 `PacketCreated.packetId`
|
||
5. 调用 `created_callback`
|
||
6. 展示分享页或详情页
|
||
|
||
领取页:
|
||
|
||
1. 查询 `detail`
|
||
2. 检查当前钱包是否已绑定
|
||
3. 未绑定则先走 wallet bind
|
||
4. 调用 `issue_claim_sign`
|
||
5. 钱包发起 `claim(...)`
|
||
6. 调用 `claim_result`
|
||
7. 刷新 `detail`
|
||
|
||
钱包绑定页:
|
||
|
||
1. 获取当前钱包地址和 chain type
|
||
2. 调用 `wallet_bind/challenge`
|
||
3. 按 `sign_method` 调钱包签名
|
||
4. 调用 `wallet_bind/confirm`
|
||
5. 调用 `wallet_bind/detail` 验证绑定状态
|
||
|
||
## 12. 风险与待办
|
||
|
||
必须尽快处理:
|
||
|
||
- 修复 `protocol/redpacket/redpacket.proto` 与当前 `internal/api` / `internal/rpc` 使用的 protobuf 类型不一致问题
|
||
- 补充管理员接口的 OpenIM 管理员权限校验
|
||
- 移除或保护 placeholder signature 降级路径
|
||
- EVM admin 从 mock 改为真实交易或明确只允许前端钱包管理
|
||
|
||
按业务优先级处理:
|
||
|
||
- 补齐 TRON 绑定验签
|
||
- 补齐 TRON 事件解析
|
||
- 增加 refund HTTP/RPC 接口
|
||
- 增加后台 indexer loop 与事件补偿
|
||
- 增加管理员操作审计 collection
|
||
|
||
上线检查:
|
||
|
||
- API 网关能发现 `redPacket` RPC 服务
|
||
- MongoDB 索引创建成功
|
||
- signer 地址与合约 signer 一致
|
||
- EVM RPC 能稳定获取 receipt
|
||
- claim 签名在测试链可通过合约验签
|
||
- 钱包绑定 recover 地址与实际钱包一致
|
||
|
||
## 13. 总结
|
||
|
||
当前 RedPacket 的核心链路是:
|
||
|
||
```text
|
||
OpenIM 登录身份
|
||
-> 钱包绑定
|
||
-> 业务鉴权
|
||
-> 后端 signer 裸签 claim digest
|
||
-> 前端钱包发 claim 交易
|
||
-> 链上事件回写 MongoDB
|
||
```
|
||
|
||
这条链路把“谁是 OpenIM 用户”“谁控制钱包”“谁有资格领取”“链上是否最终成功”分成四层校验,后端只签发授权,不直接替用户领取,从而保持用户资产操作仍由钱包确认。
|