Web3 API 网关可以认证应用调用方、分配共享节点容量、路由 RPC 流量,并提供运维证据。但它无法证明 API 调用方拥有某个钱包,也不能验证区块链共识、让被拒绝的交易变为有效交易,或把公共 RPC 端点变成可信身份系统。
设计时真正要解决的问题是:既保护稀缺的节点和服务商容量,又不改变钱包、索引器与应用所依赖的 JSON-RPC 行为。
核心要点
- 把应用凭据、网络身份、钱包所有权与交易签名视为不同的信任域。
- 为低成本读取、高成本历史查询、订阅与交易提交设置明确且独立的策略。
- 保留 JSON-RPC 响应体与 ID;HTTP
200响应中仍可能包含 JSON-RPC 错误。 - 对已认证 Consumer 限流,但共享计数器不可用时不能在没有说明的情况下失败开放。
- 节点超时并不代表可以重放交易提交;应先查询交易状态并完成对账。
先明确网关负责什么
以太坊应用通常通过 JSON-RPC 与节点通信。该协议在请求层无状态,但不同方法的成本与风险差别很大。
| RPC 工作负载 | 示例 | 网关职责 | 节点或应用职责 |
|---|---|---|---|
| 当前状态读取 | eth_blockNumber | 准入、路由,以及对安全读取进行有界重试 | 规范链视图与结果正确性 |
| 历史查询 | 对大范围执行 eth_getLogs | Consumer 预算与请求边界 | 索引可用性与查询执行 |
| 交易提交 | eth_sendRawTransaction | 认证、准入与一次有界投递 | 签名与交易验证、Mempool 策略 |
| 订阅 | 通过 WebSocket 执行 eth_subscribe | 连接数与消息预算 | 订阅状态与事件语义 |
| 管理 RPC | 客户端专用管理方法 | 默认禁止公开暴露 | 节点管理与授权 |
多数基于 HTTP 的 JSON-RPC 调用共用一个 URL 与 POST 方法,操作名位于请求体中。因此,只看路径的网关规则无法自动区分低成本读取与高成本 Trace 调用。需要按方法控制时,应使用不同的公共端点或理解 RPC 的策略组件;同时测试批量请求,避免一个 HTTP 请求在后端放大成大量工作。
分清四种身份
1sequenceDiagram
2 participant A as Web3 应用
3 participant G as API 网关
4 participant P as RPC 策略与节点池
5 participant N as 区块链节点
6 participant C as 区块链网络
7 A->>G: JSON-RPC 与应用凭据
8 G->>G: 认证 Consumer 并执行配额
9 G->>P: 获准请求与 Consumer 上下文
10 P->>P: 检查方法与网络策略
11 P->>N: 一次有界 RPC 尝试
12 N->>C: 读取状态或提交已签名交易
13 C-->>N: 网络结果
14 N-->>P: JSON-RPC 结果或错误
15 P-->>G: 保留 ID 与 RPC 语义
16 G-->>A: 响应与网关关联数据API Key 或访问令牌标识网关中配置的 Consumer。IP 地址只有在部署的可信代理规则成立时,才能标识网络来源。钱包地址只是公开标识符。有效交易签名能够证明签名密钥持有者批准了该笔交易,却不能证明 HTTP 调用方是法律意义上的所有者、获授权员工,或获取网关凭据的同一主体。
每项授权判断都应绑定正确的身份。例如,客户套餐可以依据已认证 Consumer ID 计量;托管钱包流程则必须使用自己的钱包审批与签名控制。切勿根据 X-Forwarded-For、API Key 名称或调用方提供的地址字段推断钱包所有权。
用明确的故障行为保护共享 RPC 容量
下面是 Apache APISIX 3.19.0 的配置片段,用于说明 Schema,不是完整部署。它假设已经创建 Consumer 与 key-auth 凭据,内部 RPC 代理会执行方法与链策略。示例有意省略 Redis 高可用、上游 TLS、健康检查、CA 信任库供应与机密信息供应。
1{
2 "uri": "/ethereum",
3 "methods": ["POST"],
4 "plugins": {
5 "key-auth": {
6 "header": "apikey",
7 "hide_credentials": true
8 },
9 "limit-count": {
10 "count": 120,
11 "time_window": 60,
12 "key_type": "var",
13 "key": "consumer_name",
14 "rejected_code": 429,
15 "policy": "redis",
16 "redis_host": "redis.internal",
17 "redis_port": 6379,
18 "redis_password": "$ENV://RPC_RATE_LIMIT_REDIS_PASSWORD",
19 "redis_ssl": true,
20 "redis_ssl_verify": true,
21 "redis_server_name": "redis.internal",
22 "allow_degradation": false
23 }
24 },
25 "upstream": {
26 "type": "roundrobin",
27 "nodes": {
28 "rpc-policy.internal:8080": 1
29 }
30 }
31}key-auth 插件的 hide_credentials 默认为 false:false 路径会把凭据留在转发请求中,true 路径则会在代理前移除凭据。本例选择 true,所以上游收到的是 APISIX Consumer 上下文,而不是可重复使用的 Key。但该上下文仍只代表已认证的 API Consumer,不代表钱包身份。
limit-count 插件的 allow_degradation 默认为 false。取值为 false 时,计数器后端故障会返回 HTTP 500;取值为 true 时,APISIX 会跳过限流并继续处理请求。本例保留失败关闭的默认值,因为未被察觉的失败开放路径可能耗尽共享 RPC 容量。应针对每类工作负载主动选择,并记录运维后果。
Redis 连接的 redis_ssl 默认为 false:false 路径不使用 TLS 发送计数器流量,true 路径则会加密连接。启用 TLS 后,redis_ssl_verify 同样默认为 false:false 路径不会认证 Redis 服务端证书,true 路径则会验证证书。启用验证时,redis_server_name 是 TLS SNI 名称,而且必须与证书匹配;它默认取 redis_host,因此当 Host 是证书未覆盖的别名时,需要显式设置。该边界通过 DNS、SNI 与已配置的 CA 信任库认证指定的 Redis 端点,并不会认证 API Consumer、钱包所有者或交易签名者。
show_limit_quota_header 默认为 true,会发送配额请求头;设为 false 只会隐藏这些请求头,不会关闭限流。无论选择哪条路径,都不会让 Consumer 提供的身份变得更可信。
保留 JSON-RPC 错误,不要把它们压成一类
EIP-1474 定义了以太坊 RPC 请求与响应,并列出了标准及以太坊专用错误码。网关需要同时观测两层错误:
- HTTP 与传输故障,例如
401、429、超时或上游连接失败; - 响应体中的 JSON-RPC 错误,例如参数无效对应
-32602、交易被拒对应-32003、超出已定义限制对应-32005。
不要把所有 JSON-RPC 错误都改写成泛化的 HTTP 成功指标。仪表盘应解析这两层,客户端关联响应时应保留请求 id。如果网关在 RPC 服务运行前生成过载响应,需要明确它返回 HTTP 错误、JSON-RPC 错误对象,还是两者同时返回。保持一致比假装两层错误完全相同更重要。
批量请求需要独立预算。如果数组中包含许多操作,只把一个 HTTP Envelope 计为一次请求会低估节点工作量。RPC 感知层应限制元素数量、请求体总大小、允许的方法与各操作成本。
按后果路由读取与写入
至少应为以下工作负载建立明确的节点池:
- 可由副本处理的当前状态读取;
- 需要高成本历史状态的 Archive 或 Trace 查询;
- 涉及重复投递与 Mempool 行为的交易提交;
- 消耗长连接的 WebSocket 订阅;
- 不应与公共 Listener 共用的内部管理操作。
即使是安全读取,重试也需要预算与一致性策略。区块传播或重组期间,两个健康节点可能报告不同的链头。记录选定网络、节点、Block Tag、延迟与响应类型,应用才能解释重复读取为何得到不同结果。
对于 eth_sendRawTransaction,超时意味着结果不确定:节点可能已经接收已签名交易,只是响应丢失。不要把它当作普通重试策略自动提交给多个服务商。应先推导或保留交易 Hash,查询已知节点,再根据应用的 Nonce 与替换策略对账。这些区块链语义不属于网关职责。
观测成本与滥用,同时避免记录机密信息
分别测量 HTTP 请求数与 JSON-RPC 操作数。有效维度包括已认证 Consumer、网络、方法类别、批量大小、请求与响应字节、节点池、延迟、HTTP 状态、JSON-RPC 错误码,以及缓存或重试结果。
默认不要记录原始已签名交易、认证凭据、完整钱包历史或不受限制的请求体。即使区块链数据公开,交易负载与地址活动仍可能敏感。启用负载日志前,应先定义留存、访问、脱敏与事故用途规则。
应结合行为识别滥用,而不是只看一个信号:大范围历史查询、重复失败的模拟、高 Batch 扇出、快速轮换凭据、连接抖动,以及消耗不成比例节点资源的方法组合。IP 地址可以辅助流量控制,但代理、共享网络和攻击者轮换都会使它无法成为可靠的账户身份。
Web3 网关验证清单
- 应用身份、IP 来源、钱包地址与签名权限是否彼此分离?
- 策略能否区分读取、历史查询、交易提交与订阅?
- 是否限制 Batch 元素数、请求体大小与方法成本?
- 每条路由在计数器后端故障时采用失败关闭还是失败开放,是否有明确说明?
- HTTP 故障与 JSON-RPC 错误是否都可观测?
- 交易超时后是否先查询状态,再决定是否重新提交?
- 管理 RPC 方法是否与公共流量隔离?
- 日志是否避免凭据与不必要的交易或地址数据?
常见问题
网关会验证区块链交易吗?
它可以验证粗粒度的请求结构与准入策略。区块链节点根据协议和本地 Mempool 规则验证已签名交易;交易是否纳入区块并最终确认,则由共识决定。
是否应该用钱包地址作为配额 Key?
默认不应该。调用方可以提交自己并不控制的地址。除非存在独立签名 Challenge,把调用方与该操作所用钱包绑定,否则应使用已认证 Consumer 或其他经过验证的账户身份。
读取请求可以缓存吗?
部分不可变或明确指定 Block 的读取可以缓存。相对于链头的查询与 Pending 状态会不断变化。缓存前应定义 Cache Key、Block 引用、新鲜度、重组行为与隐私边界。
后续步骤
把本方案与分布式限流一致性、IP 信任与访问名单运维以及细粒度授权结合使用。
