WebSocket
Gateway 在与其 REST API 相同的主机和端口上,于 /ws 提供 WebSocket 服务:
ws://localhost:3000/ws无需认证。Gateway 会向每个已连接的客户端推送链上事件和 AI 事件;客户端也可以在同一个 socket 上订阅余额并调用一组 RPC 方法。
连接
js
const ws = new WebSocket('ws://localhost:3000/ws')
ws.onmessage = (e) => {
const msg = JSON.parse(e.data)
switch (msg.type) {
case 'init': console.log('height', msg.data.height); break
case 'new_block': console.log('block', msg.data.height, msg.data.proposer); break
case 'tx_held': console.warn('held', msg.data.tx_hash); break
}
}服务器发出的每条消息都是带有 type 的 JSON。事件把载荷放在 data 中;balance、rpc_reply 和 error 则把各自的字段放在顶层。
在 close 时以退避策略重连——Web 应用会以指数增长的延迟重试。
事件
Gateway 每隔 POLL_NODE_INTERVAL_MS(默认 2 000 ms)轮询一次节点,并把观察到的变化转换为事件。
type | 触发时机 | data 字段 |
|---|---|---|
init | 连接建立后立即发送 | height、tps、finality_ms、shard_count、validator_count、blocks、txs、total_blocks、total_txs |
metrics | 每次轮询 | tps、height、finality_ms |
new_block | 出现新的高度 | 与 GET /api/v1/blocks/:height 返回的区块相同 |
validator_update | 每 10 个区块 | nodes、total_staked、height |
tx | Gateway 已将一笔交易转发给节点 | hash、from、to、value、gas、gas_price、shard、timestamp、block |
faucet | 水龙头向某地址发放了资金 | address、amount、tx_hash |
tx_held | 一笔转账被 Sentinel 策略暂扣 | tx_hash、to、value_human |
tx_released | 一笔被暂扣的转账已放行 | tx_hash、to、value_human |
tx_cancelled | 一笔被暂扣的转账已取消 | tx_hash、to、value_human |
tx_anomaly | Sentinel 将一笔已提交的交易判定为异常 | tx_hash、score、category |
oracle_decision | 记录了一条 AI 决策 | model、kind、subject、decision、confidence、timestamp |
model_voting_update | 有 AI 模型正在等待验证者确认 | models、height |
contract_alert | 某合约调用量异常,或某低分合约开始被调用 | address、alert、severity、call_count、delta、timestamp、model |
tx 只是提交通知
tx 事件在 gateway 把交易交给节点时触发,此时交易尚未进入区块——其 status 字段不具权威性。请通过 new_block、余额订阅或 GET /api/v1/transactions/:hash 跟踪最终性。
合约告警每隔 SENTINEL_CHECK_INTERVAL_MS(默认 30 000 ms)检查一次。
余额订阅
js
ws.send(JSON.stringify({ type: 'subscribe_balance', address: 'neuro:<15 hex>.human' }))Gateway 会立即回复一次,之后每个新区块后再回复一次:
json
{ "type": "balance", "address": "neuro:…", "balance": 50000000, "balance_human": "50", "nonce": 0 }socket 关闭时订阅即结束。
通过 socket 调用 RPC
js
ws.send(JSON.stringify({ type: 'rpc', id: 1, method: 'get_account', params: { address: 'neuro:…' } }))
// → { "type": "rpc_reply", "id": 1, "result": { … }, "error": null }这些方法与 REST 端点一一对应,返回相同的数据:
| 分组 | 方法 |
|---|---|
| 链 | get_status、get_blocks、get_block、get_block_txs、get_txs、search_tx、get_account |
| 验证者 | get_validator、get_validators |
| 名称与 NFT | human_name_price、register_human、get_my_nfts、get_nft、transfer_nft |
| 治理 | get_governance_params、get_proposals、get_proposal、create_proposal、cast_vote |
| 保险库 | get_vault_config、list_member_vaults、get_vault_proposals、create_vault_proposal、sign_vault_proposal、add_vault_member、set_vault_threshold |
| AI | get_oracle_log、get_oracle_entry、get_model_registry、get_model_registry_entry、register_model |
| 合约 | get_contracts、get_contract、get_contract_events、deploy_contract、call_contract、update_contract_score、get_certificates、get_certificate、get_contract_certificate、certify_contract |
| 测试网络 | faucet |
写方法需要在 params 中携带与其 REST 端点相同的签名字段(signature、public_key、nonce)。
限制
| 限制 | 默认值 | 变量 | 超出时 |
|---|---|---|---|
| 消息大小 | 64 KB | WS_MAX_MSG_BYTES | {"type":"error","error":"message too large"} |
| 每个连接的余额订阅数 | 50 | WS_MAX_SUBS_PER_CLIENT | subscription limit exceeded |
| 每个连接每秒的 RPC 调用数 | 30 | WS_RPC_MAX_RPS | rpc rate limit exceeded |
| 失效连接清理 | 每 30 s | WS_STALE_CHECK_MS | — |
Python
python
import asyncio, json, websockets
async def listen():
async with websockets.connect("ws://localhost:3000/ws") as ws:
await ws.send(json.dumps({"type": "subscribe_balance", "address": "neuro:<15 hex>.human"}))
async for raw in ws:
msg = json.loads(raw)
print(msg["type"], msg.get("data", msg))
asyncio.run(listen())