#[non_exhaustive]pub enum Event {
Show 32 variants
MempoolEnter {
txid: Vec<u8>,
fee: u64,
vsize: u64,
fee_rate_sat_per_kvb: u64,
time: u64,
},
MempoolLeaveConfirmed {
txid: Vec<u8>,
block_hash: Vec<u8>,
height: u32,
},
MempoolLeaveEvicted {
txid: Vec<u8>,
reason: EvictReason,
},
MempoolLeaveReplaced {
txid: Vec<u8>,
replacing_txid: Vec<u8>,
},
BlockConnected {
hash: Vec<u8>,
height: u32,
},
BlockDisconnected {
hash: Vec<u8>,
height: u32,
},
Reorg {
from_height: u32,
old_tip: Vec<u8>,
to_height: u32,
new_tip: Vec<u8>,
},
Heartbeat {
uptime_ns: u64,
},
Status {
kind: StatusKind,
state: StatusState,
severity: StatusSeverity,
message: String,
details: BTreeMap<String, String>,
},
OutpointSpent {
outpoint: Outpoint,
spending_txid: Vec<u8>,
spending_vin: u32,
confirmed: bool,
},
ScriptMatched {
scripthash: Vec<u8>,
txid: Vec<u8>,
is_output: bool,
index: u32,
confirmed: bool,
amount: Option<u64>,
raw_tx: Option<Vec<u8>>,
descriptors: Vec<DescriptorMatch>,
},
TxidMatched {
txid: Vec<u8>,
confirmed: bool,
height: u32,
},
TxidReplaced {
txid: Vec<u8>,
replacing_txid: Vec<u8>,
},
TxidEvicted {
txid: Vec<u8>,
reason: String,
},
TxidUnconfirmed {
txid: Vec<u8>,
prev_height: u32,
},
TxidDepthReached {
txid: Vec<u8>,
depth: u32,
height: u32,
},
TxidFinalized {
txid: Vec<u8>,
depth: u32,
height: u32,
},
PrefixMatched(PrefixMatch),
Lagged {
dropped_count: u64,
resume_cursor: Option<Cursor>,
},
ReplayGap {
resume_height: u32,
first_height: u32,
},
CursorAccepted {
from: Option<Cursor>,
clamped: bool,
earliest_replayed: u32,
},
CursorRejected {
reason: CursorRejectReason,
current_head: Option<Cursor>,
},
WatchSetReplaced {
added: u32,
removed: u32,
unchanged: u32,
},
WatchSetRejected {
reason: WatchSetRejectReason,
required: u64,
quota: u64,
},
RescanAccepted {
from_height: u32,
to_height: u32,
clamped: bool,
},
RescanRejected {
reason: RescanRejectReason,
tip_height: u32,
},
RescanComplete {
from_height: u32,
to_height: u32,
matches: u64,
},
BlockTweaks {
block_hash: Vec<u8>,
height: u32,
entries: Vec<TweakEntry>,
filtered: bool,
},
MempoolTweak {
entry: TweakEntry,
},
SilentPaymentMatched {
scan_pubkey: Vec<u8>,
txid: Vec<u8>,
vout: u32,
output_pubkey: Vec<u8>,
amount: u64,
tweak: Vec<u8>,
k: u32,
label: Option<u32>,
confirmed: bool,
height: Option<u32>,
raw_tx: Option<Vec<u8>>,
},
WatchAddRejected(WatchAddRejected),
Unknown,
}Expand description
A typed streaming event — the flat mirror of NodeEvent.body.
Eq is not derived: the Lagged resume cursor is a
prost-generated type that is only PartialEq.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
MempoolEnter
A transaction entered the mempool.
Fields
MempoolLeaveConfirmed
A mempool transaction was confirmed in a block.
Fields
MempoolLeaveEvicted
A mempool transaction was evicted by policy.
MempoolLeaveReplaced
A mempool transaction was replaced (RBF).
BlockConnected
A block was connected to the active chain.
BlockDisconnected
A block was disconnected (reorg).
Reorg
A reorg marker, emitted once before the disconnect/connect sequence.
Fields
Heartbeat
A periodic heartbeat.
Status
A node-health condition the daemon detected about itself — a stalled
tip, a filling disk, peer starvation. Arrives only for a subscription
that set the STATUS category bit, which
is deliberately not part of the 0 (“all”) default.
Standing conditions are level-triggered: exactly one
StatusState::Raised on entry and one StatusState::Cleared on
recovery, with hysteresis so a value hovering at the threshold does not
flap. Observations with no recovered state (IBD finishing, a deep reorg
landing) are StatusState::Edge.
Not replayable, and the gap is not observable. A status event carries
no cursor, so a from_cursor resume never yields one; the node re-raises
standing conditions after a restart instead. A client that connects
after a condition was raised will not see it until the condition
changes.
This is not something the reconnect layer can paper over.
ResilientSubscription hides reconnects
by design, and its one synthetic notice
(Event::ReplayGap) is cursor-anchored — which status has no part in.
So a drop during which one condition raised and another cleared leaves a
client’s picture silently wrong, with nothing to key recovery off.
Treat this stream as the low-latency edge signal and getwarnings over
JSON-RPC as the authority on what is wrong right now: poll it on a slow
timer as well as consuming this, and a missed transition self-corrects at
the next poll instead of persisting for the life of the process. Seeding
once on connect is not enough, because reconnects after that are
invisible to you.
Match on kind and read details for the
numbers; treat StatusKind::Unknown as informational rather than an
error, since new conditions are added additively. See
[examples/health_watch.rs].
Fields
kind: StatusKindWhich condition this describes.
state: StatusStateWhether it was entered, recovered, or is a one-shot observation.
severity: StatusSeverityHow loud it is. Ordered, so a severity floor is a comparison.
details: BTreeMap<String, String>Kind-specific structured fields, as strings.
Mostly decimal numbers (free bytes and the threshold, seconds since
the last block, a reorg’s depth and fork height, …) but not
uniformly — a cleared event can carry a reason token such as
detector_disabled or mempool_cap_zero. Parse per key, not with
one integer parse over the map.
The watched path is deliberately absent: it reaches every status subscriber and every webhook receiver, and an absolute datadir path usually names the account the node runs under.
Tolerate unknown keys and absent optional ones.
OutpointSpent
A watched outpoint was spent.
Fields
ScriptMatched
A watched script was matched on the funding or spending side.
Fields
amount: Option<u64>Matched value in satoshis: the funded output value (is_output = true) or the spent-prevout value (is_output = false). Some on
the funding side and for confirmed spends; Some for mempool spends
when the node retained the prevout value (streamprevoutmeta >= amount), else None (hash tier). None lets the consumer skip the
enrichment getrawtransaction for the common single-coin case.
raw_tx: Option<Vec<u8>>Full consensus-serialized matching transaction, present only when
this stream opted in via
set_watch_options with
include_raw_tx = true; None otherwise.
descriptors: Vec<DescriptorMatch>Descriptor attribution: the descriptor watch(es) this scripthash
belongs to, if it was registered via add_descriptor. Empty for a
directly-watched script. See DescriptorMatch.
TxidMatched
A watched txid appeared in the mempool or a connected block.
Fields
TxidReplaced
A watched tx was replaced in the mempool (RBF).
TxidEvicted
A watched tx left the mempool by policy.
Fields
TxidUnconfirmed
A watched tx’s confirming block was rolled back by a reorg.
Fields
TxidDepthReached
A depth alarm fired (single-shot).
Fields
TxidFinalized
A lifecycle watch’s auto_close_depth was reached (terminal).
Fields
PrefixMatched(PrefixMatch)
A transaction fell inside a watched script-prefix bucket.
Lagged
In-band slow-consumer lag notice. Not an error: reconnect (Subscribe) or
re-anchor (Watch) with resume_cursor to recover the dropped events.
Fields
ReplayGap
SDK-synthesized — not a wire event. Emitted by
ResilientSubscription when a durable
replay was clamped by the server to the most recent MAX_REPLAY_BLOCKS
(10,000) blocks, so the confirmed history in [resume_height, first_height - 1] was skipped. The live stream continues correctly from
first_height; the gap is unrecoverable via this stream, so full-resync
the skipped range from another source (e.g. RPC getblock). Emitted once
per resume, immediately before the first replayed block.
Detection limit — absence of this event is not proof of a complete
scan. It is synthesized by comparing the resume cursor against the
first BlockConnected delivered. A subscription that filtered chain
events out — a tweaks-only silent-payment scan, say — receives no
BlockConnected, so a clamp it suffered passes unremarked here. Such a
subscription is normally protected server-side instead (a tweaks-only
replay against a complete index is exempt from the clamp, and a node
whose index is incomplete refuses the subscribe outright rather than
clamping it), but that protection lives on the node, not in this event.
Do not treat this variant as a wallet’s only guard against a silent hole.
Fields
CursorAccepted
A mid-stream re-anchor (WatchHandle::set_cursor)
was admitted. Confirmed-history replay follows this event (in height
order) before the live tail resumes. When clamped is true the requested
cursor predated the server’s replay window: replay still runs, but only
from earliest_replayed, so full-resync history below it from another
source. This is the deterministic “accepted, replaying from X” signal.
Fields
CursorRejected
A mid-stream re-anchor (WatchHandle::set_cursor)
was not admitted. The live stream is unchanged (still emitting from
its prior position). Decide whether to retry, back off, or escalate to a
full resnapshot based on reason; current_head is where the server is
now.
Fields
reason: CursorRejectReasonWhy the re-anchor was declined.
WatchSetReplaced
An atomic watch-set replace (ResilientWatch::reload,
carried as SetWatchSet) was applied. The live watch-set now equals the
reloaded truth; the counts are the server’s authoritative diff by
effective coverage (a scripthash covered by both the old and new set —
even via a different mechanism — counts as unchanged, never gapped).
Fields
WatchSetRejected
An atomic watch-set replace was rejected; the live watch-set is
UNCHANGED (the prior set is still in effect). reason says which ceiling
refused it, and required/quota are in the matching unit:
QuotaExceeded — required units
vs the quota ceiling; CapExceeded
— required entries vs the per-connection entry cap (quota);
Malformed — a client bug (bad element),
required/quota are 0 and retrying the same set will not help. In every
case the client’s mirror still reflects the (unapplied) reloaded set, so a
consumer that ignores this keeps an over-claiming mirror; react by
reloading a set the server accepts.
Fields
reason: WatchSetRejectReasonWhy the replace was refused.
RescanAccepted
A bounded historical rescan (ResilientWatch::rescan)
was admitted. Confirmed watch-matches for the scanned range follow
this event (in height order), terminated by a RescanComplete.
from_height/to_height are the range the server will ACTUALLY scan:
clamped is true when the requested upper bound exceeded the tip and was
narrowed to it. A rescan is a side query — it does not advance the durable
cursor, and its match events carry no resume cursor.
Fields
RescanRejected
A bounded historical rescan was not admitted; no matches follow and
the live stream is unchanged. tip_height is the server’s current tip so
a client can re-scope the range and retry.
Fields
reason: RescanRejectReasonWhy the rescan was declined.
RescanComplete
Terminal marker for a bounded historical rescan: the range has been fully
scanned and every match delivered. matches counts the match events this
rescan emitted (0 when the range held none). After this the stream resumes
its prior live position.
Fields
from_height: u32The scanned range (post-clamp), echoing RescanAccepted.
BlockTweaks
One connected block’s silent-payment tweaks — the Tier 1 (client-side
scan, zero-custody) firehose payload. Arrives only for a subscription that
set the TWEAKS category bit. For each entry,
a client computes T · b_scan with its own scan secret and derives the
candidate output key(s), so the scan key never leaves the device. See
[examples/sp_light_scan.rs].
Fields
entries: Vec<TweakEntry>One entry per silent-payment-eligible transaction in the block.
MempoolTweak
One accepted-but-unconfirmed transaction’s silent-payment tweak — the
Tier 1.5 (mempool-time, zero-custody) firehose payload. Arrives only for a
subscription that set the TWEAKS category
bit and SubscribeOptions::mempool_tweaks.
Scan it exactly like a BlockTweaks entry — compute
T · b_scan and derive the candidate output key(s) — for mempool-latency
detection. Ephemeral and best-effort: not replayable, no retraction on
RBF/eviction; dedup by entry.txid (always present) against the confirmed
BlockTweaks. See [examples/sp_light_scan.rs].
Fields
entry: TweakEntryThe admitted transaction’s tweak. Always full (txid present) —
tweaks_only does not apply to mempool tweaks.
SilentPaymentMatched
A BIP 352 silent payment paid a registered scan key — the Tier 2
(scan-key watch, convenience) match, delivered on the Watch stream to a
connection that registered the target via
WatchHandle::add_silent_payments.
The node ran the ECDH; tweak + k let the wallet re-derive the output
key — and, with its b_spend, the spending key — offline. See
[examples/sp_wallet.rs].
Fields
scan_pubkey: Vec<u8>The registered target’s identity b_scan·G (33 bytes) this output
paid — echoes which of your scan keys matched, never the secret.
txid: Vec<u8>The paying transaction’s txid (internal byte order — parse_txid /
display_hex).
tweak: Vec<u8>The transaction’s 33-byte public tweak T. With k, re-derive the
full output key offline: P_k = B_spend + hash(b_scan·T || k)·G.
label: Option<u32>The matched label integer, when the output paid a registered label
(e.g. change is commonly label 0); None for an unlabeled match.
WatchAddRejected(WatchAddRejected)
An incremental watch add (add_scripts, add_outpoints, …) that the
server did not register: over the quota, over the per-add rate
limit, over a per-connection cap, without stream:watch, or malformed.
None of the items it names is watched. Items the add re-asserted
(already watched) are not named and stay watched. The server sends
nothing for an add that registered.
ResilientWatch re-sends a
RateLimited add itself after the
server’s retry hint, within its backoff budget, and hands the event on
only once that budget runs out. For any other reason, or then, it drops
the named items from its mirror before handing this on, so a reconnect
does not re-register them; re-add them yourself if you want another try.
Unknown
A body this client build does not recognize (a newer server arm), or an event with no body set. Ignored by well-behaved consumers.
Trait Implementations§
impl StructuralPartialEq for Event
Auto Trait Implementations§
impl Freeze for Event
impl RefUnwindSafe for Event
impl Send for Event
impl Sync for Event
impl Unpin for Event
impl UnsafeUnpin for Event
impl UnwindSafe for Event
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request