📥 接收解码交易

Decoded Shred Stream 通过 UDP 推送到您在数据流详情页配置的 IP 和端口——无连接、无握手、无重传。每个数据报最大 1,408 字节,携带一笔完整的交易:16 字节二进制报头、slot,以及 Solana 标准线格式的交易。


📦 信封报头(16 字节)

每个数据报都以相同的固定报头开头。所有整数均为小端序(little-endian)

偏移大小字段取值 / 语义
02magic0x5AE7(u16 LE)——其他任何值 ⇒ 拒绝该数据报
21version2 —— 拒绝任何未知版本
31msg_type2 = DecodedTx(1 = 保留)
41flags保留,0
51frag_index分片索引,从 0 开始——解码交易始终为 0
61frag_count分片总数——解码交易始终为 1(从不分片)
71_pad0
88sequ64 LE,在每个产品流内单调递增

🧾 载荷(msg_type = 2)

紧随报头之后:

字段大小语义
slot8u64 LE —— 该交易所属的 slot
transaction数据报剩余部分Solana 标准线格式(bincode 序列化的 VersionedTransaction

交易字节与任何 Solana SDK 所期望的完全一致——可原样喂给您现有的解析器。解码交易从不分片:一个数据报始终是一笔完整交易。投票交易默认排除


📉 通过 seq 检测丢包

UDP 不会重传。每个数据报都携带一个在您的数据流内每次恰好加 1 的 seqseq 出现缺口即表示丢失了相应数量的数据报:

text
收到 seq = 1042,上一个 = 1039 → 丢失 2 个数据报

记录您看到的最后一个 seq 并在出现缺口时告警——持续丢包通常意味着接收缓冲区太小(见下文)或链路上存在瓶颈。


🔧 缓冲区设置

数据流具有突发性——一个繁忙的 slot 可能连续送达大量交易。请为套接字设置充裕的接收缓冲区SO_RCVBUF,若干 MB),让短促的突发被吸收而不是被内核丢弃,并在一个专用的快速循环中持续读空套接字。


💻 最小解码器(Python)

python
import socket, struct
sock = 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 = None
while True:
dgram = sock.recv(2048)
if len(dgram) < 24:
continue
magic, version, msg_type = struct.unpack_from("<HBB", dgram, 0)
if magic != 0x5AE7 or version != 2:
continue # 拒绝未知的 magic/version
if 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 完全相同。

rust
// cargo add decoded-shredstream tokio --features tokio/macros,tokio/rt-multi-thread
use 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 = 0x5AE7FRAME_VERSION = 2FRAME_HEADER_LEN = 16MAX_DATAGRAM = 1408,以及 parse_headerStreamDecoder),因此您可以解码从自己管理的套接字读取的数据报——Go 客户端通过 ParseHeader / StreamDecoder 提供同样的能力:

rust
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 交付 — 同样的交易,通过有序流交付,并支持服务端账户过滤。
接收解码交易 — Docs | ShredStream.com