面向开发者 / JSON-RPC

订阅

四种订阅,跑在一个不是 HTTP 监听器的服务器上。迷你区块流就住在这里,而它是这里唯一一个你在别的链上找不到的表面。

一个独立的服务器

HTTP 端口上不存在订阅

对着 HTTP 接入点调用 eth_subscribe 会回答 -32601。节点在任何 地方都没有注册 jsonrpsee 订阅;socket 是它自己监听器上另写的一个服务器,而且它是唯一提供 订阅服务的地方。

副本节点根本没有 WebSocket 服务器:那个监听器只在主导路径上被启动。订阅是一个只属于主导 节点的表面。

接入点Value
接入点wss://rpc.picklechain.xyz/ws/公开测试网;示例把它写成 $WS_URL
你自己运行的节点ws://127.0.0.1:8546在你自己机器上启动的节点的默认 WebSocket 端口

On a deployment behind a reverse proxy, both /ws and /ws/ are served as two separate locations with no redirect between them: a WebSocket client mostly does not follow a 301 on its handshake, so whichever spelling was redirected would simply fail to connect.

握手

eth_subscribe 和 pickle_subscribe 可以互换使用,行为完全相同:

json
// ->
{"jsonrpc":"2.0","id":1,"method":"pickle_subscribe","params":["miniBlocks"]}

// <-
{"jsonrpc":"2.0","id":1,"result":"0x1"}

// <- then, per event
{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1","result":{…}}}
  • Subscription ids come from a per-connection counter starting at 1, so two connections both see 0x1. Do not treat an id as globally unique.
  • Notifications always arrive under the method name eth_subscription, even for a subscription opened as pickle_subscribe. Unwrap the payload at params.result.
  • eth_unsubscribe and pickle_unsubscribe both return a boolean: true if the id was found on this connection, false otherwise.

四种订阅

One notification per sealed EVM block.

值得注意。 The payload is a nine-field summary - number, hash, parentHash, timestamp, mixHash, baseFeePerGas, transactionCount, miniBlockFrom, miniBlockTo - and not the full header that eth_getBlockByNumber returns. transactionCount is a decimal number. A client that needs gasLimit, gasUsed or logsBloom must re-fetch the block.

miniBlocks

alias: newMiniBlocks

The preconfirmation stream: one notification per sealed mini-block, at the mini-block scheduling target. Both spellings are the same stream.

值得注意。 Mini-block fields break the eth_* hex convention on purpose: number, evmBlockNumber, timestampUs and each receipt's gasUsed are decimal JSON numbers, and the timestamp is in microseconds.

Logs as they are indexed, with the standard address and topics filtering. An absent or empty filter means every log.

值得注意。 A malformed topics filter answers -32602 before an id is allocated, so a failed subscribe leaves you with no subscription rather than a silent one.

Transaction hashes, as a bare string payload.

值得注意。 "Pending" does not mean what it means elsewhere. There is no public mempool; a hash is published only AFTER the transaction has executed and been indexed. Treat this as an executed-transaction feed, not a mempool feed.

一个完整示例

迷你区块流,附带第一方数据流所用的那条退路。退路很重要:socket 掉线是正常的,而轮询这条路 正是你不留空洞地补上进度的方式。

javascript
// WS_URL: wss://rpc.picklechain.xyz/ws/ on the public testnet, or a node of your own.
// The /ws path is required - an upgrade at the bare origin answers 405.
const socket = new WebSocket(WS_URL);

socket.addEventListener("open", () => {
  socket.send(JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "pickle_subscribe", params: ["miniBlocks"],
  }));
});

socket.addEventListener("message", (event) => {
  const message = JSON.parse(event.data);
  // The subscribe reply carries an id; notifications carry params.result.
  if (!message.params) return;
  const mini = message.params.result;

  // Decimal numbers, not hex quantities - and microseconds, not seconds.
  console.log(mini.number, mini.transactions.length, mini.timestampUs);
});

// A slow handler is DROPPED from the stream rather than buffered, so do the
// work elsewhere and keep this callback cheap.

如果 socket 关闭了,就从你最后见到的高度开始轮询 pickle_miniBlockNumber,然后 轮询 pickle_getMiniBlockByNumber。要给补进度设上界,参考客户端每一轮最多取 100 个,否则一次长时间断线会变成对着共享重读预算的一阵请求洪流。

限制与丢弃策略

限制Value
Message size256 KiBframe and message both; oversized answers -32005 "message too large"
Connections256excess connections are dropped at accept, with no error frame - your client sees a closed socket, not a rejection
Subscriptions per connection32-32005 "subscription limit reached"
Requests per second100 per connection-32005 "request rate limit exceeded"
Stream depth1024 per streama subscriber that falls behind is DROPPED from the stream rather than buffered, so a slow handler loses notifications silently. Do the work off the socket

慢订阅者会无声地丢数据

每条流都是一个深度为 1024 的广播通道。一个跟不上的订阅者会被从流里丢掉,而不是被允许去撑大 节点的内存:你拿不到错误帧,你拿到的是一个空洞。在迷你区块的调度目标下,那个缓冲区是几秒的 余量,所以任何比「推进一个队列」更重的逐事件工作,都不该放在这个 socket 上。

这些限制没有一个是环境可调的,而连接上限是在 accept 处强制执行的:第 257 个连接会在没有错误 帧的情况下被关闭,这看起来像一次网络故障,而不是一次拒绝。

在这个 socket 上的普通调用

Any frame that is not a subscribe or unsubscribe is forwarded to the same handler the HTTP port uses, so every method in the reference is callable over the socket.

错误码因传输方式而异

Error codes are re-wrapped on the way out. Every non-send failure comes back as -32603 regardless of its original code, so an eth_call revert is -32000 over HTTP and -32603 over the socket. A client that branches on the code needs to know which transport it is on.