跳到内容

Web3 生产联调

受众:开发者 摘要:Web3 生产联调专题:RPC 故障切换 / 私钥安全 / 资金归集 / 监控告警。

本文档说明 Web3 能力接入真实 RPC 节点后的配置、验证和排查方式。

Web3 当前定位是:

  • 钱包验签
  • Typed Data 验签
  • 只读 RPC
  • 合约只读调用
  • Token / NFT 查询
  • 代币价格查询和 Token 元数据缓存
  • 事件扫描
  • Web3 支付确认、人工复核和受控结算
  • 原生币 / Token 写交易、批量转账和资金归集

高风险能力边界:

  • 私钥托管
  • 签名和广播只在受控 API 内执行,私钥只作为单次请求参数参与签名,不落库、不写日志。
  • 批量转账、资金归集和支付结算必须配合权限、审计、限额、脱敏、幂等和失败处理。
  • 自动结算需要经过 dry-run 计划、人工复核或明确配置的超时自动处理策略,不允许绕过状态机直接改订单。

生产开启写交易能力前,先完成小额联调和回滚预案,不要直接使用主账户大额资金测试。

配置优先级

Web3 配置遵循 Config starter 统一规则:

text
数据库配置 / Config provider
  -> application.yml / application-*.yml / 环境变量
  -> 默认值

数据库配置适合生产运行期调整 RPC、超时、默认链和支付收款参数;本地文件适合开发默认值和环境模板。

建议生产配置保留在后台配置或部署平台密钥中,不要把商用 RPC key、私有节点地址或支付收款配置提交到 Git。

基础配置

最小生产配置示例:

yaml
spring:
  open:
    web3:
      enabled: true
      default-chain: bsc
      config:
        enabled: true
        prefix: spring.open.web3
      chains:
        bsc:
          enabled: true
          chain-type: evm
          chain-id: 56
          rpc-urls:
            - https://bsc-dataseed.bnbchain.org
            - https://bsc-dataseed1.bnbchain.org
          private-rpc-urls:
            - https://your-private-bsc-rpc-a.example.com
            - https://your-private-bsc-rpc-b.example.com
          native-currency: BNB
          block-explorer-url: https://bscscan.com
          connect-timeout: 5s
          read-timeout: 15s
        ethereum:
          enabled: true
          chain-type: evm
          chain-id: 1
          rpc-urls:
            - https://ethereum-rpc.publicnode.com
          private-rpc-urls:
            - https://your-private-ethereum-rpc-a.example.com
            - https://your-private-ethereum-rpc-b.example.com
          native-currency: ETH
          block-explorer-url: https://etherscan.io
          connect-timeout: 5s
          read-timeout: 15s
      price:
        enabled: true
        default-provider: pancake-v2
        fallback-providers:
          - pancake-v2-pool
        providers:
          pancake-v2:
            enabled: true
            provider: pancake-v2
            chain-name: bsc
            router-address: 0x10ED43C718714eb63d5aA57B78B54704E256024E
            quote-token-address: 0x55d398326f99059fF775485246999027B3197955
            quote-token-symbol: USDT
            quote-token-decimals: 18
            base-token-decimals: 18
            default-amount-raw: 1000000000000000000
          pancake-v2-pool:
            enabled: true
            provider: pancake-v2-pool
            chain-name: bsc
            factory-address: 0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73
            pair-address:
            quote-token-address: 0x55d398326f99059fF775485246999027B3197955
            quote-token-symbol: USDT
            quote-token-decimals: 18
            base-token-decimals: 18
            default-amount-raw: 1000000000000000000
      metadata-cache:
        enabled: true
        ttl: 10m
        max-size: 2048

等价后台配置 key:

properties
spring.open.web3.enabled=true
spring.open.web3.default-chain=bsc
spring.open.web3.chains.bsc.enabled=true
spring.open.web3.chains.bsc.chain-type=evm
spring.open.web3.chains.bsc.chain-id=56
spring.open.web3.chains.bsc.rpc-urls=https://bsc-dataseed.bnbchain.org,https://bsc-dataseed1.bnbchain.org
spring.open.web3.chains.bsc.private-rpc-urls=https://your-private-bsc-rpc-a.example.com,https://your-private-bsc-rpc-b.example.com
spring.open.web3.chains.bsc.native-currency=BNB
spring.open.web3.chains.bsc.block-explorer-url=https://bscscan.com
spring.open.web3.chains.bsc.connect-timeout=5s
spring.open.web3.chains.bsc.read-timeout=15s
spring.open.web3.price.default-provider=pancake-v2
spring.open.web3.price.fallback-providers=pancake-v2-pool
spring.open.web3.price.providers.pancake-v2.chain-name=bsc
spring.open.web3.price.providers.pancake-v2.router-address=0x10ED43C718714eb63d5aA57B78B54704E256024E
spring.open.web3.price.providers.pancake-v2.quote-token-address=0x55d398326f99059fF775485246999027B3197955
spring.open.web3.price.providers.pancake-v2-pool.chain-name=bsc
spring.open.web3.price.providers.pancake-v2-pool.factory-address=0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73
spring.open.web3.price.providers.pancake-v2-pool.quote-token-address=0x55d398326f99059fF775485246999027B3197955
spring.open.web3.metadata-cache.ttl=10m
spring.open.web3.metadata-cache.max-size=2048

