📥 接收解码交易
Decoded Shred Stream 通过 UDP 推送到您在数据流详情页配置的 IP 和端口——无连接、无握手、无重传。每个数据报最大 1,408 字节,携带一笔完整的交易:16 字节二进制报头、slot,以及 Solana 标准线格式的交易。
📦 信封报头(16 字节)
每个数据报都以相同的固定报头开头。所有整数均为小端序(little-endian)。
| 偏移 | 大小 | 字段 | 取值 / 语义 |
|---|---|---|---|
| 0 | 2 | magic | 0x5AE7(u16 LE)——其他任何值 ⇒ 拒绝该数据报 |
| 2 | 1 | version | 2 —— 拒绝任何未知版本 |
| 3 | 1 | msg_type | 2 = DecodedTx(1 = 保留) |
| 4 | 1 | flags | 保留,0 |
| 5 | 1 | frag_index | 分片索引,从 0 开始——解码交易始终为 0 |
| 6 | 1 | frag_count | 分片总数——解码交易始终为 1(从不分片) |
| 7 | 1 | _pad | 0 |
| 8 | 8 | seq | u64 LE,在每个产品流内单调递增 |
🧾 载荷(msg_type = 2)
紧随报头之后:
| 字段 | 大小 | 语义 |
|---|---|---|
slot | 8 | u64 LE —— 该交易所属的 slot |
transaction | 数据报剩余部分 | Solana 标准线格式(bincode 序列化的 VersionedTransaction) |
交易字节与任何 Solana SDK 所期望的完全一致——可原样喂给您现有的解析器。解码交易从不分片:一个数据报始终是一笔完整交易。投票交易默认排除。
📉 通过 seq 检测丢包
UDP 不会重传。每个数据报都携带一个在您的数据流内每次恰好加 1 的 seq。seq 出现缺口即表示丢失了相应数量的数据报:
收到 seq = 1042,上一个 = 1039 → 丢失 2 个数据报
记录您看到的最后一个 seq 并在出现缺口时告警——持续丢包通常意味着接收缓冲区太小(见下文)或链路上存在瓶颈。
🔧 缓冲区设置
数据流具有突发性——一个繁忙的 slot 可能连续送达大量交易。请为套接字设置充裕的接收缓冲区(SO_RCVBUF,若干 MB),让短促的突发被吸收而不是被内核丢弃,并在一个专用的快速循环中持续读空套接字。
💻 最小解码器(Python)
import socket, structsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8 * 1024 * 1024)sock.bind(("0.0.0.0", 5000)) # 您在仪表盘上配置的端口expected = Nonewhile True:dgram = sock.recv(2048)if len(dgram) < 24:continuemagic, version, msg_type = struct.unpack_from("<HBB", dgram, 0)if magic != 0x5AE7 or version != 2:continue # 拒绝未知的 magic/versionif msg_type != 2:continue # 不是 DecodedTx 数据报(seq,) = struct.unpack_from("<Q", dgram, 8)if expected is not None and seq != expected:print(f"gap: {seq - expected} datagram(s) lost")expected = seq + 1(slot,) = struct.unpack_from("<Q", dgram, 16)tx_bytes = dgram[24:] # 标准线格式——可喂给任何 Solana 交易解析器
🦀 官方客户端与 codec 模块
您不必自己编写上面的解码器:官方 decoded-shredstream 客户端(Rust、Go、JavaScript/TypeScript、Python)会绑定端口、解析信封、检测 seq 缺口,并把每笔交易连同其 slot 和签名一起交给您——UDP 和 gRPC 均可,API 完全相同。
// cargo add decoded-shredstream tokio --features tokio/macros,tokio/rt-multi-threaduse decoded_shredstream::{UdpClient, UdpConfig};#[tokio::main]async fn main() -> std::io::Result<()> {let mut client = UdpClient::bind(UdpConfig { port: 8002, ..Default::default() })?;while let Some(update) = client.next_update().await {println!("slot={} sig={} {}B", update.slot(), update.signature(), update.bytes().len());}Ok(())}
Rust crate 还以 codec 模块的形式暴露该帧格式(常量 FRAME_MAGIC = 0x5AE7、FRAME_VERSION = 2、FRAME_HEADER_LEN = 16、MAX_DATAGRAM = 1408,以及 parse_header、StreamDecoder),因此您可以解码从自己管理的套接字读取的数据报——Go 客户端通过 ParseHeader / StreamDecoder 提供同样的能力:
use std::net::UdpSocket;use decoded_shredstream::codec::{Push, StreamDecoder, MAX_DATAGRAM};fn decode_loop(socket: &UdpSocket) -> std::io::Result<()> {let mut decoder = StreamDecoder::new();let mut buf = [0u8; MAX_DATAGRAM + 64];loop {let n = socket.recv(&mut buf)?;if let Push::Update(update) = decoder.push(&buf[..n]) {println!("slot={} sig={}", update.slot(), update.signature());}}}
安装:npm install decoded-shredstream · pip install decoded-shredstream · cargo add decoded-shredstream · go get github.com/shredstream/decoded-shredstream-go。
➡️ 后续步骤
- Decoded Shred Stream —— 定位、延迟和交付模式。
- gRPC 交付 — 同样的交易,通过有序流交付,并支持服务端账户过滤。