Skip to main content

Crate thornode_pulse

Crate thornode_pulse 

Source
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-subscriber seq, signature), fire-and-forget, no head-of-line blocking. SigFirstSub::gaps counts 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 — see Error::BadPreamble), then length-delimited, fully-decoded transaction frames (Frame::Tx wrapping FullTxV2). 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).
CloseInfo
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 unless Filter::with_vote is true.
FullSub
Live full-tx subscription. Call FullSub::next in a loop.
FullTx
FullTxV2
A decoded v2 transaction frame: the v1 body plus its v2 additions.
PulseClient
A connected pulse client. Pick exactly one tier per connection.
PulseClientBuilder
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.
SigFirstItem
One sig-first delivery: the transaction’s slot, this subscriber’s per-connection sequence number (see SigFirstSub::gaps), and its signature.
SigFirstSub
Live sig-first subscription. Call SigFirstSub::next in a loop.

Enums§

Datagram
Error
Errors surfaced by the client.
Frame
A decoded v2 frame. Unknown carries the message type so a client can skip it deliberately rather than erroring.
RetryClass
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_seq meaning “nothing has been assigned to this subscriber yet”. 0 is a real, already-assigned sequence number (the FIRST delivery on any connection is seq == 0), so 0 cannot 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. None means the transaction set no limit; no implicit per-instruction default is applied.
compute_unit_price
Micro-lamports per compute unit from SetComputeUnitPrice (discriminator 3). None means 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_writable TLV.

Type Aliases§

Result