RPC 选择

公共 RPC 只适合本地体验和临时调试,不建议作为生产默认节点。

生产建议:

  • 使用稳定的服务商 RPC 或自建节点。
  • 为 BSC、Ethereum 等主网配置独立的 RPC key 和限流策略。
  • 历史事件扫描需要确认节点是否支持目标区块范围;较早历史日志通常需要 archive 能力或服务商历史日志支持。
  • 不同链必须配置正确的 chain-id,启动后通过诊断接口校验。
  • 对事件扫描和后台页面查询设置合理频率,避免被 RPC 服务商限流。
  • 如果启用默认 pancake-v2 价格查询,需确保 BSC 链已启用且 RPC 能访问 PancakeSwap V2 Router;pancake-v2-pool 降级 Provider 还需要访问 Factory 和 Pair。两者都只读,不会触发 swap 或交易广播。
  • Token 元数据缓存只缓存静态信息,生产可按 Token 数量调大 metadata-cache.max-size;余额、授权和交易状态不会被缓存。

当前 Web3 starter 使用 HTTP JSON-RPC 调用模型;如果后续需要 WebSocket 订阅、pending 交易监听或推送式事件流,应作为独立 Provider 能力设计。

启动后自检

先确认链配置是否被应用读取:

http
GET /api/v1/money/web3/chains

再检查健康摘要:

http
GET /api/v1/money/web3/chains/health/summary

查看生产诊断项:

http
GET /api/v1/money/web3/chains/diagnostics
GET /api/v1/money/web3/chains/{chainName}/diagnostics

单链健康检查:

http
GET /api/v1/money/web3/chains/{chainName}/health

常用下钻排查:

http
GET  /api/v1/money/web3/chains/{chainName}/accounts/{address}
GET  /api/v1/money/web3/chains/{chainName}/transactions/{hash}
GET  /api/v1/money/web3/chains/{chainName}/transactions/{hash}/receipt
GET  /api/v1/money/web3/chains/{chainName}/transactions/{hash}/confirmation
POST /api/v1/money/web3/chains/{chainName}/event-logs/search
POST /api/v1/money/web3/chains/{chainName}/calls/raw
POST /api/v1/money/web3/chains/{chainName}/gas-estimates
POST /api/v1/money/web3/chains/{chainName}/contract-calls

