Expand description
Rust client SDK for the pulse QUIC decoded-shred transaction stream (wire v2).
Two tiers, one connection each (the server selects the tier from the first
control message, which this SDK always negotiates to wire v2 —
thornode_pulse_wire::frame::WIRE_VERSION):
PulseClient::subscribe_sig_first— the low-latency sig-first tier. One QUIC DATAGRAM per tx (SigFirstItem: slot, per-subscriberseq, signature), fire-and-forget, no head-of-line blocking.SigFirstSub::gapscounts sequence numbers this subscriber may not have received (see its docs for the exact, honest guarantee — QUIC datagrams are unordered, so it over-reports under reordering).PulseClient::subscribe_full— the full-tx tier. A single ordered QUIC stream that opens with a 6-byte preamble (this SDK reads and verifies it before the subscription is ever handed back — seeError::BadPreamble), then length-delimited, fully-decoded transaction frames (Frame::TxwrappingFullTxV2). Stream bytes are ordered/reliable after the server enqueues them; the server’s bounded pre-stream queue may shed transactions before that point.
Both tiers also carry periodic heartbeats (idle-stream liveness, plus
highest_seq — the highest sequence number assigned to this subscriber so
far; u64::MAX means none yet, see NO_SEQ_ASSIGNED). A heartbeat is
folded into FullSub::heartbeat / SigFirstSub::gaps rather than
handed back as an item, and a message or datagram type this SDK doesn’t
recognize is skipped rather than treated as an error — that is what keeps
a future wire addition from breaking this client (see Frame::Unknown /
Datagram::Unknown).
use thornode_pulse::{Filter, PulseClient};
let endpoint = std::env::var("PULSE_ADDR")
.expect("set PULSE_ADDR to <HOST:PORT_FROM_DASHBOARD>");
let token = std::env::var("PULSE_TOKEN")
.expect("set PULSE_TOKEN to <TOKEN_FROM_SAME_LOCATION>");
let account = std::env::var("PULSE_ACCOUNT")
.expect("set PULSE_ACCOUNT to <ACCOUNT_OR_PROGRAM_PUBKEY>");
let client = PulseClient::connect_with_token(endpoint, token).await?;
let mut sub = client.subscribe_sig_first(&Filter::accounts([account])).await?;
while let Some(item) = sub.next().await? {
println!("slot {} seq {} sig {}", item.slot, item.seq, bs58::encode(item.signature).into_string());
}The wire protocol is documented in docs/PROTOCOL.md. Frame/datagram
decoders and derived-field helpers are provided by thornode-pulse-wire
and re-exported here.
Structs§
- Ack
- A parsed
{"type":"...","ok":bool,...}control-channel envelope — the server’s answer to any control message (first or update). - Close
Info - A terminal QUIC application close sent by the Pulse server.
- Filter
- Subscription filter — the account predicate model the server applies. An
empty account filter (
Filter::all) selects the unfiltered non-vote feed; the access selected for the connection determines whether that feed is available. Vote transactions remain excluded unlessFilter::with_voteistrue. - FullSub
- Live full-tx subscription. Call
FullSub::nextin a loop. - FullTx
- Full
TxV2 - A decoded v2 transaction frame: the v1 body plus its v2 additions.
- Pulse
Client - A connected pulse client. Pick exactly one tier per connection.
- Pulse
Client Builder - Builder for verified production connections. Native system roots are always loaded; custom CA certificates are additive, which supports private PKI without weakening verification for public endpoints.
- SigFirst
Item - One sig-first delivery: the transaction’s slot, this subscriber’s
per-connection sequence number (see
SigFirstSub::gaps), and its signature. - SigFirst
Sub - Live sig-first subscription. Call
SigFirstSub::nextin a loop.
Enums§
- Datagram
- Error
- Errors surfaced by the client.
- Frame
- A decoded v2 frame.
Unknowncarries the message type so a client can skip it deliberately rather than erroring. - Retry
Class - What a client should do after a terminal application close.
Constants§
- ACK_
TIMEOUT - How long the complete control round-trip (open, write and ack read) may
take before failing with
Error::AckTimeout. - CONNECT_
TIMEOUT - Bound for DNS resolution plus the QUIC/TLS handshake.
- DEFAULT_
RECV_ BUFFER - UDP receive buffer requested for the client socket, in bytes.
- FULL_
STREAM_ TIMEOUT - Bound for the server to open and preface the full-tx stream after acking.
- NO_
SEQ_ ASSIGNED - Wire sentinel for a heartbeat’s
highest_seqmeaning “nothing has been assigned to this subscriber yet”.0is a real, already-assigned sequence number (the FIRST delivery on any connection isseq == 0), so0cannot double as “none” — conflating the two would tell a client it already missed transaction 0 the instant it connected. - SIG_
QUEUE_ LEN - Depth of the sig-first handoff queue.
Functions§
- compute_
unit_ limit - Explicit
SetComputeUnitLimit(discriminator 2) only.Nonemeans the transaction set no limit; no implicit per-instruction default is applied. - compute_
unit_ price - Micro-lamports per compute unit from
SetComputeUnitPrice(discriminator 3).Nonemeans the transaction set no price — NOT zero. - fee_
payer - The fee payer is always the first account key.
- program_
ids - Every program the transaction invokes, in first-use order, deduplicated. Solana forbids an ALT-sourced program id, so this is complete without any lookup-table resolution.
- static_
writable_ accounts - Writable accounts drawn from the STATIC key array only. ALT-loaded writables
arrive separately in the frame’s
loaded_writableTLV.