Web3 生产联调以 Money Web3 后台只读 API 和正式运维页面为准;当前运维路径仍沿用 /api/v1/money/web3/**,不再通过独立 samples 调试页面验证。

RPC Smoke 手动检查

上线前可以先检查真实 RPC 是否可连、链 ID 是否正确、区块高度是否可读取。检查只调用 eth_chainIdeth_blockNumber,不会签名、不会广播交易。

bash
RPC_URL="https://bsc-dataseed.bnbchain.org"

curl -fsS -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' \
  "$RPC_URL"

curl -fsS -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \
  "$RPC_URL"

示例中的 BSC 公共节点和目标链 ID:

text
RPC URL: https://bsc-dataseed.bnbchain.org
Expected chainId: 0x38

生产建议显式传入多个节点,多个 URL 使用英文半角逗号分隔:

bash
for RPC_URL in https://rpc-a.example.com https://rpc-b.example.com; do
  echo "Checking $RPC_URL"

  curl -fsS --connect-timeout 10 --max-time 10 \
    -H 'Content-Type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' \
    "$RPC_URL"

  curl -fsS --connect-timeout 10 --max-time 10 \
    -H 'Content-Type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \
    "$RPC_URL"
done

如果只是临时排查,希望至少一个节点可用即可,先分别执行上述只读请求,再按返回结果判断是否切换备用节点。

发版或生产巡检建议确认配置中的每个节点都可用,并核对返回的 chainId 与目标链一致。

生产签名网关

写交易、跨链桥放款 / 退款、批量转账和资金归集统一经过 Web3TransactionSigner。默认 local signer 只用于兼容本地开发和已有部署;生产真实资金流推荐使用 external-http signer,把 KMS、HSM、Vault Transit 或机构托管签名服务封装在部署侧内部签名网关之后。

推荐配置:

properties
spring.open.web3.signer.provider=external-http
spring.open.web3.signer.external-http.endpoint=https://signer.internal.example.com/web3/sign
spring.open.web3.signer.external-http.authorization-header-name=Authorization
spring.open.web3.signer.external-http.authorization-value=Bearer ${WEB3_SIGNER_TOKEN}
spring.open.web3.signer.external-http.timeout=10s

签名网关 contract:

  • 请求只包含规范化交易签名材料:链名、chainIdfromtovaluedatanoncegasPricegasLimit
  • 网关返回已签名原始交易 rawTransaction 和可选 transactionHash / 元数据。
  • 框架会校验返回的 fromtochainIdvaluenonce、gas 等字段与请求一致,避免网关误签其他交易。
  • 签名网关凭据只通过环境变量、K8s Secret、Vault Agent 或云 Secret Manager 注入,不进入 Git、配置中心明文、日志或审计扩展 JSON。
  • 签名失败、超时或返回字段不一致时,本次放款 / 退款保持失败或留人工处理,不自动切回本地私钥 signer。

仍使用本地私钥 signer 的生产部署必须满足:

  • 金库私钥只经环境变量 / 外部 secret 注入;确需配置中心入库时按 Web3 私钥本地加密约定使用 enc:v1: 密文信封和环境变量主密钥。
  • spring.open.money.web3.bridge.auto-payout.enabled=true 时,只允许通过金额、风控、访问策略、签名配置和目标链出金钱包余额门控的安全单自动放款。
  • 需要严格人工审核时,显式设置 spring.open.money.web3.bridge.auto-payout.enabled=false
  • 私钥、签名中间值和 raw signed transaction 不写日志、不写业务表;审计只记录订单、出金钱包、金额、nonce 和交易哈希。

真实 KMS / HSM / Vault signer、真实 RPC 服务商、真实出金钱包和测试链 web3-bridge-testchain-smoke 属于外部 Provider 验收,不应在缺少真实凭据和真实链环境时声明完成。发版前可用 ./scripts/release web3-provider-acceptance --chain <chain> --rpc-provider <provider> 生成只读验收记录模板;记录填写后用 ./scripts/release web3-provider-gate --record <file> 校验真实链、签名、充值 / 提现 / Bridge、nonce / replay 和扫描 checkpoint 证据完整性。脚本不会连接 RPC、签名、广播交易或修改数据库。

curl 示例

bash
TOKEN=your-token
BASE_URL=http://localhost:8000

curl -H "Authorization: Bearer ${TOKEN}" \
  "${BASE_URL}/api/v1/money/web3/chains"

curl -H "Authorization: Bearer ${TOKEN}" \
  "${BASE_URL}/api/v1/money/web3/chains/bsc/health"

curl -H "Authorization: Bearer ${TOKEN}" \
  "${BASE_URL}/api/v1/money/web3/chains/bsc/accounts/0x0000000000000000000000000000000000000000"

事件日志搜索:

bash
curl -X POST \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "fromBlock": "latest",
    "toBlock": "latest",
    "addresses": ["0x0000000000000000000000000000000000000000"],
    "topics": []
  }' \
  "${BASE_URL}/api/v1/money/web3/chains/bsc/event-logs/search"

ABI 只读调用:

bash
curl -X POST \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "contractAddress": "0x0000000000000000000000000000000000000000",
    "functionName": "balanceOf",
    "inputs": [
      {
        "type": "address",
        "value": "0x0000000000000000000000000000000000000000"
      }
    ],
    "outputs": ["uint256"]
  }' \
  "${BASE_URL}/api/v1/money/web3/chains/bsc/contract-calls"

事件扫描联调

事件扫描使用检查点模型:

text
money_web3_scan_checkpoint
money_web3_scan_log

联调步骤:

  1. 确认 RPC 健康和链 ID 匹配。
  2. 确认业务模块已注册 Web3EventScanner
  3. 新增检查点,scannerName 必须与业务扫描器名称一致。
  4. 设置合理的 fromBlocktoBlock、确认数和区块跨度。
  5. 手动触发单个检查点扫描。
  6. 查看扫描日志、失败原因和业务表幂等落库结果。

手动触发:

http
POST /api/v1/admin/web3/scans/checkpoints/{id}/trigger

批量触发:

http
POST /api/v1/admin/web3/scans/trigger

注意:

  • 不要直接从创世区块开始扫公共 RPC。
  • 历史扫描优先分批推进。
  • blockRangeSize 太大容易被节点拒绝或超时。
  • 业务 handler 必须靠唯一索引或幂等 key 防重复。
  • 扫描任务失败后先看 money_web3_scan_log,不要盲目重试大范围任务。

持续扫描、补偿任务、失败重跑和通知边界见 Web3 链上事件补偿任务

Web3 支付联调

当前 Web3 支付提供:

  • 创建链上收款上下文
  • 查询交易确认状态
  • ERC-20 Transfer 事件匹配
  • 批量只读确认
  • 结算 dry-run 计划
  • 人工复核指纹
  • 复核日志留痕
  • 显式结算执行
  • 复核超时自动处理
  • 自动结算失败通知
  • 人工重试入口

结算执行会通过订单状态条件更新保护幂等,不会把已成功、已退款、已关闭等终态订单回退。

联调建议:

  1. 先用普通 Web3 交易确认接口确认交易可查。
  2. 再用 Web3 支付只读确认接口读取本地订单和链上状态。
  3. 使用结算 dry-run 计划生成建议动作。
  4. 后台人工复核 reviewFingerprint
  5. 小额执行显式结算,检查订单、流水、复核日志和通知记录。
  6. 开启超时自动处理前,确认自动动作、批量数量、失败通知和人工重试入口。

自动结算的状态机、幂等、链重组和人工复核边界见 Web3 支付自动结算设计

常见问题

现象常见原因处理方式
链配置清单为空链未启用、配置前缀错误、数据库配置未生效检查 spring.open.web3.enabledspring.open.web3.chains.*.enabled
健康检查失败RPC 不通、DNS 失败、服务商限流、超时过短更换节点,调大 read-timeout,检查网络和服务商面板
链 ID 不匹配RPC 指向了错误网络修改 private-rpc-urls / rpc-urlschain-id,不要忽略该错误
事件日志查不到区块范围错误、合约地址错误、topic 错误、节点不支持历史日志先查 latest,再缩小范围;历史日志确认 archive 能力
交易确认数不足交易刚上链或链头落后等待更多区块,或确认 RPC 是否同步落后
Token 余额看起来很大返回的是最小单位原始值使用 Token decimals 做展示换算,不要直接当业务金额
ABI 调用失败方法名、输入类型、输出类型或合约地址错误先用区块浏览器 ABI 对照,再用 raw eth_call 排查
扫描任务反复失败区块跨度过大、RPC 限流、业务 handler 抛错降低跨度,查看扫描日志,先修业务幂等与异常处理
页面返回未登录或无权限未填写 Bearer Token 或缺少 money:web3:chain:view登录后复制 token,确认 RBAC 权限

上线检查清单

  • [ ] 生产 RPC 不使用公共默认节点。
  • [ ] 每条链都配置了正确的 chain-id
  • [ ] block-explorer-url 已配置,方便后台跳转排查。
  • [ ] RPC key 和服务商地址不提交到 Git。
  • [ ] 数据库配置覆盖已验证。
  • [ ] /api/v1/money/web3/chains/diagnostics 全部必需检查项通过。
  • [ ] 生产写交易已选择签名路线:external-http 签名网关、厂商 KMS/HSM/Vault signer,或经风险确认的本地 signer。
  • [ ] 金库私钥注入方式已核对:不进 Git、不进 application.yml 明文、不进配置中心明文;配置中心入库时只存 enc:v1: 密文信封。
  • [ ] 自动放款门控已核对:金额、风控、访问策略、签名配置和目标链出金钱包余额门控均生效;严格人工审核部署已显式关闭 spring.open.money.web3.bridge.auto-payout.enabled
  • [ ] 签名网关 / KMS / HSM / Vault 的失败、超时和返回字段不一致场景已验证不会切回本地私钥 signer。
  • [ ] 测试链 web3-bridge-testchain-smoke 已完成签名 → 广播 → 确认的小额端到端记录。
  • [ ] 事件扫描检查点使用合理区块跨度。
  • [ ] 业务事件落库有唯一索引或幂等 key。
  • [ ] Web3 支付自动处理策略、批量数量、失败通知和人工重试入口已确认。
  • [ ] 写交易、批量转账和资金归集已完成小额联调,私钥没有落库或进入日志。
  • [ ] 生产后台页面不会高频轮询链健康、交易或事件日志接口。

Released under the Apache License 2.0.