Firedancer WebSocket API
Firedancer provides an optional HTTP websockets API for consumers to subscribe to validator information. It primarily exists in the current form to support the Firedancer GUI.
WARNING
The API is not currently stable, is not versioned, may not exist for long, may break or start producing incorrect data at any moment, and should not generally be used for anything without extreme caution.
Connecting
To connect to the API, create a WebSocket client from the language of your choice, for example in JavaScript
client = new WebSocket("ws://localhost:80/websocket");[tiles]
[tiles.gui]
listen_port = 80The port to connect to is specified in the validator configuration TOML file.
The API is split into various topics which will be streamed to any and all connected clients.
Compression
If configured properly, the server can optionally compress messages larger than 200 bytes. In order to enable this feature, the client must specify the compress-zstd subprotocol in the opening websocket handshake.
client = new WebSocket("ws://localhost:80/websocket", protocols=['compress-zstd']);
client.binaryType = "arraybuffer";ws.onmessage = function onmessage(ev: MessageEvent<unknown>) {
if (typeof ev.data === 'string') {
... parse string
} else if (ev.data instanceof ArrayBuffer) {
... decompress then parse
}
};In order to distinguish between compressed and non-compressed messages, the server will send compressed messages as a binary websocket frame (i.e. opcode=0x2) and regular messages as a text websocket frame (i.e. opcode=0x1).
Keeping Up
The server does not drop information, slow down, or stop publishing the stream of information if the client cannot keep up. A client that is reading too slow and cannot keep up with incoming data stream will have its connection forcibly closed by the server.
Most data updates are streamed in real time as the changes occur except certain updates (performance counters like packet counters) which would change too quickly, which are instead republished on a regular frequency described below.
Each message is published with frequency described in the documentation below. The meaning of these frequencies are:
| Frequency | Meaning |
|---|---|
| Once | The message is published only once, immediately after a connection is established |
| Live | The message is published live, immediately after the underlying data in the validator is changed |
| Request | The message is published in response to a specific client request |
| 1s | The message is republished at regular one second intervals |
| Once + Live | The message is published immediately after a connection is established, and then republished whenever the data is changed |
Most information related to the state of the validator is sent both Once when the connection is established, and then live whenever it is updated.
All data is encoded in JSON, with a containing envelope as follows:
{
"topic": "summary",
"key": "cluster",
"value": "mainnet-beta",
}Queries
Some messages are published on-demand in response to a request, and are marked with a frequency of Request. To issue a query, send a websocket frame to the server with an envelope like:
{
"topic": "slot",
"key": "query",
"id": 42,
"params": {
"slot": 285291521
}
}The topic and key correspond to the request method you wish to call. The id value is an unsigned integer (must fit in u64) that will be echoed back in the envelope of the response object. params are request specific parameters documented for each on-demand query.
If the client issues a malformed request, it will be forcibly disconnected. If the client issues a well-formed request for data that the validator does not have (for example, an old slot), the query will receive a response with a value of null.
{
"topic": "slot",
"key": "query",
"id": 42,
"value": null
}Forks
The Solana network may occasionally fork, in which case there will be more than one active chain. When showing information derived from the chain, the API will (unless specified otherwise) show information reflecting the current fork choice of this validator. The current fork choice of this validator might not be the newest, or the heaviest (most voted on, or most likely to be chosen) fork.
For example, when showing the transactions per second (TPS) rate under summary.estimated_tps, it will be calculated using the transactions and block timings observed in the current fork. Similarly, the completed_slot is the last completed slot on the current fork choice.
When the validator switches fork choice, certain of this information will be republished to make sure it reflects the new fork choice.
Topics
summary
A set of high level informational fields about the validator.
summary.ping
| frequency | type | example |
|---|---|---|
| Request | null | below |
Sends a ping to the server, which will respond with a pong. This is an application level ping/pong and not a WebSocket control frame.
Example
{
"topic": "summary",
"key": "ping",
"id": 42,
}{
"topic": "summary",
"key": "ping",
"id": 42,
"value": null
}summary.version
| frequency | type | example |
|---|---|---|
| Once | string | "0.106.11814" |
The current version of the running validator.
summary.is_alpenglow
| frequency | type | example |
|---|---|---|
| Once | boolean | true |
Whether the validator is running Alpenglow consensus.
The API supports both Tower BFT and Alpenglow validators. Messages whose shape or meaning depends on the consensus implementation are documented below with separate Tower and Alpenglow labels.
Clients must use this message to determine the running consensus implementation. A server which does not publish summary.is_alpenglow must be treated as a Tower server. The consensus mode does not change during the lifetime of a validator process.
summary.cluster
| frequency | type | example |
|---|---|---|
| Once + Live | string | "mainnet-beta" |
One of mainnet-beta, devnet, testnet, pythtest, pythnet, development, or unknown. Indicates the cluster that the validator is likely to be running on. The cluster is guessed by looking at the genesis hash of the chain and comparing it to known cluster genesis hashes. The cluster cannot change once the validator is running, but because it may not be known when the validator first starts, you might get two cluster messages. One unknown immediately when the validator is booted, and then a message with mainnet (or other known cluster) when the validator learns its cluster from a downloaded snapshot.
summary.commit_hash
| frequency | type | example |
|---|---|---|
| Once | string | "78eefec7c779ef138aaaf4afe76cd6eaf4807006" |
The commit hash used to build the validator.
summary.identity_key
| frequency | type | example |
|---|---|---|
| Once + Live | string | "Fe4StcZSQ228dKK2hni7aCP7ZprNhj8QKWzFe5usGFYF" |
The public identity key assigned to the running validator, encoded in base58. Firedancer supports changing the identity key of the validator while it is running through a set-identity command, and if this happens a new identity_key will be published.
Summary information in this API is tied to the validator instance and not the identity key, for example, the skip rate is the skip rate of all blocks produced by this validator, regardless of what identity key they were published with. The mine field of blocks similarly indicates if this validator published the block, not whether it had the same identity key as the validator has now.
When changing identity key, current vote information is reset and republished for the new identity. Other summary information continues counting for blocks published by this validator instance.
summary.vote_state
| frequency | type | example |
|---|---|---|
| Once + Live | string | voting |
One of voting, non-voting, or delinquent, indicating the current vote status of the validator.
Under Tower, the validator considers itself delinquent if the last vote it has landed on its own currently chosen fork is more than 150 slots behind that fork.
Under Alpenglow, vote_slot is the latest slot for which this validator's vote was included in a reward certificate. See summary.vote_slot for more details. Given the current processed slot s, a voting validator is current when vote_slot > s - 128 for s >= 128, or when vote_slot > 0 for s < 128, otherwise they are delinquent.
summary.vote_distance
| frequency | type | example |
|---|---|---|
| Once + Live | number | 2 |
A number showing the distance between the highest slot the validator has landed a vote for, and the current highest replayed slot on the validators fork choice. This value excludes skipped slots, unless the distance is larger than 2 epochs worth of slots (NOTE: skipped slots are not excluded on Frankendancer). A distance of more than 150 means the validator is considered delinquent.
This message is Tower-only and is not published by an Alpenglow validator. Alpenglow clients can compare summary.completed_slot and summary.vote_slot when a raw slot distance is useful.
summary.turbine_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number|null | 100 |
The largest slot that is known by the validator to have been published to the blockchain. This is typically going to be the largest slot we've seen in a received turbine shred, but can also be a slot for which we were just leader. During boot, the max known slot may not be known yet if we haven't received any shreds. In this case this message will publish null.
It is worth noting that turbine_slot might be momentarily inaccurate (too large). If this happens, it should self-correct after about 4.8 seconds. This happens because turbine_slot is derived from the header on incoming shreds. In the worst case, a malicious leader shred can create an arbitrarily large slot on a new fork. All slot numbers received from shreds, including any malicious shreds, are forgotten after 4.8 seconds. This ensures our estimate self-corrects over time.
NOTE: this message is only supported on the Firedancer client, the Frankendancer client will always publish null for this message
summary.repair_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number|null | 100 |
The largest slot which the validator has fully retrieved and reconstructed by repair. In Alpenglow mode this is reported by the rotor tile rather than the repair tile; the field name is unchanged. This slot has the same problem as summary.turbine_slot (it might sporadically become unboundedly large) and provides the same guarantees.
summary.vote_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number|null | 100 |
Under Tower, this is the most recent slot this node has landed a vote for and will typically be one slot behind the current slot on the leader schedule.
Under Alpenglow, this is the latest slot for which this validator's ordinary notarize or skip vote was included in a reward certificate. It is the slot being voted on, not the later slot whose block carries the certificate. Participation in a fallback or finalization certificate does not advance it, because only reward certificates are consequential for the validator's on-chain rewards.
This is reset to null when the validator identity changes, and may also be null before the vote account has recorded any participation.
summary.caught_up_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number|null | 100 |
The slot when this validator caught up to the tip of the blockchain. This slot is recorded when replay slot is within 4 slots (one leader rotation) of summary.turbine_slot. If the WebSocket client connects before the validator has caught up, then this message will be published with null. The message would then be published once when the validator actually catches up.
Since summary.turbine_slot can be sometimes arbitrarily larger than the ground truth, that affects the accuracy of the catch-up slot as well. If a maliciously large shred arrives within 3 leader rotations of the validator catchup event, then summary.turbine_slot will be wrong, and summary.slot_caught_up will not be recorded until after 4.8 seconds when the malicious slot is forgotten. Functionally, this means that summary.slot_caught_up could be arbitrarily larger than the true catchup slot. The likelihood of this is low, and decreases for larger errors.
NOTE: this message is only supported on the Firedancer client, the Frankendancer client will always publish null for this message
summary.catch_up_history
| frequency | type | example |
|---|---|---|
| Once | CatchUpHistory | see below |
This validator records a history of all slots that were received from turbine or repair responses, as well as shred events that occurred while catching up. After catching up, slots are no longer recorded in this history. For repair and turbine slots, the history is available for the lifetime of the validator. Shred events are only available if the validator is in the catching up phase.
Example
{
"topic": "summary",
"key": "catch_up_history",
"value": {
"repair": [11, 12, 13, ...],
"turbine": [21, 22, 23, ...],
"shreds": {
"reference_slot": 289245044,
"reference_ts": "1739657041588242791",
"slot_delta": [0, 0],
"shred_idx": [1234, null],
"event": [0, 1],
"event_ts_delta": ["1000000", "2000000"]
}
}
}CatchUpHistory
| Field | Type | Description |
|---|---|---|
| repair | number[] | A list of all slots for which a repair shred was received that are older than summary.caught_up_slot |
| turbine | number[] | A list of all slots for which a turbine shred was received that are older than summary.caught_up_slot |
| shreds | SlotShreds | A list of shred events which have occurred for this validator in the past 15 seconds. If the validator has already caught up, or has not yet started catching up, then null |
summary.startup_time_nanos
| frequency | type | example |
|---|---|---|
| Once | string | "1719910299914232" |
A UNIX timestamp in nanoseconds of the validator's startup. The timestamp is taken by the gui tile during boot, so it occurs before the validator downloads a snapshot and fully catches up to the cluster.
summary.server_time_nanos
| frequency | type | example |
|---|---|---|
| Once | string | "1719910299914232" |
summary.startup_progress
| frequency | type | example |
|---|---|---|
| Once + Live | StartupProgress | below |
Information about the validator's progress in starting up. There are various stages of starting up which the validator goes through in order before it is ready. Typically, the phase will only move forward except for a special case: the validator can go from downloading_full_snapshot back to searching_for_full_snapshot if the snapshot peer found is downloading too slow and we would like to try a different one (and the same applies for the incremental snapshot).
The phases are,
| Phase | Description |
|---|---|
| initializing | The validator has just booted and has not yet started looking for RPC services to download snapshots from |
| searching_for_full_snapshot | The validator is searching over gossip for an RPC node to download a full snapshot from |
| downloading_full_snapshot | The validator has found an RPC peer to download a full snapshot from and the download is now in progress |
| searching_for_incremental_snapshot | The validator is searching over gossip for an RPC node to download an incremental snapshot from |
| downloading_incremental_snapshot | The validator has found an RPC peer to download an incremental snapshot from and the download is now in progress. The incremental snapshot is a smaller snapshot taken more regularly, which builds on top of a full snapshot |
| cleaning_blockstore | Removes stale data from the blockstore |
| cleaning_accounts | Removes stale data from the accounts database |
| loading_ledger | Loads the ledger data from disk into memory |
| processing_ledger | The blockstore is replayed to get to the current slot |
| starting_services | RPC, the leader TPU, the replay TVU, snapshots, and all other services are being started |
| halted | The validator is halted at a specific slot because of a development option provided at boot, and will not proceed further |
| waiting_for_supermajority | The validator is waiting at a specific slot for a supermajority of stake to come online on the gossip network so it can proceed. This is used during cluster restarts |
| running | The validator is fully booted and running normally |
Example
{
"topic": "summary",
"key": "startup_progress",
"value": {
"phase": "downloading_full_snapshot",
"downloading_full_snapshot_peer": "145.40.125.99:8899",
"downloading_full_snapshot_slot": 291059318,
"downloading_full_snapshot_elapsed_secs": 24.01,
"downloading_full_snapshot_remaining_secs": 254.26,
"downloading_full_snapshot_throughput": 17193374.00,
"downloading_full_snapshot_total_bytes": 4746970624,
"downloading_full_snapshot_current_bytes": 375455480,
"downloading_incremental_snapshot_peer": null,
"downloading_incremental_snapshot_slot": null,
"downloading_incremental_snapshot_elapsed_secs": null,
"downloading_incremental_snapshot_remaining_secs": null,
"downloading_incremental_snapshot_throughput": null,
"downloading_incremental_snapshot_total_bytes": null,
"downloading_incremental_snapshot_current_bytes": null,
"ledger_slot": null,
"ledger_max_slot": null,
"waiting_for_supermajority_slot": null,
"waiting_for_supermajority_stake_percent": null
}
}StartupProgress
| Field | Type | Description |
|---|---|---|
| phase | string | One of initializing, searching_for_full_snapshot, downloading_full_snapshot, searching_for_incremental_snapshot, downloading_incremental_snapshot, cleaning_blockstore, cleaning_accounts, loading_ledger, processing_ledger, starting_services, halted, waiting_for_supermajority, or running |
| downloading_full_snapshot_slot | number|null | If the phase is at least downloading_full_snapshot or later, this is the slot that is being (or was) downloaded from the snapshot provider. Otherwise it is null |
| downloading_full_snapshot_peer | string|null | If the phase is at least downloading_full_snapshot or later, this is the peer RPC address that the snapshot is being downloaded from. Otherwise it is null |
| downloading_full_snapshot_elapsed_secs | number|null | If the phase is at least downloading_full_snapshot or later, this is the duration, in seconds that the validator has been downloading the snapshot for. Otherwise it is null |
| downloading_full_snapshot_remaining_secs | number|null | If the phase is at least downloading_full_snapshot or later, this is the estimated duration, in seconds that the validator has left to download the snapshot. Otherwise it is null |
| downloading_full_snapshot_throughput | number|null | If the phase is currently downloading_full_snapshot, this is the current download throughput in bytes per second. Otherwise it is null |
| downloading_full_snapshot_total_bytes | number|null | If the phase is at least downloading_full_snapshot or later, this is the total size of the snapshot being downloaded in bytes. Otherwise it is null |
| downloading_full_snapshot_current_bytes | number|null | If the phase is at least downloading_full_snapshot or later, this is the current size of the snapshot that has been downloaded in bytes. Otherwise it is null |
| downloading_incremental_snapshot_slot | number|null | If the phase is at least downloading_incremental_snapshot or later, this is the slot that is being (or was) downloaded from the snapshot provider. Otherwise it is null |
| downloading_incremental_snapshot_peer | string|null | If the phase is at least downloading_incremental_snapshot or later, this is the peer RPC address that the snapshot is being downloaded from. Otherwise it is null |
| downloading_incremental_snapshot_elapsed_secs | number|null | If the phase is at least downloading_incremental_snapshot or later, this is the duration, in seconds that the validator has been downloading the snapshot for. Otherwise it is null |
| downloading_incremental_snapshot_remaining_secs | number|null | If the phase is at least downloading_incremental_snapshot or later, this is the estimated duration, in seconds that the validator has left to download the snapshot. Otherwise it is null |
| downloading_incremental_snapshot_throughput | number|null | If the phase is currently downloading_incremental_snapshot, this is the current download throughput in bytes per second. Otherwise it is null |
| downloading_incremental_snapshot_total_bytes | number|null | If the phase is at least downloading_incremental_snapshot or later, this is the total size of the snapshot being downloaded in bytes. Otherwise it is null |
| downloading_incremental_snapshot_current_bytes | number|null | If the phase is at least downloading_incremental_snapshot or later, this is the current size of the snapshot that has been downloaded in bytes. Otherwise it is null |
| ledger_slot | number|null | If the phase is at least processing_ledger or later, this is the current slot that we have replayed up to in the ledger. Otherwise it is null |
| ledger_max_slot | number|null | If the phase is at least processing_ledger or later, this is the maximum slot we need to replay up to in the ledger. Otherwise it is null |
| waiting_for_supermajority_slot | number|null | If the phase is at least waiting_for_supermajority or later, and we are stopped waiting for supermajority, this is the slot that we are stopped at. Otherwise it is null |
| waiting_for_supermajority_stake_percent | number|null | If the phase is at least waiting_for_supermajority or later, and we are stopped waiting for supermajority, this is the percentage of stake that is currently online and gossiping to our node. Otherwise it is null. The validator will proceed with starting up once the stake percent reaches 80 |
summary.boot_progress
| frequency | type | example |
|---|---|---|
| Once + Live | BootProgress | below |
Information about the validator's progress in starting up. There are various stages of starting up which the validator goes through in order before it is ready.
The phases form a state machine, and the validator can progress through them in interesting ways,
+--+ +------------------------------+
| v | v
joining_gossip -> loading_full_snapshot -> catching_up -> running
v ^ ^
loading_incremental_snapshot --+Some interesting transitions are,
- The validator may skip joining gossip and go straight to loading a snapshot if it was instructed to load from a specific file or source
- The full snapshot may be restarted many times, if a snapshot is corrupt or fails to download
- The incremental snapshot may be skipped if the full snapshot is sufficient
- The incremental snapshot may be abandoned and the phase returns to a new full snapshot, if the incremental snapshot is corrupt or fails to download
- The validator may skip the catching up phase if the snapshot brings it fully up to date, although this is extremely rare and unlikely to happen on mainnet except if the chain is halted or restarting
| Phase | Description |
|---|---|
| joining_gossip | The validator has just booted and has started looking for RPC services to download snapshots from |
| loading_full_snapshot | The validator has found an RPC peer to download a full snapshot, or a local snapshot to read from disk. The snapshot is being downloaded, decompressed, and inserted into the account database |
| loading_incremental_snapshot | The validator has found an RPC peer to download an incremental snapshot. The snapshot is being downloaded, decompressed, and inserted into the client database |
| catching_up | The validator is replaying / repairing any missing slots up to the tip of the chain |
| running | The validator is fully booted and running normally |
Example
{
"topic": "summary",
"key": "boot_progress",
"value": {
"phase": "waiting_for_supermajority",
"boot_target_slot_duration_nanos": 400000000,
"accounts_database_path": "/path/to/accounts.db",
"gui_database_path": "/path/to/gui.db",
"joining_gossip_elapsed_seconds": 5,
"loading_full_snapshot_elapsed_seconds": 7.8,
"loading_full_snapshot_reset_count": 0,
"loading_full_snapshot_slot": 359396820,
"loading_full_snapshot_total_bytes_compressed": "5004677960",
"loading_full_snapshot_read_bytes_compressed": "960692224",
"loading_full_snapshot_read_path": "/path/to/snapshot-359396820-EuXH88VnugHwoeusjHFXAg1Fp1VucJp2Z3SSjmrpBcam.tar.zst",
"loading_full_snapshot_decompress_bytes_decompressed": "4961009663",
"loading_full_snapshot_decompress_bytes_compressed": "826495323",
"loading_full_snapshot_insert_bytes_decompressed": "4864409599",
"loading_full_snapshot_insert_accounts": 10634591,
"loading_incremental_snapshot_elapsed_seconds": null,
"loading_incremental_snapshot_reset_count": null,
"loading_incremental_snapshot_slot": null,
"loading_incremental_snapshot_total_bytes_compressed": null,
"loading_incremental_snapshot_read_bytes_compressed": null,
"loading_incremental_snapshot_read_path": null,
"loading_incremental_snapshot_decompress_bytes_decompressed": null,
"loading_incremental_snapshot_decompress_bytes_compressed": null,
"loading_incremental_snapshot_insert_bytes_decompressed": null,
"loading_incremental_snapshot_insert_accounts": null,
"wait_for_supermajority_bank_hash": "2CeCyRoYmcctDmbXWrSUfTT4aQkGVCnArAmbdmQ5dGFi",
"wait_for_supermajority_shred_version": "37500",
"wait_for_supermajority_attempt": 1,
"wait_for_supermajority_total_stake": "1",
"wait_for_supermajority_connected_stake": "1",
"wait_for_supermajority_total_peers": 1,
"wait_for_supermajority_connected_peers": 1,
"catching_up_elapsed_seconds": null,
"catching_up_first_replay_slot": null,
}
}BootProgress
| Field | Type | Description |
|---|---|---|
| phase | string | One of joining_gossip, loading_full_snapshot, loading_incremental_snapshot, catching_up, waiting_for_supermajority, or running. This indicates the current phase of the boot process |
| boot_target_slot_duration_nanos | number|null | Target slot duration in nanoseconds for the epoch containing the boot snapshot slot, preferring the incremental snapshot over the full snapshot. null at startup until known |
| accounts_database_path | string | Absolute path to the on-disk accounts database file that this validator loads accounts into |
| gui_database_path | string | Absolute path to the on-disk gui database file that this validator saves historical monitoring info into |
| joining_gossip_elapsed_seconds | number | If the phase is joining_gossip, this is the duration, in seconds, spent joining the gossip network |
| loading_{full|incremental}_snapshot_elapsed_seconds | number | If the phase is at least loading_{full|incremental}_snapshot, this is the elapsed time, in seconds, spent reading (either downloading or reading from disk) the snapshot since the last reset |
| loading_{full|incremental}_snapshot_reset_count | number|null | If the phase is at least loading_{full|incremental}_snapshot or later, this is the number of times the load for the snapshot failed and the phase was restarted from scratch. A snapshot load may fail due to an unreliable or underperforming network connection. Otherwise, null |
| loading_{full|incremental}_snapshot_slot | number|null | If the phase is at least loading_{full|incremental}_snapshot or later, this is the slot of the snapshot being loaded. Otherwise, null |
| loading_{full|incremental}_snapshot_total_bytes_compressed | number|null | If the phase is at least loading_{full|incremental}_snapshot, this is the (compressed) total size of the snapshot being loaded, in bytes. Otherwise, null |
| loading_{full|incremental}_snapshot_read_bytes_compressed | number|null | If the phase is at least loading_{full|incremental}_snapshot, this is the (compressed) total number of bytes read from disk for the snapshot. Otherwise, null |
| loading_{full|incremental}_snapshot_read_path | string|null | If the phase is at least loading_{full|incremental}_snapshot, this is either the remote url or local file path from which the snapshot is being read. Otherwise, null |
| loading_{full|incremental}_snapshot_decompress_bytes_decompressed | number|null | If the phase is at least loading_{full|incremental}_snapshot, this is the (decompressed) number of bytes processed by decompress from the snapshot so far. Otherwise, null |
| loading_{full|incremental}_snapshot_decompress_bytes_compressed | number|null | If the phase is at least loading_{full|incremental}_snapshot, this is the (compressed) number of bytes processed by decompress from the snapshot so far. Otherwise, null |
| loading_{full|incremental}_snapshot_insert_bytes_decompressed | number|null | If the phase is at least loading_{full|incremental}_snapshot, this is the (decompressed) number of bytes processed from the snapshot by the snapshot insert time so far. Otherwise, null |
| loading_{full|incremental}_snapshot_insert_accounts | number|null | If the phase is at least loading_{full|incremental}_snapshot, this is the current number of accounts inserted into the validator's accounts database from this snapshot. Otherwise, null |
| wait_for_supermajority_bank_hash | string|null | If the client was configured to include the waiting_for_supermajority phase at startup, this is the expected bank hash of the snapshot bank. This ensures all validators join the cluster with the same starting state. null if wait for supermajority is not enabled |
| wait_for_supermajority_shred_version | string|null | If the client was configured to include the waiting_for_supermajority phase at startup, this is the expected shred version it was configured with. Shred version is functionally a hash of (genesis_hash, cluster_restart_history) which ensures only nodes which explicitly agree on the restart slot and restart attempt count can communicate with each other. null if wait for supermajority is not configured |
| wait_for_supermajority_attempt | number|null | If the client was configured to include the waiting_for_supermajority phase at startup, this is the number of times this cluster has been restarted onto the snapshot slot, including the current attempt. null if wait for supermajority is not configured |
| wait_for_supermajority_total_stake | string|null | If the phase is at least waiting_for_supermajority, this is the total network stake in lamports used to determine the 80% restart threshold |
| wait_for_supermajority_connected_stake | string|null | If the phase is at least waiting_for_supermajority, this is the network stake in lamports that is currently active on gossip and waiting for the restart threshold |
| wait_for_supermajority_total_peers | number|null | If the phase is at least waiting_for_supermajority, this is the total number of peers with an active stake |
| wait_for_supermajority_connected_peers | number|null | If the phase is at least waiting_for_supermajority, this is the number of peers with an active stake currently active on gossip and waiting for the restart threshold |
| catching_up_elapsed_seconds | number | If the phase is catching_up, this is the duration, in seconds, the validator has spent catching up to the current slot |
| catching_up_first_replay_slot | number | If the phase is catching_up, this is the first slot that exited the replay pipeline after booting |
The wait_for_supermajority_* fields will be null if the client is not configured to wait for a cluster restart, which is the case for typical client usage.
The wait_for_supermajority_*_stake stake fields are derived differently from the gossip.network_stats.health activated stake (which is from the start of the epoch). These fields account for any stake that is activating/deactivating in the current epoch and any stake that was explicitly undelegated prior to restart (e.g. inactive testnet participants or bad actors).
During the waiting_for_supermajority phase, per-peer offline status is available via the wait_for_supermajority.peer_{add|remove} message.
summary.schedule_strategy
| frequency | type | example |
|---|---|---|
| Once | string | below |
A description of the configured operational mode of the transaction scheduler. The following modes are possible:
- "perf"
- "balanced"
- "revenue"
The scheduler mode determines how eager / greedy the scheduler is when filling a block. "perf" means the scheduler tries to fill the block as quickly as possible while "revenue" means the scheduler will wait as long as possible before filling the block. "balanced" is somewhere in the middle.
Example
{
"topic": "summary",
"key": "schedule_strategy",
"value": "balanced"
}summary.tiles
| frequency | type | example |
|---|---|---|
| Once | Tile[] | below |
Information about the tile topology of Firedancer. This is a list of tiles in the system.
In Firedancer, available tiles are
- netlnk: Helps the net tile perform network-stack related functions. Separated from the net tile for security/performance reasons.
- net: Handles all ingress/egress network traffic.
- metric: Serves system-wide metrics from shared memory to an HTTP Prometheus endpoint.
- ipecho: Obtains shred version from cluster.
- gossvf: "Gossip verify". Performs preliminary validation of untrusted gossip messages arriving from the network.
- gossip: Handles trusted, parsed messages from gossvf tile.
- shred: Parses, verifies, and reconstructs untrusted shred payloads from the network.
- repair: Consumes parsed shreds from shred tile, issues repair requests for any missing shreds, and reconstructs the block. This tile is present only in Tower mode.
- rotor: The Alpenglow counterpart of the repair tile. Consumes block data relayed to us over Rotor, issues repair requests for anything still missing, and reconstructs the block. This tile is present only in Alpenglow mode. Note that the
summary.repair_slotfield and therepairnetwork traffic category keep their names in both modes and report on whichever of the two tiles the topology has. - replay: Consumes block shreds from repair or rotor and schedules execution and validation of block transactions.
- exec: Executes replay transactions.
- tower: Runs Tower BFT consensus and communicates Tower fork-choice and root decisions to replay. This tile is present only in Tower mode.
- votor: Runs Alpenglow consensus, exchanges BLS votes and certificates, and communicates parent and root decisions to replay. This tile is present only in Alpenglow mode.
- send: Sends Tower vote transactions originating from this validator into our own TPU as well as to other leaders' TPU. This tile is not used for Alpenglow consensus votes (which are not transactions).
- quic: Implements QUIC network protocol for receiving transactions.
- verify: Verifies transaction signatures and performs preliminary deduplication.
- dedup: Performs second deduplication pass for verified transactions.
- resolv: Resolves transaction address lookup tables (i.e., tables of account addresses which transactions use but must be retrieved from the accounts database).
- pack: Functions as transaction scheduler.
- bank: Helps pack execute scheduled transactions.
- poh: Generates block "ticks" and manages leader status.
- sign: Generates signatures for various tiles which require them (e.g. repair, gossip).
- rpc: Supports a subset of the Solana RPC API.
- gui: Serves the GUI, which includes the WebSocket API described in this document.
short-lived
- snapct: Manages the snapshot loading state machine.
- snapld: Loads snapshots from the network or from the file system.
- snapdc: Decompresses snapshot data.
- snapin: Inserts snapshot data into the accounts database.
- genesi: Handles cluster bootstrapping if validator is booting a new cluster. If booting into an existing cluster, fetches cluster info (e.g. genesis hash).
Tile
| Field | Type | Description |
|---|---|---|
| kind | string | What kind of tile it is. In Firedancer, one of the above tiles. In Frankendancer, might be one of net, sock, quic, verify, dedup, pack, bank, poh, shred, store, sign, plugin, or http |
| kind_id | number | The index of the tile in its kind. For example, if there are four verify tiles they have kind_id values of 0, 1, 2, and 3 respectively |
| pid | number | The process id of the tile |
| priority | string | The priority label of the tile. One of "floating", "startup", "normal", or "critical". This is also reported in summary.live_tile_metrics.priority for backwards compatibility |
Example
{
"topic": "summary",
"key": "tiles",
"value": [
{ "kind": "net", "kind_id": 0, "pid": 1234, "priority": "critical" },
{ "kind": "quic", "kind_id": 0, "pid": 1235, "priority": "normal" },
{ "kind": "verify", "kind_id": 0, "pid": 1236, "priority": "normal" },
{ "kind": "verify", "kind_id": 1, "pid": 1237, "priority": "normal" },
{ "kind": "dedup", "kind_id": 0, "pid": 1238, "priority": "normal" },
{ "kind": "pack", "kind_id": 0, "pid": 1239, "priority": "normal" },
{ "kind": "bank", "kind_id": 0, "pid": 1240, "priority": "normal" },
{ "kind": "poh", "kind_id": 0, "pid": 1241, "priority": "critical" }
]
}summary.identity_balance
| frequency | type | example |
|---|---|---|
| Once + Live | string | "21125572" |
Account balance of this validator's identity account in lamports. The balance is on the highest slot of the currently active fork of the validator.
summary.vote_balance
| frequency | type | example |
|---|---|---|
| Once + Live | string | "21125572" |
Account balance of this validator's vote account in lamports. The balance is on the highest slot of the currently active fork of the validator.
summary.vote_commission
| frequency | type | example |
|---|---|---|
| Once + Live | number|null | 500 |
Commission configured on this validator's vote account in basis points. The value is read from the highest slot of the currently active fork of this validator. For example, 500 represents a 5% commission. The value is null when the vote account is not found.
summary.root_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number | 275138349 |
Under Tower, this is the last slot rooted by Tower. Rooted slots are fully confirmed and irreversible, and the rooted slot never decreases as switching fork cannot unroot the slot. The number will not always increase by one, as skipped slots do not update the root slot. For example, if the root slot goes from 1001 to 1003 it means slot 1002 was skipped.
Under Alpenglow, this is the highest block which is both finalized by the cluster and successfully replayed locally. Equivalently, for each known consensus fork take the minimum of its highest finalized slot and highest replayed slot, then publish the maximum of those per-fork values. This can lag summary.finalized_slot while the validator downloads or replays a finalized block, and never decreases.
summary.optimistically_confirmed_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number | 275138349 |
The highest slot on the current fork that was optimistically confirmed. Optimistic confirmation means that over two-thirds of stake have voted to confirm the slot, and it is unlikely (although still possible, if validators switch vote) to not become rooted.
Although rare, the optimistically_confirmed_slot could decrease if a validator switches to another fork that does not have this slot.
This message is Tower-only and is not published by an Alpenglow validator.
summary.finalized_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number | 275138349 |
The highest slot known to have been finalized by Alpenglow cluster votes. It never decreases, and it can be greater than summary.root_slot or summary.completed_slot while the validator is still acquiring or replaying the finalized fork. It is derived only from the finalization certificates Votor's own certificate pool produces.
summary.notarized_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number | 275138349 |
The largest slot for which the validator has observed either a notarization certificate or a skip certificate. It never decreases.
This message is Alpenglow-only and is not published by a Tower validator.
summary.completed_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number | 275138349 |
The highest completed slot on the current fork choice of the validator. The completed slot may decrease if the validator is switching forks, or could stay the same for much more than the slot production time (400 milliseconds) if leaders are offline and not producing blocks.
summary.estimated_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number | 275138349 |
The estimated slot is the same as the completed slot, except it still progresses forward even if the current leaders are skipping (not producing) their slot. For example, if the last completed slot was 1001 and it has been 800 milliseconds since that slot, the estimated slot is likely to be 1003.
summary.reset_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number | 275138349 |
Under Tower, this is the slot corresponding to the head of the fork the validator most recently chose to vote for. A fork choice is triggered by the completion of a replay slot, so the publish interval is approximately one slot duration.
Under Alpenglow, this should be the latest reset parent selected by Votor's state machine for replay and leader production, which advances in leader-window sized steps.
summary.storage_slot
| frequency | type | example |
|---|---|---|
| Once + Live | number | 275138349 |
The oldest active rooted slot across all banks in the bank pool. Active here means that the bank has a positive reference count, which means there is some consumer which is still using it. This slot is less than or equal to the current consensus root and is always on the canonical consensus fork. Banks for slots before this slot or slots on a non-canonical fork will have a reference count of zero.
summary.estimated_slot_duration_nanos
| frequency | type | example |
|---|---|---|
| Once + Live | number | 450267129 |
The estimated duration of each slot on the network. This is a moving average from the prior 750 slots, or around five minutes. Live here means the estimate is republished whenever it changes, which is when a new slot is confirmed on the currently active fork.
summary.skip_rate
| frequency | type | example |
|---|---|---|
| Once + Live | SkipRate | {"epoch": 522, "skip_rate": 0.456172} |
The skip rate of an epoch is the ratio of skipped_slots/total_slots for our leader slots in that epoch. The skip rate is only known for slots that have happened since the validator was started, and we do not incorporate slots from before boot, as we cannot know if they were skipped or not. If this validator has not had any leader slots since it was booted, the skip rate reported will be zero.
The skip rate is specific to this running validator, and not any given identity key. If the validator identity is changed with set-identity, the skip rate will remain the same at first, and then start incorporating skips for the new identity key.
SkipRate
| Field | Type | Description |
|---|---|---|
| epoch | number | The epoch that the skip rate is being published for |
| skip_rate | number | The updated skip rate for the provided epoch |
summary.tps_history
| frequency | type | example |
|---|---|---|
| Once | number[][] | below |
A list of the last 300 TPS samples taken by the validator. Currently the spacing between samples is poorly defined, but it's roughly one sample per slot. Each sample is a moving average from the prior 10 seconds. Each element in the outer array represents a sample, and the outer array will have up to 300 samples. Samples are listed from oldest first.
Under Tower, each sample is [total_tps, vote_tps, nonvote_success_tps, nonvote_failed_tps]:
[[5492.2, 4578.841, 914.24, 0], [6134.44419, 5149.23, 985, 0]]Under Alpenglow, votes are not transactions. Each sample is [success_tps, failed_tps] and includes all on-chain transactions:
[[914.24, 0], [985, 0]]summary.estimated_tps
| frequency | type | example |
|---|---|---|
| Once + Live | object | below |
The estimated number of transactions per second the network is running at. This is a moving average from the prior 10 seconds. For a more precise view of transactions per second, the client can calculate it from the stream of new slot data.
Under Tower, the object contains total, vote, successful non-vote, and failed non-vote rates:
Tower example
{
"topic": "summary",
"key": "estimated_tps",
"value": {
"total": 8348,
"vote": 6875,
"nonvote_success": 1473,
"nonvote_failed": 0
}
}Under Alpenglow, the object contains only successful and failed on-chain transaction rates.
Alpenglow example
{
"topic": "summary",
"key": "estimated_tps",
"value": {
"success": 1473,
"failed": 0
}
}summary.live_network_metrics
| frequency | type | example |
|---|---|---|
| Once + 100ms | NetworkMetrics | below |
Live network metrics provides a live view of network bandwidth utilization across the various protocols used in the client.
The protocols list contains various different protocols the client uses to communicate with the internet.
[
"turbine",
"gossip",
"tpu",
"repair",
"rserve",
"metrics",
"votor"
]- turbine: the protocol used to disseminate blockchain data, which contains primarily executable transactions.
- gossip: the protocol used to disseminate node metadata, including node IP addresses used to help nodes find each other on the network
- tpu: "transaction processing unit", refers to the various subsystems in a client used to consume and forward incoming Solana transactions for their next leader slot.
- repair: a client subsystem which requests any missing block data needed by the replay pipeline which may have been lost over the network. In Alpenglow mode this category reports the rotor tile, which serves the same role; the category name is unchanged
- rserve: "repair serve", a client subsystem which serves repair requests from other nodes, responding with any block data they are missing
- metrics: refers to the Firedancer metrics tile, which serves an http Prometheus metrics endpoint
- votor: Alpenglow BLS vote and certificate traffic. New servers append this entry to preserve the indices of existing protocols; its counters are zero in Tower mode. Older servers may publish only the first six array elements.
{
"topic": "summary",
"key": "live_network_metrics",
"value": {
"ingress": [12345432, 5431234, 92345, 43210, 8765, 123, 765432],
"egress": [12345432, 5431234, 92345, 43210, 8765, 456, 654321],
"ingress_ema": [1234543.00, 543123.00, 9234.00, 4321.00, 876.00, 12.00, 76543.00],
"egress_ema": [1234543.00, 543123.00, 9234.00, 4321.00, 876.00, 45.00, 65432.00],
"ingress_max_5m": 15000000,
"egress_max_5m": 14500000
}
}NetworkMetrics
| Field | Type | Description |
|---|---|---|
| ingress | number[] | ingress[i] is the total number of ingress network bytes for protocols[i] |
| egress | number[] | egress[i] is the total number of egress network bytes for protocols[i] |
| ingress_ema | number[] | ingress_ema[i] is the EMA-smoothed (1-second half-life) ingress throughput in bytes per second for protocols[i], sampled every ~100 ms |
| egress_ema | number[] | egress_ema[i] is the EMA-smoothed (1-second half-life) egress throughput in bytes per second for protocols[i], sampled every ~100 ms |
| ingress_max_5m | number | peak total EMA-smoothed (1-second half-life) ingress throughput in bytes per second across all protocols, rolling 5-minute window |
| egress_max_5m | number | peak total EMA-smoothed (1-second half-life) egress throughput in bytes per second across all protocols, rolling 5-minute window |
summary.live_txn_waterfall
| frequency | type | example |
|---|---|---|
| Once + 100ms | LiveTxnWaterfall | below |
Alpenglow example
{
"topic": "summary",
"key": "live_txn_waterfall",
"value": {
"next_leader_slot": 285228774,
"waterfall": {
"in": {
"pack_cranked": 1,
"pack_retained": 2011,
"resolv_retained": 13,
"quic": 66767,
"udp": 1054,
"gossip": 0,
"block_engine": 13
},
"out": {
"net_overrun": 1,
"quic_overrun": 44,
"quic_frag_drop": 13,
"quic_abandoned": 15,
"tpu_quic_invalid": 16,
"tpu_udp_invalid": 17,
"verify_overrun": 2059,
"verify_parse": 14,
"verify_failed": 4092,
"verify_duplicate": 128,
"dedup_duplicate": 87,
"resolv_lut_failed": 4,
"resolv_expired": 0,
"resolv_ancient": 2,
"resolv_no_ledger": 0,
"resolv_retained": 0,
"pack_invalid": 6,
"pack_expired": 2,
"pack_retained": 1985,
"pack_overrun": 54,
"pack_priority": 58422,
"bank_invalid": 14,
"block_success": 2976,
"block_fail": 419
}
}
}
}LiveTxnWaterfall
| Field | Type | Description |
|---|---|---|
| next_leader_slot | number|null | The next leader slot that the transactions are being accumulated for |
| waterfall | TxnWaterfall | A waterfall of transactions received since the end of the previous leader slot |
A transaction waterfall describes the transactions that are received before and during a leader slot, and what happened to them. A typical waterfall is that we acquire transactions from QUIC in the lead up to (before) our leader slot, drop a few of them that fail to verify, drop a few duplicates, drop some low priority ones that won't fit into our block, and then successfully place some transactions into a block. Transactions can also be received and dropped during the leader slot, but it's important to note: the waterfall shows statistics for all transactions since the end of our last leader slot. These are transactions that are now eligible for placement into the next one.
The waterfall is typically useful when viewing what happened in a past leader slot: we want to know where transactions came from, and for what reasons they didn't make it into the block. For example, if we received 100,000 transactions leading up to the slot, but only 6000 made it in, what happened to the other 94,000?
The live waterfall is a special case: it's for the next slot of the validator, rather than one that is in the past. Because the slot hasn't happened yet, we know certain information: how many transactions we have received so far from users that we could pack into our next block, how many have expired, how many failed to verify, and so on, but we probably won't know how many made it into the block yet, as we do when looking at the waterfall for a block that has been published.
The waterfall should generally be balanced: total transactions in and total transactions out will be the roughly the same, but not always strictly. Transactions in could be more or less than transactions out due to sampling jitter. When subtracting, be sure to account for potential underflow.
summary.live_tile_primary_metric
| frequency | type | example |
|---|---|---|
| Once + 100ms | LiveTilePrimaryMetric | below |
Example
{
"topic": "summary",
"key": "live_tile_primary_metric",
"value": {
"next_leader_slot": 285228774,
"tile_primary_metric": {
"quic": 3,
"bundle_rtt_smoothed_millis": 30,
"bundle_rx_delay_millis_p90": 101,
"net_in": 37803082,
"net_out": 4982399,
"verify": 0,
"dedup": 0,
"bank": 89407,
"pack": 0,
"poh": 0,
"shred": 0,
"store": 0
}
}
}LiveTilePrimaryMetric
| Field | Type | Description |
|---|---|---|
| next_leader_slot | number|null | The next leader slot |
| tile_primary_metric | TilePrimaryMetric | Per-tile-type primary metrics. Some of these are point-in-time values (P), and some are 1-second moving window averages (W) |
TilePrimaryMetric
| Field | Type | Description |
|---|---|---|
| net_in | number | Network ingress bytes per second (W) |
| quic | number | Active QUIC connections (P) |
| bundle_rtt_smoothed_millis | number | The round-trip time for grpc messages sent to the bundle server. These are mostly ping-pong messages. An exponential moving average ( avg = 1/8 val + 7/8 avg ) is used to filter the signal (W) |
| bundle_rx_delay_millis_p90 | number | An estimate of the 90th percentile of the one-way delay of a bundle dispatched from the bundle server (W) |
| verify | number | Fraction of transactions that failed sigverify (W) |
| dedup | number | Fraction of transactions deduplicated (W) |
| pack | number | Fraction of pack buffer filled (P) |
| bank | number | Execution TPS (W) |
| net_out | number | Network egress bytes per second (W) |
summary.live_tile_timers
| frequency | type | example |
|---|---|---|
| Once + 25ms | number[] | below |
Live tile timers is an array, one entry per tile, of how idle the tile was in the preceding 10 millisecond sampling window. A value of -1 indicates no sample was taken in the window, typically because the tile was context switched out by the kernel or it is hung.
The tiles appear in the same order here that they are reported when you first connect by the summary.tiles message.
Example
{
"topic": "summary",
"key": "live_tile_timers",
"value": [
44.972112412,
90.12,
5.42148,
6.24870,
5.00158,
8.1111556,
76.585,
44.225,
12.98,
16.2981,
43.857,
14.1,
3.15716,
93.2456,
87.998
]
}summary.live_tile_metrics
| frequency | type | example |
|---|---|---|
| Once + 25ms | TileMetrics | below |
Live tile metrics is a live feed of various metrics related to tile health and resource utilization.
The timers field is a matrix of percentages, where entry on row i, column j is the percentage of time tile i spent in regimes[j] over the previous 10 millisecond sampling window. A value of -1 indicates no sample was taken in the window, typically because the tile was context switched out by the kernel or it is hung.
The regimes array contains the processing states that a tile can exist in. Tile regimes are the cartesian product of the following two state vectors:
State vector 1:
- running: means that at the time the run loop executed, there was no upstream message I/O for the tile to handle.
- processing: means that at the time the run loop executed, there was one or more messages for the tile to consume.
- stalled: means that at the time the run loop executed, a downstream consumer of the messages produced by this tile is slow or stalled, and the message link for that consumer has filled up. This state causes the tile to stop processing upstream messages.
State Vector 2:
- maintenance: the portion of the run loop that executes infrequent, potentially CPU heavy tasks
- routine: the portion of the run loop that executes regularly, regardless of the presence of incoming messages
- handling: the portion of the run loop that executes as a side effect of an incoming message from an upstream producer tile
- parked: the tile is asleep in the kernel waiting for work or a deadline, not executing the run loop
regimes
[
"running_maintenance",
"processing_maintenance",
"stalled_maintenance",
"running_routine",
"processing_routine",
"stalled_routine",
"running_handling",
"processing_handling",
"waiting",
"stalled_waiting",
]"waiting" is running_parked and "stalled_waiting" is stalled_parked. "stalled_handling" and "processing_parked" are impossible states (a tile with a message to consume never parks), and are therefore excluded.
The sched_timers field is structured the same as the timers field, but represents a different set of regimes that together make up the total scheduling and execution time for a tile. Like the timers field, these regimes when combined make up the wallclock time elapsed since a previous sample. Unlike timers field, these regimes are designed to highlight context switches and kernel task scheduling overhead. The timers field also includes context switches but just bookkeeps it to whatever regime was running when the tile got switched out.
sched_regimes
[
"wait",
"idle",
"user",
"system",
"interrupt",
]The regimes mean the following
- wait: the time a tile's process spent waiting in the runqueue before being dispatched
- user: the time a tile's process spent executing in user mode
- system: the time a tile's process spent executing in kernel mode
- interrupt: the time stolen from the tile's CPU by hardirq/softirq handlers or a hypervisor. Only reported for fixed (pinned) tiles; floating tiles report
0. Requires a kernel withCONFIG_IRQ_TIME_ACCOUNTINGfor accurate accounting - idle: Any remaining wallclock time not accounted for by the other 4 regimes
The tile indices i appear in the same order here that they are reported when you first connect by the summary.tiles message.
Example
{
"topic": "summary",
"key": "live_tile_metrics",
"value": {
"timers": [
[10.1, 0, 0, 15.3, 17, 58, 0, 0],
[10, 0, 0, 15, 17, 58, 0, 0],
...
],
"sched_timers": [
[20.5, 29.5, 49.0, 1.0, 0.0],
[10.5, 39.5, 38.5, 11.0, 0.5],
...
],
"in_backp": [
0,
0,
...
],
"backp_msgs": [
0,
10,
...
],
"alive": [
1,
1,
...
],
"nvcsw": [
0,
1234,
...
],
"nivcsw": [
0,
3,
...
],
"minflt": [
1,
3,
...
],
"majflt": [
0,
0,
...
],
"last_cpu": [
23,
12,
...
],
"interrupts": [
0,
5821,
...
],
"tlb_shootdowns": [
0,
42,
...
],
"priority": [
"normal",
"critical",
...
],
"timer_ticks": [
1234,
0,
...
]
}
}TileMetrics
| Field | Type | Description |
|---|---|---|
| timers | (number[]|null)[] | timers[i] is null if no sample was taken in the window, typically because the tile was context switched out by the kernel or it is hung. Otherwise, timers[i][j] is the percentage of time from the last 10ms tile i spent in regime regimes[j] |
| sched_timers | (number|null)[] | sched_timers[i] is the percentage of time from the last 10ms tile i spent in regime sched_regimes[j] |
| alive | number[] | alive[i] is 2 if tile i has permanently shut down, 1 if tile i has updated its heartbeat timer any time in the last 100ms, and 0 otherwise |
| in_backp | boolean[] | in_backp[i] is true if tile i is currently backpressured and false otherwise. |
| backp_msgs | number[] | backp_msgs[i] is the number of times since startup that tile i has had to wait for one or more consumers to catch up to resume publishing |
| nvcsw | number[] | nvcsw[i] is the number of voluntary context switches that occurred for tile i since startup |
| nivcsw | number[] | nivcsw[i] is the number of involuntary context switches that occurred for tile i since startup |
| minflt | number[] | minflt[i] is the number of minor page faults that occurred for tile i since startup. Minor page faults occur for requested pages already in memory, but not in the page table |
| majflt | number[] | majflt[i] is the number of major page faults that occurred for tile i since startup. Major page faults occur for requested pages not in memory or the page table |
| last_cpu | number[] | last_cpu[i] is the CPU index that tile i was last recorded executing on |
| interrupts | number[] | interrupts[i] is the number of device IRQs handled on the CPU that tile i is pinned to since startup. Only reported for fixed (pinned) tiles; other tiles report 0 |
| tlb_shootdowns | number[] | tlb_shootdowns[i] is the number of TLB shootdowns handled on the CPU that tile i is pinned to since startup. Only reported for fixed (pinned) tiles; other tiles report 0 |
| timer_ticks | number[] | timer_ticks[i] is the number of local timer interrupts (LOC) handled on the CPU that tile i is pinned to since startup. Only reported for fixed (pinned) tiles; other tiles report 0. Near-zero on nohz_full CPUs running a single task |
| priority | string[] | priority[i] is the priority label of tile i. One of "floating", "startup", "normal", or "critical" |
Note that a null entry in timers field indicates that the tile has not published new information about the following fields
- timers
- alive
- in_backp
- backp_msgs
Similarly, a null entry in sched_timers means that updates for that tile have not been published for the following fields
- nvcsw
- nivcsw
- minflt
- majflt
- last_cpu
- interrupts
Since the fields are not nullable, they contain the previously held value. The client should ignore / interpolate them where applicable.
summary.live_program_cache
| frequency | type | example |
|---|---|---|
| Once + 100ms | ProgramCacheMetrics | below |
Live program cache metrics provides a live view of program cache performance and utilization across the replay and execution tiles. The program cache is a fixed-size, fork-aware, thread-concurrent cache over programs in the account database. It avoids re-loading and re-validating sBPF ELF binaries on every transaction execution.
Example
{
"topic": "summary",
"key": "live_program_cache",
"value": {
"hits": 482910,
"lookups": 483200,
"insertions": 145,
"insertion_bytes": 18743296,
"evictions": 12,
"eviction_bytes": 1048576,
"spills": 0,
"spill_bytes": 0,
"free_bytes": 104857600,
"size_bytes": 134217728
}
}ProgramCacheMetrics
| Field | Type | Description |
|---|---|---|
| hits | number | Total number of program cache hits across all execution tiles over the last 1 minute. Misses can be derived as lookups - hits |
| lookups | number | Total number of program cache lookups across all execution tiles over the last 1 minute |
| insertions | number | Total number of program cache insertions across all execution tiles since startup |
| insertion_bytes | number | Total number of bytes inserted into the program cache since startup |
| evictions | number | Total number of program cache evictions across all execution tiles since startup |
| eviction_bytes | number | Total number of bytes evicted from the program cache since startup |
| spills | number | Total number of times a compiled program was not cached because the cache was full and no entry could be evicted. The program is executed from a temporary buffer instead, which is slower |
| spill_bytes | number | Total number of bytes used by spilled programs since startup |
| free_bytes | number | Current number of free bytes in the program cache heap |
| size_bytes | number | Total size in bytes of the program cache heap |
The hit rate can be derived as hits / lookups. Heap utilization can be derived as 1 - (free_bytes / size_bytes). A healthy validator should have a high hit rate (close to 1.0) and low spill counts.
summary.health
| frequency | type | example |
|---|---|---|
| Once + 100ms | Health | below |
Provides precise status information for the major validator subsystems. The status is computed by the diagnostics tile (diag), which periodically reads metrics from the relevant tiles and evaluates liveness conditions.
Each subsystem reports a specific state string describing its current operational status. All subsystems share the "disabled" state, which indicates that the relevant tiles are not present in the topology or the feature is not configured. The remaining states are subsystem-specific and described below.
Example
{
"topic": "summary",
"key": "health",
"value": {
"vote": "voting",
"bundle": "connected",
"replay": "running",
"turbine": "running",
"builder": "disabled"
}
}Health
| Field | Type | Description |
|---|---|---|
| vote | string | Vote subsystem status |
| bundle | string | Bundle subsystem status |
| replay | string | Replay subsystem status |
| turbine | string | Turbine subsystem status |
| builder | string | External block builder status |
vote states:
| State | Description |
|---|---|
disabled | The validator is non-voting, or the active consensus tile (tower for Tower or votor for Alpenglow) is not present in the topology |
not_started | The active consensus tile exists but is not yet running, or the validator has not yet recorded a vote |
delinquent | Under Tower, the vote distance exceeds 150 slots or the vote slot has not advanced for over 60 seconds. Under Alpenglow, the validator is delinquent according to the exact 128-slot on-chain vote-account lookback in summary.vote_state. Also reported when votes land but the vote account fails the validator admission ticket filter (not V4 with a BLS pubkey, or below the V4 rent-exempt minimum), so its stake is not admitted: no leader slots, no rewards |
voting | The validator is voting and is not delinquent |
bundle states:
| State | Description |
|---|---|
disabled | No bundle tiles are configured in the topology |
disconnected | All bundle tiles are disconnected from their block engine |
connecting | At least one bundle tile is attempting to connect, but none are connected or sleeping |
connected | At least one bundle tile has an active connection to its block engine |
sleeping | At least one bundle tile is deliberately idle (for example, no upcoming leader slots, or standing by while another block source is active), but none are connected |
replay states:
| State | Description |
|---|---|
disabled | The replay tile is not present in the topology |
not_started | The replay tile exists but is not yet running, or the turbine slot or reset slot is zero (replay has not yet begun) |
behind | The gap between the turbine slot and the reset slot exceeds 12 slots, or the reset slot has not advanced in over 12 seconds |
running | The replay tile is keeping up with incoming turbine data |
turbine states:
| State | Description |
|---|---|
disabled | No shred tiles or no replay tile are present in the topology |
not_started | The relevant tiles exist but are not yet all running, or the turbine slot is zero |
stalled | The turbine slot has not advanced in over 12 seconds |
repair_outpacing | Turbine slot is advancing, but repair byte throughput has exceeded turbine byte throughput over the last 12-second window, indicating degraded turbine connectivity |
running | Turbine is receiving shreds and its throughput exceeds repair |
builder states:
| State | Description |
|---|---|
disabled | No external block builder is configured. Validator clients without block builder support always report this state |
disconnected | The block builder is disconnected |
connecting | The block builder connection is being established |
unhealthy | The block builder is connected but is not in a usable state |
connected | The block builder is connected and healthy |
summary.live_system_resources
| frequency | type | example |
|---|---|---|
| Once + 30s | SystemLive | below |
CPU topology, configured Firedancer hugepage allocations, and live host memory and disk usage. This message is only emitted by full Firedancer; Frankendancer omits it. CPU topology and Firedancer allocations are captured at startup, while host memory and disk values are sampled live.
Host memory values are best-effort snapshots refreshed every ~30 seconds. The Firedancer allocation breakdown is fixed at startup.
Example
{
"topic": "summary",
"key": "live_system_resources",
"value": {
"cpus": [
{ "online": true, "numa_node": 0, "die_idx": 0, "sibling_cpu": 2, "tile_idxs": [0, 3] },
{ "online": true, "numa_node": 0, "die_idx": 0, "sibling_cpu": null, "tile_idxs": [1] },
{ "online": true, "numa_node": 0, "die_idx": 0, "sibling_cpu": 0, "tile_idxs": [] }
],
"memory": {
"available_bytes": 326417514496,
"free_bytes": 214748364800,
"nodes": [
{
"node": 0,
"total_bytes": 274877906944,
"free_bytes": 60129542144,
"shared_bytes": 4294967296,
"tiles": [
{ "tile_idx": 0, "bytes": 2147483648 },
{ "tile_idx": 1, "bytes": 8589934592 }
]
},
{
"node": 1,
"total_bytes": 274877906944,
"free_bytes": 154618822656,
"shared_bytes": 2147483648,
"tiles": [
{ "tile_idx": 2, "bytes": 107374182400 }
]
}
]
},
"disk": [
{
"name": "/data",
"total_bytes": 549755813888,
"used_bytes": 343597383680,
"files": [
{ "path": "/data/accounts.db", "category": "accounts", "bytes": 171798691840 },
{ "path": "/data/shreds.db", "category": "shreds", "bytes": 64424509440 },
{ "path": "/data/gui.db", "category": "gui", "bytes": 1073741824 },
{ "path": "/data/firedancer.log", "category": "logs", "bytes": 2147483648 }
]
},
{
"name": "/mnt",
"total_bytes": 1099511627776,
"used_bytes": 665719930880,
"files": [
{ "path": "/mnt/snapshots", "category": "snapshots", "bytes": 665719930880 }
]
}
]
}
}SystemLive
| Field | Type | Description |
|---|---|---|
| cpus | SystemCpu[] | One entry per logical CPU. The array index is the logical CPU ID |
| memory | SystemLiveMemory | Live host memory usage and configured Firedancer hugepage allocations by NUMA node |
| disk | SystemDiskMount[] | Live usage for filesystems used by the validator |
SystemCpu
| Field | Type | Description |
|---|---|---|
| online | boolean | Whether the CPU was online at validator startup |
| numa_node | number | NUMA node containing this CPU |
| die_idx | number | null | Linux-reported processor die containing this CPU, or null when package or die metadata is unavailable or invalid. Known die_idx values are dense, zero-based indices assigned to distinct Linux (physical_package_id, die_id) pairs in ascending logical CPU order |
| sibling_cpu | number | null | Logical CPU ID of the other hyperthread on the same physical core, or null when there is no known sibling. This ID indexes the cpus array |
| tile_idxs | number[] | Indices in summary.tiles of tiles pinned to this CPU. Multiple entries indicate configured CPU sharing, for example between startup and post-start tiles. Tiles the kernel schedules across CPUs are not listed |
SystemLiveMemory
| Field | Type | Description |
|---|---|---|
| available_bytes | number | Host-wide estimate of physical RAM available for new allocations without swapping |
| free_bytes | number | Host-wide physical RAM currently unused |
| nodes | SystemLiveMemoryNode[] | Live memory usage by NUMA node |
available_bytes - free_bytes provides a global estimate of memory that is currently in use but can be reclaimed without swapping, such as page cache and reclaimable kernel allocations.
SystemLiveMemoryNode
| Field | Type | Description |
|---|---|---|
| node | number | NUMA node index |
| total_bytes | number | Total physical RAM local to this node |
| free_bytes | number | Physical RAM reported free on this node |
| shared_bytes | number | Firedancer workspace bytes shared by tiles or not attributable to one tile |
| tiles | SystemLiveMemoryTile[] | Firedancer workspace and stack bytes uniquely attributable to individual tiles |
SystemLiveMemoryTile
| Field | Type | Description |
|---|---|---|
| tile_idx | number | Index of the tile in the summary.tiles message array |
| bytes | number | Hugepage-backed workspace and stack memory allocated to this tile |
The tracked Firedancer hugepage allocation on a NUMA node is:
shared_bytes + Σ tiles.bytesThis total includes Firedancer hugetlbfs workspaces and tile stacks. It does not include anonymous heap allocations, ordinary file-backed mappings, or kernel memory.
A workspace with one owning tile is attributed to that tile; a workspace with multiple owners or no unique owner is reported in shared_bytes.
SystemDiskMount A mount is included if at least one validator data path resolves onto it.
| Field | Type | Description |
|---|---|---|
| name | string | Filesystem mount point, e.g. /data or /mnt |
| total_bytes | number | Total filesystem capacity in bytes |
| used_bytes | number | Total bytes used on the filesystem, including files not written by the validator |
| files | SystemDiskFile[] | Validator-managed files and directories on this filesystem |
SystemDiskFile
| Field | Type | Description |
|---|---|---|
| path | string | Absolute path of the validator-managed file or directory |
| category | string | One of accounts, shreds, snapshots, gui, or logs. Clients should tolerate unknown values |
| bytes | number | Logical length owned and maintained at this path. The value may lag filesystem changes but converges to the current logical length |
snapshot_server
snapshot_server.transfers
| frequency | type | example |
|---|---|---|
| Live | SnapshotServerTransfer[] | below |
Provides updates about active transfers from the snapshot server on this validator to remote peers. Updates are rate limited to 50ms.
A transfer operation is identified by the key (tile_idx:conn_idx). The upload array contains a batch of updates for various download ops but this is not the complete list of snapshots.
There may be multiple events for the same key in the array. The last one wins.
Keys get removed if closed is set or when they expire (see expire_time_nanos).
Example
{
"topic": "snapshot_server",
"key": "transfers",
"value": [
{
"tile_idx": 0,
"conn_idx": 12,
"request_seq": "3",
"client_ip": "145.40.125.99",
"client_port": 51422,
"slot": 290627318,
"incremental": true,
"base_slot": 290620000,
"snapshot_size": "42949672960",
"range_start": "0",
"range_size": "42949672960",
"bytes_sent": "8455716864",
"start_time_nanos": "1719910299914232000",
"sample_time_nanos": "1719910371392211000",
"expire_time_nanos": "1719910372392211000",
"closed": false
}
]
}SnapshotServerTransfer
| Field | Type | Description |
|---|---|---|
| tile_idx | number | Index of the snapsv server tile (typically 0) |
| conn_idx | number | Connection slot in the peer's conn table |
| request_seq | string | Tile-unique sequence number of the current HTTP request |
| client_ip | string | The client's IP address |
| client_port | number | The client's TCP source port |
| slot | number | The slot of the snapshot being downloaded |
| incremental | bool | true if the snapshot is incremental, false if it is full |
| base_slot | number|null | Base slot of the incremental snapshot |
| snapshot_size | string | Total size of the snapshot file in bytes |
| range_start | string | Offset of first byte of the client's requested file range (typically 0) |
| range_size | string | Number of bytes in the client's requested file range (typically snapshot_size) |
| bytes_sent | string | Number of snapshot bytes sent so far (<= range_size) |
| start_time_nanos | string | UNIX timestamp in nanoseconds of when the transfer began |
| sample_time_nanos | string | UNIX timestamp in nanoseconds of this update |
| expire_time_nanos | string | UNIX timestamp in nanoseconds by when the transfer operation is assumed aborted in the absence of updates |
| closed | bool | true implies final event for a transfer; HTTP request is done/aborted |
accounts
Live view of the accounts database backend. The accounts database is a log-structured store on disk, partitioned into fixed-size regions which are written sequentially and reclaimed by background compaction. A fixed-size in-memory cache, partitioned into size classes, sits in front of the disk. Events on this topic are only emitted to full clients (those connected with ?full=true).
accounts.stats
| frequency | type | example |
|---|---|---|
| Once + 100ms | AccountsStats | below |
A snapshot of the accounts database backend covering disk usage, in-progress compaction, per-class cache occupancy, aggregate IO rates, and per-partition state. All *_per_sec values are triangular-weighted moving-window rates over recent 100ms samples; cumulative counters are since process start.
Example
{
"topic": "accounts",
"key": "stats",
"value": {
"sample_time_nanos": 1742000000000000000,
"disk": {
"accounts_total": 412563002,
"accounts_capacity": 536870912,
"allocated_bytes": 549755813888,
"current_bytes": 184583291392,
"used_bytes": 168540143616
},
"compaction": {
"in_compaction": 1,
"next_compaction_remaining_seconds": 0.0,
"next_compaction_partition_idx": 184,
"compactions_requested": 184,
"compactions_completed": 183,
"accounts_relocated_bytes": 9382913024,
"relocated_bytes_per_sec": 41943040.0
},
"cache": {
"hit_rate_ema": 0.9987,
"size_bytes": 68719476736,
"classes": [
{
"class": 0,
"used_slots": 524288,
"max_slots": 1048576,
"reserved_slots": 8192,
"target_used_slots": 786432,
"low_water_used_slots": 262144,
"not_found": 1024,
"evicted": 9381,
"preevicted": 4012,
"committed_new": 18472,
"committed_overwrite": 1839204,
"not_found_per_sec": 0.4,
"evicted_per_sec": 12.1,
"preevicted_per_sec": 5.2,
"committed_new_per_sec": 1.9,
"committed_overwrite_per_sec": 4189.3,
"reads_per_sec": 8124.0,
"writes_per_sec": 4189.0,
"hit_rate_ema": 0.9999
}
]
},
"io": {
"acquired": 184392013,
"acquired_writable": 92013874,
"bytes_read": 9183920128,
"bytes_copied": 184392013120,
"bytes_written": 21482938474,
"bytes_written_accdb": 9382913024,
"read_ops": 891203,
"write_ops": 1839204,
"acquired_per_sec": 38201.0,
"acquired_writable_per_sec": 19102.0,
"bytes_read_per_sec": 4194304.0,
"bytes_copied_per_sec": 83886080.0,
"bytes_written_per_sec": 10485760.0,
"read_ops_per_sec": 412.0,
"write_ops_per_sec": 819.0,
"prewrite_ratio": 0.42
},
"tiles": [
{
"name": "execle",
"kind_id": 0,
"joiner_type": "RW",
"status": 1,
"acquired": 12381924,
"bytes_read": 9183920,
"bytes_written": 18374822,
"acquired_per_sec": 4128.0,
"acquired_writable_per_sec": 2064.0,
"bytes_read_per_sec": 524288.0,
"bytes_copied_per_sec": 8388608.0,
"bytes_written_per_sec": 1048576.0,
"read_ops_per_sec": 41.0,
"write_ops_per_sec": 82.0,
"not_found_per_sec": 0.5,
"evicted_per_sec": 1.2,
"committed_per_sec": 2063.0,
"acquire_calls_per_sec": 0.0,
"hit_rate_ema": 0.9999
}
],
"partitions": [
{
"partition_idx": 184,
"file_offset": 6597069766656,
"tier": 0,
"write_offset": 12884901888,
"bytes_freed": 4187593113,
"read_ops": 18320,
"bytes_read": 5153960755,
"write_ops": 8421,
"bytes_written": 12884901888,
"read_ops_per_sec": 210.0,
"bytes_read_per_sec": 41943040.0,
"write_ops_per_sec": 0.0,
"bytes_written_per_sec": 0.0,
"utilization": 1.0,
"fragmentation": 0.325,
"used_frac": 1.0,
"fragmented_frac": 0.325,
"compaction_trigger_frac": 0.30,
"age_seconds": 9182.4,
"filled_seconds": 8710.9,
"compaction_state": 2,
"compaction_frac": 0.15,
"is_write_head": false
}
]
}
}AccountsStats
| Field | Type | Description |
|---|---|---|
| sample_time_nanos | number | Unix nanosecond timestamp at which this sample was taken |
| disk | Disk | Disk-level capacity and utilization (see below) |
| compaction | Compaction | Aggregate compaction activity (see below) |
| cache | Cache | In-memory cache occupancy and per-size-class metrics (see below) |
| io | Io | Aggregate IO counters and rates across all accdb joiners (see below) |
| tiles | Tile[] | Per-tile breakdown of accdb activity in stable order. Snapshot-loader snapin rows disappear after shutdown |
| partitions | Partition[] | Per-partition snapshot. Partitions that have never been written and are not being compacted are omitted |
Disk
| Field | Type | Description |
|---|---|---|
| accounts_total | number | Current number of accounts indexed by the database |
| accounts_capacity | number | Maximum number of accounts the index can hold |
| allocated_bytes | number | Total bytes reserved on disk for the accounts database file |
| current_bytes | number | Bytes currently committed within partitions (including fragmentation) |
| used_bytes | number | Bytes currently in use by live account data (excluding fragmentation) |
Compaction
| Field | Type | Description |
|---|---|---|
| in_compaction | number | Non-zero if a partition is currently being compacted |
| next_compaction_remaining_seconds | number|null | Estimated time remaining, in seconds, until the next compaction starts. 0 if a compaction is currently in progress (in_compaction is non-zero). null if no compaction is currently projected |
| next_compaction_partition_idx | number|null | Index of the partition estimated to be compacted next. If a compaction is currently in progress (in_compaction is non-zero), this is the index of the partition being compacted. null if no compaction is currently projected |
| compactions_requested | number | Total number of partition compactions enqueued since startup |
| compactions_completed | number | Total number of partition compactions completed since startup |
| accounts_relocated_bytes | number | Total bytes of account data rewritten by compaction since startup |
| relocated_bytes_per_sec | number | Recent rate at which compaction is rewriting account data, in bytes per second |
Cache
| Field | Type | Description |
|---|---|---|
| hit_rate_ema | number | Recent cache hit rate across all classes, in the range [0, 1] |
| size_bytes | number | Total in-memory cache footprint in bytes |
| classes | CacheClass[] | One entry per cache size class, in ascending class order (see below) |
CacheClass Accounts are bucketed into 8 size classes by data length: 0 covers up to 128 B, 1 covers 129 B - 512 B, 2 covers 513 B - 2 KiB, 3 covers 2 KiB - 8 KiB, 4 covers 8 KiB - 32 KiB, 5 covers 32 KiB - 128 KiB, 6 covers 128 KiB - 1 MiB, and 7 covers 1 MiB - 10 MiB.
| Field | Type | Description |
|---|---|---|
| class | number | The size class index, 0 through 7 |
| used_slots | number | Number of cache slots in this class currently holding an account |
| max_slots | number | Total number of cache slots provisioned for this class |
| reserved_slots | number | Slots held in reserve for prewrite, not available for eviction |
| target_used_slots | number | Steady-state target occupancy for this class |
| low_water_used_slots | number | Occupancy below which the class will not preemptively evict |
| not_found | number | Cumulative count of cache misses (account had to be read from disk) for this class |
| evicted | number | Cumulative count of cache lines reclaimed from this class to make room |
| preevicted | number | Cumulative count of cache lines speculatively reclaimed in advance by the background accdb tile |
| committed_new | number | Cumulative count of brand-new account versions committed at this class |
| committed_overwrite | number | Cumulative count of overwrite commits (existing fork + generation) at this class |
| not_found_per_sec | number | Recent rate of misses, in accounts per second |
| evicted_per_sec | number | Recent rate of evictions, in accounts per second |
| preevicted_per_sec | number | Recent rate of background pre-evictions, in accounts per second |
| committed_new_per_sec | number | Recent rate of new commits, in accounts per second |
| committed_overwrite_per_sec | number | Recent rate of overwrite commits, in accounts per second |
| reads_per_sec | number | Recent rate of read-only acquires at this class, in accounts per second |
| writes_per_sec | number | Recent rate of writable acquires at this class, in accounts per second |
| hit_rate_ema | number | Recent hit rate at this class, in the range [0, 1] |
Io
| Field | Type | Description |
|---|---|---|
| acquired | number | Cumulative count of accounts acquired (read or write) by any joiner since startup |
| acquired_writable | number | Cumulative count of accounts acquired writable since startup |
| bytes_read | number | Cumulative bytes read from disk since startup |
| bytes_copied | number | Cumulative bytes copied out of the cache on a hit since startup |
| bytes_written | number | Cumulative bytes written to disk since startup |
| bytes_written_accdb | number | Cumulative bytes written to disk by the accdb tile background work (preevict and compaction) since startup |
| read_ops | number | Cumulative disk read operations since startup |
| write_ops | number | Cumulative disk write operations since startup |
| acquired_per_sec | number | Recent acquire rate, in accounts per second |
| acquired_writable_per_sec | number | Recent writable acquire rate, in accounts per second |
| bytes_read_per_sec | number | Recent disk read throughput, in bytes per second |
| bytes_copied_per_sec | number | Recent cache-hit copy throughput, in bytes per second |
| bytes_written_per_sec | number | Recent disk write throughput, in bytes per second |
| read_ops_per_sec | number | Recent disk read operation rate, in operations per second |
| write_ops_per_sec | number | Recent disk write operation rate, in operations per second |
| prewrite_ratio | number | Fraction of recent disk writes attributable to background prewrite/compaction, in the range [0, 1] |
Tile
| Field | Type | Description |
|---|---|---|
| name | string | Tile kind name, e.g. execle, execrp, replay, tower, rpc, resolv, or accdb |
| kind_id | number | Instance index within this tile kind |
| joiner_type | string | RW if the tile reads and writes accounts (execle, execrp, replay, tower, accdb), RO if it only reads (rpc, resolv) |
| status | number | 1 if the tile is running, 2 if it has gracefully shut down |
| acquired | number | Cumulative count of accounts this tile has acquired since startup |
| bytes_read | number | Cumulative bytes this tile has read from disk since startup |
| bytes_written | number | Cumulative bytes this tile has written to disk since startup |
| acquired_per_sec | number | Recent acquire rate for this tile, in accounts per second |
| acquired_writable_per_sec | number | Recent writable acquire rate for this tile, in accounts per second (always 0 for RO tiles and accdb) |
| bytes_read_per_sec | number | Recent disk read throughput for this tile, in bytes per second |
| bytes_copied_per_sec | number | Recent cache-hit copy throughput for this tile, in bytes per second |
| bytes_written_per_sec | number | Recent disk write throughput for this tile, in bytes per second |
| read_ops_per_sec | number | Recent disk read operation rate for this tile |
| write_ops_per_sec | number | Recent disk write operation rate for this tile |
| not_found_per_sec | number | Recent rate of cache misses (account had to be read from disk) for this tile |
| evicted_per_sec | number | Recent rate at which this tile's commits evicted lines from the cache (always 0 for RO tiles) |
| committed_per_sec | number | Recent rate of account version commits (new + overwrite) by this tile (always 0 for RO tiles and accdb) |
| acquire_calls_per_sec | number | Recent rate of accounts database acquire calls (account lookups) by this tile |
| hit_rate_ema | number | Recent cache hit rate for this tile, in the range [0, 1] |
Partition Partitions are fixed-size regions of the on-disk accounts database file. Each partition is assigned to a compaction tier (layer) and accumulates writes from the active write head for that tier; once a partition crosses the compaction trigger it is enqueued and later compacted into a higher tier, after which it is reclaimed.
| Field | Type | Description |
|---|---|---|
| partition_idx | number | Stable index of the partition within the database |
| file_offset | number | Byte offset of the partition's start within the accounts database file |
| tier | number | Compaction tier: 0 (hot), 1 (warm), 2 (cold), or 255 (off, fully compacted and awaiting reclaim) |
| write_offset | number | Byte offset of the partition's write head within the partition |
| bytes_freed | number | Bytes within the partition marked freed by subsequent writes |
| read_ops | number | Cumulative read operations against this partition since process start |
| bytes_read | number | Cumulative bytes read from this partition since process start |
| write_ops | number | Cumulative write operations into this partition since process start |
| bytes_written | number | Cumulative bytes written into this partition since process start |
| read_ops_per_sec | number | Recent read operation rate against this partition |
| bytes_read_per_sec | number | Recent read throughput against this partition, in bytes per second |
| write_ops_per_sec | number | Recent write operation rate into this partition |
| bytes_written_per_sec | number | Recent write throughput into this partition, in bytes per second |
| utilization | number | Fraction of the partition occupied by writes, in the range [0, 1] |
| fragmentation | number | Fraction of the written region marked freed, in the range [0, 1] |
| used_frac | number | Fraction of the partition occupied by live (non-freed) data |
| fragmented_frac | number | Fraction of the partition occupied by freed data |
| compaction_trigger_frac | number | Fragmentation fraction at which the partition is enqueued for compaction |
| age_seconds | number | Seconds since the partition was first opened for writes |
| filled_seconds | number | Seconds since the partition was closed to new writes, or 0 if still active |
| compaction_state | number | 0 idle, 1 queued for compaction, 2 currently being compacted |
| compaction_frac | number | Fraction of the partition that the compaction read head has processed |
| is_write_head | boolean | True if this partition is the active write head for any tier |
block_engine
Block engines are providers of additional transactions to the validator, which are configurable by the operator. The validator may not be configured to use any block engines, in which case no update will be provided. For now, at most one block engine can be configured, and the name and url will not change during the lifetime of the validator.
block_engine.update
| frequency | type | example |
|---|---|---|
| Once + Live | BlockEngine | below |
Example
{
"topic": "block_engine",
"key": "update",
"value": {
"name": "jito",
"url": "https://mainnet.block-engine.jito.wtf",
"ip": "141.98.216.97",
"status": "connected"
}
}BlockEngine
| Field | Type | Description |
|---|---|---|
| name | string | A short, descriptive name for the block engine |
| url | string | An HTTP URL for the block engine which the validator client connects to |
| ip | string | The resolved IP address of the block engine |
| status | string | One of disconnected, connecting, or connected indicating the state of the connection to the block engine |
epoch
Information about an epoch. Epochs are never modified once they have been determined, so the topic only publishes a continuous stream of new epochs as they are known. When connecting, the current and next epoch are known, unless the validator has recently booted in which case they may not be known and no epochs will be sent until the snapshot is loaded. Epochs become known one epoch in advance, and will only be published once they are confirmed (the prior epoch has fully rooted).
epoch.new
| frequency | type | example |
|---|---|---|
| Once + Live | EpochSchedule | below |
Example
{
"epoch": 636,
"start_time_nanos": "12412481240412",
"end_time_nanos": "1719910299914232",
"start_slot": 274752000,
"end_slot": 275183999,
"epoch_schedule": {
"slots_per_epoch": 432000,
"first_normal_epoch": 0,
"first_normal_slot": 0,
"warmup": false
},
"target_slot_duration_nanos": 400000000,
"excluded_stake_lamports": "0",
"staked_pubkeys": [
"Fe4StcZSQ228dKK2hni7aCP7ZprNhj8QKWzFe5usGFYF",
"2CeCyRoYQcctDmbXWrSUfTT4aQkGVCnArAmbdmQ5QGFi",
"6JPDr4URdEDP5MqPgmDT6jk2nToyMUzNU27qsGxrRgKz",
"8ri9HeWZv4Dcf4BD46pVPjmefzJLpbtfdAtyxyeG4enL",
// ... many more ...
],
"staked_lamports": [
"360",
"240",
"180",
"9991",
// ... many more ...
],
"leader_slots": [
15,
1542,
761,
// ... many more ...
]
}EpochSchedule
| Field | Type | Description |
|---|---|---|
| epoch | number | An identity counter for each epoch, starting at zero for the first epoch and going up |
| start_time_nanos | string | A UNIX timestamp, in nanoseconds, of when the epoch started. This is the time the last non-skipped block of the prior epoch finished replaying locally on this validator, if the validator was online when that happened, otherwise it is null |
| end_time_nanos | string | A UNIX timestamp, in nanoseconds, of when the epoch ended. This is the time the last non-skipped block of the epoch finished replaying locally on this validator, if the validator was online when that happened, otherwise it is null |
| start_slot | number | The first slot (inclusive) in the epoch |
| end_slot | number | The last slot (inclusive) in the epoch |
| epoch_schedule | EpochScheduleConfig|null | The cluster's epoch schedule parameters, which can be used to map any slot to its epoch. Always null on Frankendancer |
| target_slot_duration_nanos | number | The cluster-wide target slot duration, in nanoseconds, for the epoch. This is typically 400000000 (400ms) on most clusters unless a reduce_slot_time feature gate is in effect |
| excluded_stake_lamports | string | Always zero. Firedancer tracks the complete validator-admission-ticket stake set. |
| staked_pubkeys | string[] | All validator identity keys in the validator-admission-ticket stake set for this epoch, capped at 2,000 entries |
| staked_lamports | string[] | A list with the same length as the staked_pubkeys field. stake_lamports[ i ] is the number of lamports staked on the pubkey staked_pubkeys[ i ] as of this epoch |
| leader_slots | number[] | An array, one entry per four slots, of which pubkey in the leader_pubkeys array is leader for those slots. On mainnet-beta this array will always have a length of 108,000, which is the number of slots in an epoch divided by four. Leader slots are in groups of four because the leader schedule is generated in such a way as to guarantee each leader gets at least four consecutive slots. For example, to find the pubkey of the leader in slot 1000 of the epoch, it is staked_pubkeys[ leader_slots[ 1000/4 ] ] |
EpochScheduleConfig
| Field | Type | Description |
|---|---|---|
| slots_per_epoch | number | The number of slots in every epoch at or after first_normal_epoch |
| first_normal_epoch | number | The first epoch whose length is slots_per_epoch |
| first_normal_slot | number | The first slot in first_normal_epoch |
| warmup | boolean | Whether epochs before first_normal_epoch use the protocol's warmup schedule, starting at 32 slots and doubling in length each epoch |
Mapping a slot to an epoch
When epoch_schedule is not null, use the following exact integer formula for any non-negative integer slot. floor_log2 is the floor of the base-2 logarithm and div is integer division. MINIMUM_SLOTS_PER_EPOCH is the Solana protocol constant 32.
if slot < first_normal_slot:
epoch = floor_log2(slot + MINIMUM_SLOTS_PER_EPOCH)
- floor_log2(MINIMUM_SLOTS_PER_EPOCH)
else:
epoch = first_normal_epoch
+ ((slot - first_normal_slot) div slots_per_epoch)When warmup is false every slot uses the second branch. Warmup epochs are used on testnet.
On establishing a connection two epochs are sent to the client. The current epoch that the cluster is in, and the next epoch. From then on, new epochs are published live as they are calculated by the validator. For epoch T, it is published as end_slot in epoch T-2 is rooted. The epoch is speculatively known as soon as end_slot in epoch T-2 is completed, rather than rooted, but no speculative epoch information is published until the epoch is finalized by rooting the slot.
gossip
Information about the validator's connection to the gossip network. Gossip is a distributed database which maintains a single underlying store called the Cluster Replicated Data Store (CRDS), which this documentation will simply call the "Gossip Table". The Gossip Table has "CRDS values", which are store entries that take the form of one of several different structured variants specified in the protocol. This documentation will simply call these "table entries".
Note that "Gossip messages" and "Gossip Table entries" are distinct measures and therefore cannot be compared coherently. A typical Gossip message may contain several table entries, or none at all.
The server maintains a table called the "Peer Table" with per-peer Gossip connection metrics. This table is large and updates frequently. Instead of sending a new copy of the table to every client periodically, the node maintains a viewport of a sorted instance of the Peer Table. The gossip.query_scroll, gossip.query_sort, gossip.peers_size_update, and gossip.view_update allow the client to synchronize with and update their viewport.
A viewport is parameterized by the following attributes:
- sort_key: a list of (column, direction) tuples which describe a possible ordering of the Peer Table. Earlier columns in the sort key have higher precedence, meaning they are "stable sorted" later. This increases the visual impact of their ordering.
- start_row: the Peer Table index of the first row in the viewport
- row_cnt: the number of rows in the viewport
The server imposes a limit of a maximum of 200 rows per viewport. When a client first connects, the server assigns them a default viewport state, which is specified below.
gossip.network_stats
| frequency | type | example |
|---|---|---|
| Once + 300ms | GossipNetworkStats | below |
Example
{
"health": {
"num_push_messages_rx_success": 1234,
"num_push_messages_rx_failure": 0,
"num_push_entries_rx_success": 0,
"num_push_entries_rx_failure": 0,
"num_push_entries_rx_duplicate": 0,
"num_pull_response_messages_rx_success": 0,
"num_pull_response_messages_rx_failure": 0,
"num_pull_response_entries_rx_success": 0,
"num_pull_response_entries_rx_failure": 0,
"num_pull_response_entries_rx_duplicate": 0,
"total_stake": "411123000000000000",
"total_staked_peers": "911",
"total_unstaked_peers": "5334",
"connected_stake": "123456789",
"connected_staked_peers": 623,
"connected_unstaked_peers": 1432,
},
"ingress": {
"total_throughput": 131204210,
"peer_names": ["Coinbase 02", "Figment", "Jupiter", ... ],
"peer_identities": ["FDpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21FD", "FD7btgySsrjuo25CJCj7oE7VPMyezDhnx7pZkj2v69FD", "FDXWcZ7T1wP4bW9SB4XgNNwjnFEJ982nE8aVbbNuwFD", ... ],
"peer_throughput": [15121541, 11697591, 9131124 ]
},
"egress": {
"total_throughput": 131204210,
"peer_names": ["Coinbase 02", "Figment", "Jupiter", ... ],
"peer_identities": ["FDpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21FD", "FD7btgySsrjuo25CJCj7oE7VPMyezDhnx7pZkj2v69FD", "FDXWcZ7T1wP4bW9SB4XgNNwjnFEJ982nE8aVbbNuwFD", ... ],
"peer_throughput": [15121541, 11697591, 9131124 ]
},
"storage": {
"capacity": 2097152,
"expired_total": 1234,
"evicted_total": 0,
"count": [0, 10608, 95, ...],
"count_tx": [0, 10608, 95, ...],
"bytes_tx": [0, 9827342, 9723, ...],
},
"messages": {
"num_bytes_rx": [857419, 8839524, 43480758, ...],
"num_bytes_tx": [28938, 2416123, 72351557, ...],
"num_messages_rx": [1364, 20477, 456094, ...],
"num_messages_tx": [26, 2490, 73599, ...],
}
}GossipNetworkStats
| Field | Type | Description |
|---|---|---|
| health | GossipNetworkStake | Aggregate statistics related to the health of the gossip network and the amount of connected peers / stake |
| ingress | GossipNetworkTraffic | Ingress network traffic and peer metrics |
| egress | GossipNetworkTraffic | Egress network traffic and peer metrics |
| storage | GossipStorageStats | Storage statistics showing the storage utilization for the Gossip Table. Inner arrays are ordered according to the following tables_entries array ["ContactInfoV1","Vote","LowestSlot","SnapshotHashes","AccountsHashes","EpochSlots","VersionV1","VersionV2","NodeInstance","DuplicateShred","IncrementalSnapshotHashes","ContactInfoV2","RestartLastVotedForkSlots","RestartHeaviestFork"] |
| messages | GossipMessageStats | Message statistics showing the message traffic for the Gossip Table. Inner arrays are ordered according to the following message_types array ["pull_request","pull_response","push","ping","pong","prune"] |
GossipNetworkHealth
| Field | Type | Description |
|---|---|---|
| num_{push|pull_response}_entries_rx_ | number | The number of Gossip Table entries that this node has ever received. success means only entries that were fully received and included in the Table are counted. failure means only entries that were dropped for any reason, including parsing failures or invariant violations, are counted. duplicate refers to entries that were dropped as duplicates. {push|pull_request} means that only entries received via Gossip {push|pull_request} messages are counted |
| num_{push|pull_response}_messages_rx_ | number | The number of Gossip messages that this node has ever received. success means only messages that were fully valid, even if any entries they contain were dropped. failure means only messages that were dropped for any reason, including parsing failures or invariant violations, are counted. duplicate refers to messages that were dropped as duplicates. {push|pull_request} is the type of Gossip message counted |
| total_stake | number | The total active stake on the Solana network for the current epoch. The information is derived from the getLeaderSchedule rpc call at startup and is fixed for the duration of the epoch |
| total_staked_peers | number | The total number of peers on the current epoch leader schedule also active on Gossip. This information is derived from getClusterNodes and getLeaderSchedule rpc calls at startup |
| total_unstaked_peers | number | The total number of peers active on gossip, not including peers on the leader schedule. This information is derived from getClusterNodes and getLeaderSchedule rpc calls at startup |
| connected_stake | number | The sum of active stake across all peers with a ContactInfo entry in the Gossip Table. The stake quantity is taken from the leader schedule, and reflects the activate stake at the start of the current epoch |
| connected_staked_peers | number | The number of currently connected peers that have nonzero active stake |
| connected_unstaked_peers | number | The number of currently connected peers without any stake currently active |
GossipNetworkTraffic
| Field | Type | Description |
|---|---|---|
| total_throughput | number | The Gossip network throughput in bytes per second |
| peer_names | string[] | The names of the 64 peers on the Gossip network with the largest contribution to our traffic |
| peer_identities | string[] | The base58 identity pubkey of the 64 peers on the Gossip network with the largest contribution to our traffic |
| peer_throughput | number[] | A list of network throughputs in bytes per second. The peer name for each entry is the corresponding entry in peer_names |
GossipStorageStats
| Field | Type | Description |
|---|---|---|
| capacity | number | The total number of entries that can be stored in the Gossip Table before old entries start being evicted |
| expired_total | number | The cumulative number of Gossip Table entries that have expired and been removed |
| evicted_total | number | The cumulative number of Gossip Table entries that have been evicted due to insufficient space |
| count | number[] | count[i] is the number of currently active table_entries[i] entries currently in the Gossip Table |
| count_tx | number[] | count_tx[i] is the number of egress table_entries[i] entries transmitted until now |
| bytes_tx | number[] | bytes_tx[i] is the number of egress table_entries[i] bytes transmitted until now |
GossipMessageStats
| Field | Type | Description |
|---|---|---|
| num_bytes_rx | number[] | num_bytes_rx[i] is the ingress cumulative byte amount received as message_types[i] messages |
| num_bytes_tx | number[] | num_bytes_tx[i] is the egress cumulative byte amount sent for message_types[i] messages |
| num_messages_rx | number[] | num_messages_rx[i] is the ingress cumulative message count received as message_types[i] messages |
| num_messages_tx | number[] | num_messages_tx[i] is the egress cumulative message count sent for message_types[i] messages |
gossip.query_scroll
| frequency | type | example |
|---|---|---|
| Request | GossipViewData | below |
| param | type | description |
|---|---|---|
| start_row | number | The first row in the contiguous chunk of rows from the Peer Table in the client's view |
| end_row | number | The last row in the contiguous chunk of rows from the Peer Table in the client's view |
The client's view of the peer table changes when they scroll. This request includes the bounds for the updated view, which lets the server respond with the view's data. If the requested rows are outside the bounds of the table, only the active rows are included in the response.
When a client first connects, before a query_scroll request has been made, their viewport state will be initialized with start_row=0 and row_cnt=0 (i.e. an empty viewport), meaning they will get no updates until their viewport grows to a non-zero size.
Note that the default client view is an empty viewport, meaning no updates will be published to the client until after the first query_scroll received by the server.
Example
{
"topic": "gossip",
"key": "query_scroll",
"id": 16,
"params": {
"start_row": 10,
"row_cnt": 12,
}
}{
"topic": "gossip",
"key": "query_scroll",
"id": 16,
"value": {
"10": {"IP Address": "192.168.0.1", "col2": 2},
"11": {"IP Address": "192.168.0.2", "col2": 4},
"12": {"IP Address": "192.168.0.3", "col2": 6}
}
}GossipViewData The tabular data in the clients view, as a 2D dictionary. The dictionary is keyed by row index (object keys are always strings). Each value is a dictionary that represents a table row. Each row is keyed by column name, and each row value is the value of the cell for the corresponding (rowIndex, column_name)
gossip.query_sort
| frequency | type | example |
|---|---|---|
| Request | GossipViewData | below |
| param | type | description |
|---|---|---|
| col | string[] | col[ i ] is the name of the column with the ith sort precedence in the requested view |
| dir | number[] | dir[ i ] is sort direction col[ i ] in the requested view |
The server maintains a copy of each client's active sort key. This message allows clients to change their sort key which will in change the ordering of their view. Since updating the sort key changes the client's view completely, the response will be a fresh copy of all the data in the client's new view.
When a client first connects, the start with the following sort key by default, until an update is made.
- ("Stake", desc)
- ("Pubkey", desc)
- ("Name", desc)
- ("Country", desc)
- ("IP Addr", desc)
- ("Ingress Push", desc)
- ("Ingress Pull", desc)
- ("Egress Push", desc)
- ("Egress Pull", desc)
The provided sort key is a list of column names and a corresponding list of column directions. Directions are provided as signed integers with the following meanings:
- ascending: 1
- descending: -1
- no sort / ignore: 0
All columns in the table must be present in the provided sort key. If the column doesn't affect the ordering of the view, it should have a direction of 0. Note that the relative ordering of columns with dir==0 can be arbitrary as it does not change the view ordering.
Example
{
"topic": "gossip",
"key": "query_sort",
"id": 32,
"params": {
"col": ["IP Addr", "Pubkey", "Name", "Country", "Stake", "Egress Pull", "Egress Push", "Ingress Pull", "Ingress Push"],
"dir": [1, 0, 0, 0, 0, 0, 0, 0, 0],
}
}{
"topic": "gossip",
"key": "query_sort",
"id": 32,
"value": {
"10": {"IP Address": "192.168.0.1", ...},
"11": {"IP Address": "192.168.0.2", ...},
"12": {"IP Address": "192.168.0.3", ...}
}
}gossip.peers_size_update
| frequency | type | example |
|---|---|---|
| Live | number | below |
The latest known count of the number of rows in the gossip peer table. This is sent every time the total number of rows in the gossip peer table changes.
Example
{
"topic": "gossip",
"key": "peers_size_update",
"value": 1234
}gossip.view_update
| frequency | type | example |
|---|---|---|
| Once + Live | GossipPeerViewUpdate | below |
Sent every time the content of the client's view changes (i.e. cell values).
Example
{
"topic": "gossip",
"key": "view_update",
"value": {
"changes": [
{
"row_index": 10,
"column_name": "IP Address",
"new_value": "192.168.0.1"
},
{
"row_index": 10,
"column_name": "Port",
"new_value": 12345
}
]
}
}GossipPeerViewUpdate
| Field | Type | Description |
|---|---|---|
| changes | GossipPeerViewCellUpdate[] | An list of cells in the client's view that changed values since the last GossipPeerViewUpdate message |
GossipPeerViewCellUpdate
| Field | Type | Description |
|---|---|---|
| row_index | number | The index of the updated cell's row |
| column_name | string | The name of the updated cell's column |
| new_value | any | The new value display in the cell |
peers
Information about validator peers from the cluster. Peer data is sourced from gossip, the accounts database, and the on-chain configuration program. All peer information is authenticated meaning it can only be reported from the holder of the private key, however not all peer data is validated or checked for correctness. In particular, data from the gossip network and the config program is self reported by the validator and could be empty, corrupt, filled with garbage, or malicious.
Peer information is keyed by the validator identity key. Multiple vote accounts could in theory use the same identity keypair, although it is not likely. Not all identities reported will have gossip data, a vote account, or validator information published to the config program, but all identities will have at least one of these fields reported. Once an identity is no longer in these three data sources, it will be removed.
peers.update
| frequency | type | example |
|---|---|---|
| Once + 60s | PeerUpdate | below |
Example
{
"update": [
{
"identity_pubkey": "Fe4StcZSQ228dKK2hni7aCP7ZprNhj8QKWzFe5usGFYF",
"gossip": {
"version": "1.18.15",
"feature_set": 4215500110,
"wallclock": 0,
"shred_version": 0,
"sockets": {
"gossip": "93.119.195.160:8001",
"tpu": "192.64.85.26:8000",
// ... other sockets ...
},
"country_code": "CN",
"city_name": "Beijing"
},
"vote": [
{
"vote_account": "8ri9HeWZv4Dcf4BD46pVPjmefzJLpbtfdAtyxyeG4enL",
"activated_stake": "5812",
"last_vote": 281795801,
"root_slot": 281795770,
"epoch_credits": 5917,
"commission": 5,
"delinquent": false
}
],
"info": {
"name": "ExampleStake Firedancer 🔥💃",
"details": "A longer description of the validator, perhaps describing the team behind it or how the node is operated",
"website": "https://github.com/firedancer-io/firedancer",
"icon_url": "https://docs.firedancer.io/fire.svg",
"keybase_username": ""
}
}
],
"remove": [
{ "identity_pubkey": "8ri9HeWZv4Dcf4BD46pVPjmefzJLpbtfdAtyxyeG4enL" }
]
}PeerUpdateGossip
| Field | Type | Description |
|---|---|---|
| wallclock | number | Not entirely sure yet TODO |
| shred_version | number | A u16 representing the shred version the validator is configured to use. The shred version is changed when the cluster restarts, and is used to make sure the validator is talking to nodes that have participated in the same cluster restart |
| client_id | number|null | The client id broadcast by the validator on Gossip. Refer to https://github.com/solana-foundation/solana-validator-client-ids/blob/main/client-ids.csv for an official mapping of id to name. Will be null on Frankendancer. |
| version | string|null | Software version being advertised by the validator. Might be null if the validator is not gossiping a version, or we have received the contact information but not the version yet. The version string, if not null, will always be formatted like major.minor.patch where major, minor, and patch are u16s |
| feature_set | number|null | First four bytes of the FeatureSet hash interpreted as a little endian u32. Might be null if the validator is not gossiping a feature set, or we have received the contact information but not the feature set yet |
| sockets | [key: string]: string | A dictionary of sockets that are advertised by the validator. key will be one of gossip serve_repair_quic, rpc, rpc_pubsub, serve_repair, tpu, tpu_forwards, tpu_forwards_quic, tpu_quic, tpu_vote, tvu, tvu_quic, tpu_vote_quic, or alpenglow. The value is an address like <addr>:<port>: the location to send traffic to for this validator with the given protocol. Address might be either an IPv4 or an IPv6 address |
| country_code | string|null | ISO 3166-1 alpha-2 country code of where the validator is located, determined by GeoIP lookup on the gossip IP address. This information is powered by DB-IP.com. Country code may not be correct and is a best estimate. If no country code could be determined, will be null. |
| city_name | string|null | The name of the city where the validator is located, determined by GeoIP lookup on the gossip IP address. This information is powered by DB-IP.com. City name may not be correct and is a best estimate. If no city name could be determined, will be null. |
PeerUpdateVoteAccount
| Field | Type | Description |
|---|---|---|
| vote_account | string | The public key of vote account, encoded in base58 |
| activated_stake | string | The amount of stake in lamports that is activated on this vote account for the current epoch. Warming up or cooling down stake that was delegating during this epoch is not included |
| last_vote | number|null | The last vote by the vote account that was landed on chain, as seen by this validator. If the vote account has not yet landed any votes on the chain this will be null |
| root_slot | number|null | The last slot that was rooted by the vote account, based on the vote history. If the vote account has not yet rooted any slots this will be null |
| epoch_credits | number | The number of credits earned by the vote account during the current epoch |
| delinquent | boolean | Whether the vote account is delinquent or not. A vote account is considered delinquent if it has not had a vote land on chain for any of the last 127 (inclusive) confirmed slots, according to this validator. If there have been less than 128 confirmed slots on the chain (it is a new chain), a validator is considered delinquent only if it has not voted yet at all |
PeerUpdateInfo
| Field | Type | Description |
|---|---|---|
| name | string | Self reported name of the validator, could be any string or empty string if there is no name set |
| details | string | Self reported detailed description of the validator, could be any string or empty string if there is no details set |
| website | string | Self reported website of the validator, could be any string and need not be a valid URI, or could be empty string if there is no website set |
| icon_url | string | Self reported URL of the validator icon, could be any string and need not be a valid URI, or could be empty string if there is no icon URI set |
| keybase_username | string | Self reported keybase username of the validator, could be any string or empty string if there is no username set. Keybase is a public, legacy storage for icon images. Although this method for publicizing an icon is deprecated, it is included for completeness as many validators have not migrated to iconUrl |
PeerUpdate
| Field | Type | Description |
|---|---|---|
| identity | string | Identity public key of the validator, encoded in base58 |
| gossip | PeerUpdateGossip|null | Information reported for the validator identity over the gossip network. This is authenticated and the gossip node must have been in possession of the private key to publish gossip data as this identity. Gossip information is not validated or checked for correctness and could be set to any values by the peer |
| vote | PeerUpdateVoteAccount[] | Information about the vote account(s) associated with this identity key, if there are any. It is extremely unusual for multiple vote accounts to report the same identity key. Vote account information like stake and commission is derived from the accounts on chain and cannot be corrupt, invalid, or incorrect |
| info | PeerUpdateInfo|null | If the validator has published self reported identifying information to the chain. This is authenticated and the operator must have been in possession of the private key to publish info as this identity. Information is not validated or checked for correctness and could be set to any values by the peer |
PeerRemove
| Field | Type | Description |
|---|---|---|
| identity | string | Identity public key of the validator, encoded in base58 |
PeersUpdate
| Field | Type | Description |
|---|---|---|
| add | GossipPeerUpdate[] | List of peer validators that were added since the last update, or all of the peers for the first update after connecting |
| update | GossipPeerUpdate[] | List of peer validators that were changed since the last update |
| remove | GossipPeerRemove[] | List of peer validators that were removed since the last update |
The gossip.update message is republished every five seconds, with a list of gossip peers added, removed, or updated. The list of peers is full and includes this node itself, nodes with a different shred_version, nodes publishing corrupt or bad information, and so on.
wait_for_supermajority
Messages published during the wait-for-supermajority phase. These messages are only published if the client is configured to boot with the waiting_for_supermajority phase enabled.
wait_for_supermajority.stakes
| frequency | type | example |
|---|---|---|
| Once | WaitForSupermajorityEpoch | below |
Sent once per connection, after the snapshot is fully loaded and validator info has been parsed from the ConfigProgram accounts in the snapshot.
Example
{
"topic": "wait_for_supermajority",
"key": "stakes",
"value": {
"staked_pubkeys": [
"Fe4StcZSQ228dKK2hni7aCP7ZprNhj8QKWzFe5usGFYF",
"2CeCyRoYQcctDmbXWrSUfTT4aQkGVCnArAmbdmQ5QGFi",
"6JPDr4URdEDP5MqPgmDT6jk2nToyMUzNU27qsGxrRgKz"
],
"staked_lamports": [
"360",
"240",
"180"
],
"infos": [
null,
null,
{
"name": "Validator",
"details": "",
"website": "",
"icon_url": "",
"keybase_username": ""
}
]
}
}WaitForSupermajorityEpoch
| Field | Type | Description |
|---|---|---|
| staked_pubkeys | string[] | Identity pubkeys of all staked validators in the snapshot epoch, base58 encoded |
| staked_lamports | string[] | A list with the same length as staked_pubkeys. staked_lamports[ i ] is the number of lamports staked on staked_pubkeys[ i ] |
| infos | (PeerUpdateInfo|null)[] | A list with the same length as staked_pubkeys. Each element is a PeerUpdateInfo object if the validator has published self-reported info via ConfigProgram in the snapshot, or null otherwise |
wait_for_supermajority.peer_add
| frequency | type | example |
|---|---|---|
| Once + Live | string[] | below |
Example
{
"topic": "wait_for_supermajority",
"key": "peer_add",
"value": [
"Fe4StcZSQ228dKK2hni7aCP7ZprNhj8QKWzFe5usGFYF",
"2CeCyRoYQcctDmbXWrSUfTT4aQkGVCnArAmbdmQ5QGFi"
]
}Value is a flat array of base58-encoded identity pubkeys that have come online since the last message (or all currently-online peers on initial connect).
wait_for_supermajority.peer_remove
| frequency | type | example |
|---|---|---|
| Live | string[] | below |
Example
{
"topic": "wait_for_supermajority",
"key": "peer_remove",
"value": [
"9aE6Bp1hbDpMFKqnWGUMbfxfMPXswPbkNwNrSjhpFiSN"
]
}Value is a flat array of base58-encoded identity pubkeys that have gone offline (activity timeout expired) since the last message.
timeline
Historical event data recorded by the validator, queryable over a UNIX nanosecond timestamp window.
timeline.query_shreds
| frequency | type | example |
|---|---|---|
| Request | SlotShreds | below |
| param | type | description |
|---|---|---|
| start_ns | string | Inclusive lower bound of the GUI insertion-time window, as a UNIX timestamp in nanoseconds |
| end_ns | string | Inclusive upper bound of the GUI insertion-time window, as a UNIX timestamp in nanoseconds |
WebSocket clients may request historical shred metadata over a UNIX nanosecond timestamp window. The requested window must not exceed 60 seconds. The response has the same shape as the live slot.live_shreds topic and includes retained events which were inserted into the server database during that window. Events are available as they arrive, without waiting for replay completion. If no shred events fall in the window, the response arrays are empty.
Example
{
"topic": "timeline",
"key": "query_shreds",
"id": 32,
"params": {
"start_ns": "1739657041588000000",
"end_ns": "1739657041589000000"
}
}{
"topic": "timeline",
"key": "query_shreds",
"id": 32,
"value": {
"reference_slot": 289245044,
"reference_ts": "1739657041588242791",
"slot_delta": [0, 0],
"shred_idx": [1234, null],
"event": [0, 1],
"event_ts_delta": ["1000000", "2000000"]
}
}timeline.query_agg_revenue
| frequency | type | example |
|---|---|---|
| Request | TimelineAggRevenue | below |
| param | type | description |
|---|---|---|
| start_ns | string | Inclusive lower bound, as a UNIX timestamp in nanoseconds |
| end_ns | string | Exclusive upper bound, as a UNIX timestamp in nanoseconds |
| granularity | string | Required; one of the granularities below |
start_ns and end_ns are non-negative UNIX nanosecond timestamps, encoded as decimal strings without leading zeros (except "0"). Both must be less than 9223372036854775807, and end_ns must be greater than start_ns. Windows are half-open: [start_ns, end_ns).
| granularities |
|---|
250ms, 500ms, 1s, 2s, 4s, 8s, 15s, 30s, 1m, 2m, 4m, 8m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1d |
The request window is aligned to the request granularity bucket boundaries. At most 10,000 buckets may be requested, and the aligned exclusive end must also be less than 9223372036854775807. The connection is closed if either limit is exceeded.
Revenue aggregates include locally produced blocks. Transactions are bucketed by commit time into cached calendar-day aggregates, retained independently of detailed transaction history.
The GUI's database is wiped on boot. When it reaches capacity, data is evicted approximately oldest-first.
TimelineAggRevenue
| field | type | description |
|---|---|---|
| granularity | string | Echoes the requested granularity |
| reference_ts_ns | string | Start of the first aligned response bucket |
| available_start_ns | string|null | Inclusive start of the retained calendar-day aggregates |
| available_end_ns | string|null | Exclusive end of the retained calendar-day aggregates |
| txn_fees | (string|null)[] | Sum of base transaction fees per bucket, in lamports |
| prio_fees | (string|null)[] | Sum of priority fees per bucket, in lamports |
| tips | (string|null)[] | Sum of tips per bucket, in lamports |
available_start_ns and available_end_ns are half-open lookup bounds, [available_start_ns, available_end_ns), reflecting the data available in the server's database. Both are null when the server is missing data needed for a non-empty response.
Each array has one entry per aligned bucket. Bucket i covers [reference_ts_ns + i*duration, reference_ts_ns + (i+1)*duration). An entry is null when the field is unknown, distinct from a known zero. null values are ignored when computing rolled-up aggregates.
Example
{
"topic": "timeline",
"key": "query_agg_revenue",
"id": 50,
"params": {
"start_ns": "1739657040000000000",
"end_ns": "1739657100000000000",
"granularity": "15s"
}
}{
"topic": "timeline",
"key": "query_agg_revenue",
"id": 50,
"value": {
"granularity": "15s",
"reference_ts_ns": "1739657040000000000",
"available_start_ns": "1739577600000000000",
"available_end_ns": "1739664000000000000",
"txn_fees": ["6015000", "5935000", null, "6200000"],
"prio_fees": ["120400", "98200", null, "131000"],
"tips": ["2500000", "0", null, "1000000"]
}
}slot
Slots are opportunities for a leader to produce a block. Their level contract depends on the consensus mode.
Tower SlotLevel
| level | description |
|---|---|
incomplete | The slot does not exist, either because the chain has not yet reached the slot or because it is still in the process of being replayed by our validator |
completed | The slot has been fully received and successfully replayed by our validator |
optimistically_confirmed | The slot has been finished and successfully replayed by our validator, and more than two-thirds of stake have voted to confirm the slot |
rooted | Our validator has rooted the slot and considers the slot final. This occurs when 32 subsequent slots have been built on top of it |
finalized | Our validator has rooted the slot, and more than two-thirds of stake has rooted the slot; the network considers it final |
Tower also publishes a separate skipped boolean. Before a slot becomes rooted or finalized, its level and skipped state can regress or change when the validator switches forks. Rooted and finalized states are permanent.
Alpenglow SlotLevel
| level | description |
|---|---|
incomplete | No block for this slot has completed replay on the currently selected fork |
completed | A block for this slot has completed replay locally, but no stronger applicable certificate state is being reported |
notarized | The selected block has completed replay and has a regular or fallback notarization certificate, distinguished by notarization_kind |
skip_notarized | A skip certificate has been observed for this slot, but a later finalization has not yet made the skip irreversible |
rooted | The selected block has completed replay locally and is directly or implicitly finalized |
skipped | Alpenglow finality establishes that the canonical branch contains no block at this slot |
Alpenglow does not publish a separate skipped boolean; the two skip states are part of level. rooted and skipped are permanent. Before then, a fork change can cause a level to regress or switch branches. Levels can also jump when a certificate is learned before local replay or when finalizing a descendant implicitly finalizes its ancestors and skips gaps in the branch.
The normal block progression is incomplete → completed → notarized → rooted; the skip progression is incomplete → skip_notarized → skipped. Intermediate updates may be omitted, but notarized and rooted always imply local replay completed, while skipped always implies cluster finality.
Alpenglow uses the following certificate and finalization definitions:
- A regular notarization certificate contains ordinary notarize votes from at least 60% of stake.
- A notar-fallback certificate contains a combination of ordinary notarize and notarize-fallback votes from at least 60% of stake. It makes the block eligible as a parent but does not finalize it.
- A skip certificate contains skip or skip-fallback votes from at least 60% of stake.
- Fast finalization directly finalizes a block with ordinary notarize votes from at least 80% of stake.
- Slow finalization directly finalizes a regularly notarized block with a finalize certificate from at least 60% of stake.
- Finalizing a block implicitly finalizes its ancestors and implicitly skips slots absent from that branch.
Slots are either mine (created by this validator), or not, in which case we are replaying a block from another validator. Slots that are mine contain additional information about our performance creating the block for that slot. The mine field means that this specific validator published the block. It might happen that a block is published by a leader with our identity key, but not this specific validator (for example, if the block was published by another computer, and then this validator took over the identity key with a set-identity operation) in which case the mine field will be set to false, even though the block has our key.
Some information is only known for blocks that have been replayed successfully (reached the completed state), for example the number of transactions in the block. Replay-derived information can remain available after the validator selects a fork which skips that slot because the validator may previously have replayed a block on another fork. Under Tower this can produce skipped: true together with a non-terminal level. Under Alpenglow the level itself becomes skipped once the skip is final. Once known, block information does not typically change, except in rare cases where a leader publishes multiple blocks and the validator changes which block it associates with the slot.
Common SlotPublish fields
| Field | Type | Description |
|---|---|---|
| slot | number | Identity of the slot, counting up from zero for the first slot in the chain |
| mine | boolean | True if this validator was the leader for this slot. This will never change for a slot once it has been published, and will be aligned with the epoch information, except in cases where the validator identity is changed while the validator is running |
| start_timestamp_nanos | string | A UNIX timestamp, in nanoseconds, representing the time that the validator is first aware that it is leader. At this point the poh tile will signal the pack tile to begin filling the block for this slot with transactions |
| target_end_timestamp_nanos | string | A UNIX timestamp, in nanoseconds, representing the target time in nanoseconds that the pack tile should stop scheduling transactions for the slot. Transactions might still finish executing after this end time, if they started executing before it and ran over the deadline. In rare cases, transactions may also appear to begin after this timestamp due to slight clock drift between execution cores |
| duration_nanos | number|null | A duration in nanoseconds of how long it took us to receive and replay the slot. This is the time as measured since we completed replay of the parent slot locally on this validator, til the time we replayed this slot locally on this validator |
| completed_time_nanos | string|null | UNIX timestamp in nanoseconds of when this validator finished replaying the slot locally. If the slot was skipped, this may be null which indicates the block for this slot did not finish replaying on this validator. In some cases, a skipped slot will still have a completed time, if we received the data for the block, replayed it, and then decided to use a different fork |
| level | string | The mode-specific SlotLevel described above |
| max_compute_units | number|null | The maximum number of compute units that can be packed into the slot. This limit is one of many consensus-critical limits defined by the solana protocol, and helps keeps blocks small enough for validators consume them quickly. It may grow occasionally via on-chain feature activations |
| compute_units | number|null | Total number of compute units used by the slot. Compute units are a synthetic metric that attempt to capture, based on the content of the block, the various costs that go into processing that block (i.e. cpu, memory, and disk utilization). They are based on certain transaction features, like the number of included signatures, the number of included signature verification programs, the number of included writeable accounts, the size of the instruction data, the size of the on-chain loaded account data, and the number of computation steps. NOTE: "compute units" is an overloaded term that is often used in misleading contexts to refer to only a single part of the whole consensus-critical cost formula. For example, the getBlock RPC call includes a "computeUnitsConsumed" which actually only refers only the execution compute units associated with a transaction, but excludes other costs like signature costs, data costs, etc. This API will always use compute units in a way that includes ALL consensus-relevant costs, unless otherwise specified |
| shreds | number|null | Total number of shreds in the successfully replayed block. Note value is only available in the Firedancer client and will be 0 or null in the Frankendancer client |
| transaction_fee | string|null | Total amount of transaction fees that this slot collects in lamports after any burning |
| priority_fee | string|null | Total amount of priority fees that this slot collects in lamports after any burning |
| tips | string|null | Total amount of tips that this slot collects in lamports, across all block builders, after any commission to the block builder is subtracted |
| is_voter | boolean|null | Whether the active identity was a voter. Under Alpenglow this is learned when the reward carrier at slot + 8 is replayed, so it remains null until then and the slot is republished once known. Under Tower it is available immediately. |
Tower-only SlotPublish fields
| Field | Type | Description |
|---|---|---|
| skipped | boolean | True if the slot is skipped on the validator's currently active fork |
| success_nonvote_transaction_cnt | number|null | Total number of successfully executed non-vote transactions in the block |
| failed_nonvote_transaction_cnt | number|null | Total number of failed non-vote transactions in the block |
| success_vote_transaction_cnt | number|null | Total number of successfully executed vote transactions in the block |
| failed_vote_transaction_cnt | number|null | Total number of failed vote transactions in the block |
| vote_slot | number|null | The most recent slot for which this validator had landed a vote as of the time this slot was replayed |
| vote_latency_exact | number|null | The skip-discounted distance between this slot and the slot containing this validator's vote for it, or null if the vote has not landed |
Alpenglow-only SlotPublish fields
| Field | Type | Description |
|---|---|---|
| success_transaction_cnt | number|null | Total number of successfully executed on-chain transactions in the block |
| failed_transaction_cnt | number|null | Total number of failed on-chain transactions in the block |
| notarization_kind | string|null | Strongest notarization proof known for the selected block: regular, fallback, or null. regular supersedes fallback and is also implied by direct fast or slow finalization. The value is retained after the block becomes rooted |
| finalization_kind | string|null | Strongest finality proof known for this slot: fast, slow, implicit, or null. fast supersedes slow, and either direct proof supersedes implicit. A terminal rooted or skipped level always has a non-null value; skipped uses implicit |
| vote_slot | number|null | Latest slot for which this validator's vote was included in a reward certificate, as of this slot's replay. It is the slot voted on, not the slot which carried the certificate. It is null when this validator has no recorded reward-certificate participation yet |
| vote_rewarded | boolean|null | Whether this validator's ordinary notarize or skip vote for this slot was included in the reward certificate carried by slot + 8. It is null until the reward outcome is known, or when is_voter for this slot is false. Once resolved, the server republishes this slot with true or false. |
| vote_count | number|null | Number of distinct validators that voted for this slot, counting each signer once across the notarize and skip reward certificates carried by slot + 8. It is null before those certificates are replayed |
slot.skipped_history
| frequency | type | example |
|---|---|---|
| Once | number[] | [286576808, 286576809, 286576810, 286576811, 286625025, 286625026, 286625027] |
A list of leader slot of the validator from the current epoch which were skipped.
The skipped slots include only rooted slots of ours which are skipped on the currently active fork under Tower. Under Alpenglow, they include only our slots which have reached the terminal skipped level.
slot.skipped_history_cluster
| frequency | type | example |
|---|---|---|
| Once | number[] | [286576808, 286576809, 286576810, 286576811, 286625025, 286625026, 286625027] |
A list of all leader slots in the current epoch which are skipped and rooted under Tower, or which have reached the terminal skipped level under Alpenglow.
slot.late_votes_history
| frequency | type | example |
|---|---|---|
| Once | LateVotesHistory | below |
A collection of slots from this epoch for which this validator voted late or not at all. Specifically, the following slots are included
- rooted slots with a vote latency > 1
- rooted slots that were never voted for that were not skipped
The slot array is run-length encoded: it holds pairs [run_start, run_end] (both inclusive) describing contiguous ranges of affected slots. Each run shares the same latency_exact value.
The latency_exact array holds one entry per run, so its length is exactly half the length of the slot. It is the skip-discounted vote latency (null for missing vote).
This message is Tower-only and is not published by an Alpenglow validator.
Example
{
"topic": "slot",
"key": "late_votes_history",
"value": {
"slot": [286576808, 286576808, 286576810, 286576810, 286576811, 286576811, 286625025, 286625025],
"latency_exact": [2, null, 3, 2]
}
}slot.missed_vote_history
| frequency | type | example |
|---|---|---|
| Once | MissedVoteHistory | below |
An Alpenglow-only collection of resolved slots from the current epoch for which this validator was an eligible voter for that slot but was not included in either reward certificate (skip or notarize) carried for the slot. Fallback and finalization votes are not rewarded and do not affect this result.
A reward certificate for slot s is carried by slot s + 8. Slots whose reward outcome is not yet known, including the most recent eight slots, are omitted rather than reported as missed.
The slot array is run-length encoded as inclusive [run_start, run_end] pairs.
Example
{
"topic": "slot",
"key": "missed_vote_history",
"value": {
"slot": [286576808, 286576810, 286625025, 286625025]
}
}MissedVoteHistory
| Field | Type | Description |
|---|---|---|
| slot | number[] | An even-length array of inclusive [run_start, run_end] pairs for missed reward slots |
slot.live_shreds
| frequency | type | example |
|---|---|---|
| 50ms | SlotShreds | below |
The validator sends a continuous stream of update messages with detailed information about the time and duration of different shred state transitions (i.e. shred events). A given event is only ever sent once and is broadcast to all WebSocket clients.
Example
{
"topic": "slot",
"key": "live_shreds",
"value": {
"reference_slot": 289245044,
"reference_ts": "1739657041588242791",
"slot_delta": [0, 0],
"shred_idx": [1234, null],
"event": [0, 1],
"event_ts_delta": ["1000000", "2000000"]
}
}SlotShreds
| Field | Type | Description |
|---|---|---|
| reference_slot | number | The smallest slot number across all the shreds in a given message |
| reference_ts | number | The smallest UNIX nanosecond event timestamp number across all the events in a given message |
| slot_delta | number[] | reference_slot + slot_delta[i] is the slot to which shred event i belongs |
| shred_idx | (number|null)[] | shred_idx[i] is the slot shred index of the shred for shred event i. If null, then shred event i applies to all shreds in the slot (i.e. this is used for slot_complete) |
| event | number[] | event[i] is the enum value for shred event i. Possible values are repair_request (0), shred_received_turbine (1), shred_received_repair (2), shred_replay_exec_done (3), slot_complete (4), and shred_published (6) |
| event_ts_delta | string[] | reference_ts + event_ts_delta[i] is the UNIX nanosecond timestamp when shred event i occurred |
slot.update
| frequency | type | example |
|---|---|---|
| Live | SlotUpdate | below |
Example
SlotUpdate
| Field | Type | Description |
|---|---|---|
| publish | SlotPublish | General information about the slot. Contains several nullable fields in case a future slot is queried and the information is not known yet |
| waterfall | TxnWaterfall|null | If the slot is not mine, will be null. Otherwise, a waterfall showing reasons transactions were acquired since the end of the prior leader slot |
| tile_primary_metric | TilePrimaryMetric|null | If the slot is not mine, will be null. Otherwise, max value of per-tile-type primary metrics since the end of the prior leader slot |
slot.query_rankings
| frequency | type | example |
|---|---|---|
| Request | SlotRankings | below |
| param | type | description |
|---|---|---|
| mine | bool | If mine is true, only include slots produced by this validator in the result. Otherwise, any slot from the current epoch may be included |
Example
{
"topic": "slot",
"key": "query_rankings",
"id": 32,
"params": {
"mine": false
}
}{
"topic": "slot",
"key": "query_rankings",
"id": 32,
"value": {
"slots_largest_tips": [1, 2, 3],
"vals_largest_tips": [12345678, 1234567, 123456],
"slots_largest_fees": [1, 2, 3],
"vals_largest_fees": [12345678, 1234567, 123456],
"slots_largest_rewards": [1, 2, 3],
"vals_largest_rewards": [12345678, 1234567, 123456],
"slots_largest_duration": [1, 2, 3],
"vals_largest_duration": [450000000, 440000000, 430000000],
"slots_largest_compute_units": [1, 2, 3],
"vals_largest_compute_units": [47000000, 46000000, 45000000],
"slots_largest_skipped": [7, 8, 9],
"vals_largest_skipped": [7, 8, 9],
"slots_smallest_tips": [1, 2, 3],
"vals_smallest_tips": [0, 0, 0],
"slots_smallest_fees": [1, 2, 3],
"vals_smallest_fees": [0, 0, 0],
"slots_smallest_rewards": [1, 2, 3],
"vals_smallest_rewards": [0, 0, 0],
"slots_smallest_duration": [1, 2, 3],
"vals_smallest_duration": [100000000, 120000000, 160000000],
"slots_smallest_compute_units": [1, 2, 3],
"vals_smallest_compute_units": [15000000, 16000000, 17000000],
"slots_smallest_skipped": [4, 5, 6],
"vals_smallest_skipped": [4, 5, 6]
}
}SlotRankings
| Field | Type | Description |
|---|---|---|
| {slots|vals}_{smallest|largest}_tips | number[] | Rankings for the {smallest|largest} tips this epoch |
| {slots|vals}_{smallest|largest}_fees | number[] | Rankings for the {smallest|largest} fees this epoch |
| {slots|vals}_{smallest|largest}_rewards | number[] | Rankings for the {smallest|largest} rewards this epoch |
| {slots|vals}_{smallest|largest}_rewards_per_cu | number[] | Rankings for the {smallest|largest} rewards/cu ratio this epoch |
| {slots|vals}_{smallest|largest}_duration | number[] | Rankings for the {smallest|largest} slot durations this epoch |
| {slots|vals}_{smallest|largest}_compute_units | number[] | Rankings for the {smallest|largest} compute units this epoch |
| {slots|vals}_{smallest|largest}_skipped | number[] | Rankings for the {earliest|latest} skipped slots this epoch |
Each metric in this message will have four associated arrays.
- vals_smallest_metric: Metric value for the lowest ranked slots (sorted ascending)
- slots_smallest_metric: Slot numbers for vals_smallest_metric source slots
- slots_largest_metric: Slot numbers for vals_largest_metric source slots
- vals_largest_metric: Metric value for the highest ranked slots (sorted descending)
Slots before boot time are not included in these rankings. Unless explicitly mentioned, skipped slots are not included.
slot.query
| frequency | type | example |
|---|---|---|
| Request | SlotResponse | below |
| param | type | description |
|---|---|---|
| slot | number | The slot to query for information about |
Alpenglow example
{
"topic": "slot",
"key": "query",
"id": 32,
"params": {
"slot": 289245044
}
}{
"topic": "slot",
"key": "query",
"id": 32,
"value": {
"publish": {
"slot": 289245044,
"mine": true,
"start_timestamp_nanos": null,
"target_end_timestamp_nanos": null,
"duration_nanos": 400000000,
"completed_time_nanos": "1739657041688342791",
"level": "rooted",
"notarization_kind": "regular",
"finalization_kind": "fast",
"success_transaction_cnt": 10524,
"failed_transaction_cnt": 6746,
"max_compute_units": 48000000,
"compute_units": 0,
"shreds": 123,
"transaction_fee": "12345",
"priority_fee": "123456",
"tips": "0",
"is_voter": true,
"vote_slot": 289245043,
"vote_rewarded": true,
"vote_count": 115
}
}
}slot.query_detailed
| frequency | type | example |
|---|---|---|
| Request | SlotResponse | below |
| param | type | description |
|---|---|---|
| slot | number | The slot to query for information about |
Alpenglow example
{
"topic": "slot",
"key": "query_detailed",
"id": 32,
"params": {
"slot": 289245044
}
}{
"topic": "slot",
"key": "query_detailed",
"id": 32,
"value": {
"publish": {
"slot": 289245044,
"mine": true,
"start_timestamp_nanos": null,
"target_end_timestamp_nanos": null,
"duration_nanos": 400000000,
"completed_time_nanos": "1739657041688342791",
"level": "rooted",
"notarization_kind": "regular",
"finalization_kind": "fast",
"success_transaction_cnt": 10524,
"failed_transaction_cnt": 6746,
"max_compute_units": 48000000,
"compute_units": 0,
"shreds": 123,
"transaction_fee": "12345",
"priority_fee": "123456",
"tips": "0",
"is_voter": true,
"vote_slot": 289245043,
"vote_rewarded": true,
"vote_count": 115
},
"waterfall": {
"in": {
"pack_cranked": 1,
"pack_retained": 0,
"resolv_retained": 0,
"quic": 28159,
"udp": 14323,
"gossip": 0,
"block_engine": 13
},
"out": {
"net_overrun": 0,
"quic_overrun": 0,
"quic_frag_drop": 0,
"quic_abandoned": 0,
"tpu_quic_invalid": 0,
"tpu_udp_invalid": 0,
"verify_overrun": 0,
"verify_parse": 0,
"verify_failed": 0,
"verify_duplicate": 114,
"dedup_duplicate": 19384,
"resolv_lut_failed": 3,
"resolv_expired": 0,
"resolv_ancient": 0,
"resolv_retained": 0,
"resolv_no_ledger": 0,
"pack_invalid": 0,
"pack_expired": 0,
"pack_already_executed": 0,
"pack_retained": 2225,
"pack_wait_full": 0,
"pack_leader_slow": 0,
"bank_invalid": 10253,
"block_success": 3101,
"block_fail": 3720
}
},
"tile_primary_metric": {
"quic": 3,
"net_in": 37803082,
"net_out": 4982399,
"verify": 0,
"dedup": 0,
"bank": 89407,
"pack": 0,
"poh": 0,
"shred": 0,
"store": 0
},
"tile_timers": [
{
"timestamp_nanos": "1739657041688242791",
"tile_timers": [
44.972112412,
90.12,
5.42148,
6.24870,
5.00158,
8.1111556,
76.585,
44.225,
12.98,
16.2981,
43.857,
14.1,
3.15716,
93.2456,
87.998
]
},
{
"timestamp_nanos": "1739657041688342791",
"tile_timers": [
44.972112412,
90.12,
5.42148,
6.24870,
5.00158,
8.1111556,
76.585,
44.225,
12.98,
16.2981,
43.857,
14.1,
3.15716,
93.2456,
87.998
]
},
// ... many more ...
],
"scheduler_counts": [
{
"timestamp_nanos": "1739657041688242791",
"regular": 246,
"votes": 0,
"conflicting": 123,
"bundles": 123
},
{
"timestamp_nanos": "1739657041688342791",
"regular": 244,
"votes": 0,
"conflicting": 123,
"bundles": 123
}
// ... many more ...
]
}
}slot.query_transactions
| frequency | type | example |
|---|---|---|
| Request | SlotTransactionsResponse | below |
| param | type | description |
|---|---|---|
| slot | number | The slot to query for information about |
Alpenglow example
{
"topic": "slot",
"key": "query_transactions",
"id": 32,
"params": {
"slot": 289245044
}
}{
"topic": "slot",
"key": "query_transactions",
"id": 32,
"value": {
"publish": {
"slot": 289245044,
"mine": true,
"start_timestamp_nanos": "1739657041688346791",
"target_end_timestamp_nanos": "1739657042088346880",
"duration_nanos": 400000000,
"completed_time_nanos": "1739657041688342791",
"level": "rooted",
"notarization_kind": "regular",
"finalization_kind": "fast",
"success_transaction_cnt": 10524,
"failed_transaction_cnt": 6746,
"max_compute_units": 48000000,
"compute_units": 0,
"shreds": 123,
"transaction_fee": "12345",
"priority_fee": "123456",
"tips": "0",
"is_voter": true,
"vote_slot": 289245043,
"vote_rewarded": true,
"vote_count": 115
},
"limits": {
"used_total_block_cost": 10000000,
"used_total_vote_cost": 0,
"used_account_write_costs": 1000000,
"used_total_bytes": 123456,
"used_total_microblocks": 12345,
"max_total_block_cost": 100000000,
"max_total_vote_cost": 0,
"max_account_write_cost": 40000000,
"max_total_bytes": 987654321,
"max_total_microblocks": 32768
},
"scheduler_stats": {
"end_slot_reason": "timeout",
"block_hash": "9cjSTpZ82xeHouc3sDEzsQ8yZHKjvc8o46EstxfrprD1",
"slot_schedule_counts": [123, 123, 123, 123, 123, 123, 123],
"end_slot_schedule_counts": [0, 10, 0, 0, 0, 0, 0],
"pending_smallest_cost": 3000,
"pending_smallest_bytes": 1232,
"pending_vote_smallest_cost": null,
"pending_vote_smallest_bytes": null
},
"transactions": {
"start_timestamp_nanos": "1739657041688346791",
"target_end_timestamp_nanos": "1739657042088346880",
"txn_arrival_timestamps_nanos": ["1754409729593613895"],
"txn_bank_idx": [0],
"txn_compute_units_consumed": [3428],
"txn_compute_units_requested": [3428],
"txn_check_start_timestamps_nanos": ["1754409729594432846"],
"txn_commit_end_timestamps_nanos": ["1754409729594500000"],
"txn_commit_start_timestamps_nanos": ["1754409729594477657"],
"txn_error_code": [0],
"txn_execute_start_timestamps_nanos": ["1754409729594455631"],
"txn_from_bundle": [false],
"txn_landed": [true],
"txn_load_start_timestamps_nanos": ["1754409729594451074"],
"txn_mb_end_timestamps_nanos": ["1754409729594625003"],
"txn_mb_start_timestamps_nanos": ["1754409729594431327"],
"txn_microblock_id": [0],
"txn_priority_fee": ["0"],
"txn_signature": ["2BfWBnhTP1ZZwFZutwThj5VT1hX71X9otbgFr21W2XJfcppXakbPCvJ2eCh8eBcS74Lfjar5AuowuppAjsEceSuW"],
"txn_transaction_fee": [0],
"txn_tips": ["0"],
"txn_source_ipv4": ["123.123.123.123"],
"txn_source_tpu": ["quic"]
}
}
}SlotResponse
| Field | Type | Description |
|---|---|---|
| publish | SlotPublish | General information about the slot. Contains several nullable fields in case a future slot is queried and the information is not known yet |
| waterfall | TxnWaterfall|null | If the slot is not mine, will be null. Otherwise, a waterfall showing reasons transactions were acquired since the end of the prior leader slot |
| tile_primary_metric | TilePrimaryMetric|null | If the slot is not mine, will be null. Otherwise, max value of per-tile-type primary metrics since the end of the prior leader slot |
| tile_timers | TsTileTimers[]|null | If the slot is not mine, will be null. Otherwise, an array of TsTileTimers samples from the slot, sorted earliest to latest. We store this information for the most recently completed 4096 leader slots. This will be null for leader slots before that |
| scheduler_counts | SchedulerCounts[]|null | If the slot is not mine, will be null. Otherwise, an array of SchedulerCounts samples from the slot, sorted earliest to latest. We store this information for the most recently completed 4096 leader slots. This will be null for leader slots before that |
SlotTransactionsResponse
| Field | Type | Description |
|---|---|---|
| publish | SlotPublish | General information about the slot. Contains several nullable fields in case a future slot is queried and the information is not known yet |
| transactions | Transactions|null | If the slot is not mine, will be null. Otherwise, metrics for the transactions in this slot. Arrays have a separate entry for each scheduled transaction that was packed in this slot, and are ordered in the same order the transactions appear in the block. Note that not all scheduled transactions will land in the produced block (e.g. failed bundles are ignored), but these arrays nonetheless include metrics for excluded transactions |
| limits | SlotLimits | The various protocol-derived resource limits and their corresponding utilization for this block. If mine is false, then this value is null |
| scheduler_stats | SlotScheduleStats | Various metrics tracked by the transaction scheduler and collected at the end of a leader slot. If mine is false, then this value is null |
TxnWaterfall
| Field | Type | Description |
|---|---|---|
| in | TxnWaterfallIn | Transactions received into the waterfall |
| out | TxnWaterfallOut | Transactions sent out of the waterfall |
TxnWaterfallIn
| Field | Type | Description |
|---|---|---|
| pack_cranked | number | Transactions were created as part of an initializer bundle. Initializer bundles are special bundles created by pack that manage block engine state on the chain. They contain crank transactions, which create and update tip distribution accounts. There is typically one crank transaction per leader rotation |
| pack_retained | number | Transactions were received during or prior to an earlier leader slot, but weren't executed because they weren't a high enough priority, and were retained inside the validator to potentially be included in a later slot |
| resolv_retained | number | Transactions were received during or prior to an earlier leader slot, but weren't executed because we did not know the blockhash they referenced. They were instead kept in a holding area in case we learn the blockhash later |
| quic | number | A QUIC transaction was received. The stream does not have to successfully complete |
| udp | number | A non-QUIC UDP transaction was received |
| gossip | number | Under Tower, a gossipped vote transaction was received from a gossip peer. This fixed-shape field is deprecated and always 0 under Alpenglow |
| block_engine | number | A transaction received from a block engine, for example Jito. The transaction might or might not have been part of a bundle |
TxnWaterfallOut
| Field | Type | Description |
|---|---|---|
| net_overrun | number | Transactions were dropped because the net tile couldn't keep up with incoming network packets. It is unclear how many transactions would have been produced by the packets that were dropped, and this counter (along with the corresponding counter for the in side) assumes one transaction per dropped packet |
| quic_overrun | number | Transactions were dropped because the QUIC tile couldn't keep up with incoming network packets. It is unclear how many transactions would have been produced by the fragments from net that were overrun, and this counter (along with the corresponding counter for the in side) assumes one transaction per dropped packet |
| quic_frag_drop | number | Transactions were dropped because there are more ongoing receive operations than buffer space |
| quic_abandoned | number | Transactions were dropped because a connection closed before all bytes were received |
| tpu_quic_invalid | number | Transactions were dropped because the QUIC tile decided that incoming QUIC packets were not valid. It is unclear how many transactions would have been produced by the packets that were invalid, and this counter (along with the corresponding counter for the in side) assumes one transaction per invalid packet |
| tpu_udp_invalid | number | Transactions were dropped because the QUIC tile decided that incoming non-QUIC (regular UDP) packets were not valid |
| verify_overrun | number | Transactions were dropped because the verify tiles could not verify them quickly enough |
| verify_parse | number | Transactions were dropped because they were malformed and failed to parse |
| verify_failed | number | Transactions were dropped because signature verification failed |
| verify_duplicate | number | Transactions were dropped because the verify tiles determined that they had already been processed |
| dedup_duplicate | number | Transactions were dropped because the dedup tile determined that they had already been processed |
| resolv_retained | number | Transactions were retained inside the validator memory because they referenced a blockhash we do not yet know. We might include the transactions in a future block, if we learn about the blockhash they reference |
| resolv_lut_failed | number | Transactions were dropped because they contained invalid address lookup tables (LUTs) |
| resolv_expired | number | Transactions were dropped because they contained a transaction that was already expired |
| resolv_no_ledger | number | Transactions were dropped because they contained a LUT but we didn't yet have a ledger to look them up in |
| resolv_ancient | number | Transactions were dropped because they referenced a blockhash we didn't recognize, and while waiting to see if the blockhash would arrive, the buffer became full |
| pack_invalid | number | Transactions were dropped because pack determined they would never execute. Reasons can include the transaction requested too many compute units, or was too large to fit in a block |
| pack_expired | number | Transactions were dropped because pack determined that their TTL expired |
| pack_already_executed | number | Transactions dropped from pack because they were already executed (in either the replay or leader pipeline) |
| pack_retained | number | Transactions were retained inside the validator memory because they were not high enough priority to make it into a prior block we produced, but have not yet expired. We might include the transactions in a future block |
| pack_leader_slow | number | Transactions were dropped while leader because the bank tiles could not execute them quickly enough, pack will drop the lowest priority transactions first |
| pack_wait_full | number | Transactions were dropped while we were waiting for our leader slot because we ran out of memory to store them. All incoming transactions are dropped without regard for the priority |
| bank_invalid | number | Transactions were dropped because a bank tile could not execute them enough to charge fees. Failed transactions can still pay fees and be included in a block, but invalid transactions do not make it to a block. Reasons can include insufficient fee payer balance, or invalid address lookup tables |
| block_success | number | Transactions made it into a block, and execution succeeded |
| block_fail | number | Transactions made it into a block, but execution failed |
SchedulerCounts
| Field | Type | Description |
|---|---|---|
| timestamp_nanos | string | A UNIX nanosecond timestamp representing the time when these counts were sampled by the gui tile. |
| regular | number | The number of transactions stored in the "regular" treap (i.e. the primary buffer) at timestamp_nanos. Under Alpenglow this includes transactions which would have been classified as vote transactions under Tower |
| votes | number | Under Tower, the number of transactions stored in the "votes" treap (i.e. the buffer dedicated to vote transactions) at timestamp_nanos. This fixed-shape field is deprecated and always 0 under Alpenglow |
| conflicting | number | The number of transactions stored in the "conflicting" treap (i.e. the buffer for transactions with perceived account write conflicts, which receive slightly less priority) at timestamp_nanos |
| bundles | number | The number of transactions stored in the "bundles" treap (i.e. the buffer dedicated for bundle transactions) at timestamp_nanos |
TsTileTimers
| Field | Type | Description |
|---|---|---|
| timestamp_nanos | string | A timestamp of when the tile timers were sampled, nanoseconds since the UNIX epoch |
| tile_timers | TileTimer[] | A list of all tile timing information at the given sample timestamp |
WriteAcctCost
| Field | Type | Description |
|---|---|---|
| account | string | The pubkey for this writeable account |
| cost | number | The total compute units for all transactions that list account as a writeable account |
SlotLimits
| Field | Type | Description |
|---|---|---|
| used_total_block_cost | number | The total block cost in compute units |
| used_total_vote_cost | number | Under Tower, the compute units from vote transactions consumed for this block. This fixed-shape field is deprecated and always 0 under Alpenglow |
| used_account_write_costs | WriteAcctCost[] | The top 5 writeable account costs for this block |
| used_total_bytes | number | The number of bytes from transaction payloads and microblock headers consumed in total for this block |
| used_total_microblocks | number | The total number of microblocks included in this block |
| max_total_block_cost | number | The maximum possible value for used_total_block_cost |
| max_total_vote_cost | number | Under Tower, the maximum possible value for used_total_vote_cost. This fixed-shape field is deprecated and always 0 under Alpenglow |
| max_account_write_cost | number | The maximum possible value for used_account_write_cost |
| max_total_bytes | number | The maximum possible value for used_total_bytes |
| max_total_microblocks | number | The maximum possible value for used_total_microblocks |
SlotScheduleStats
| Field | Type | Description |
|---|---|---|
| block_hash | string | The final POH hash in the block as a base58 encoded string |
| end_slot_reason | string | The reason pack ended packing for this leader slot. One of "timeout", "microblock_limit", or "leader_switch" |
| slot_schedule_counts | number[] | slot_schedule_counts[i] is the number of transactions across the leader slot that had ["success", "fail_cu_limit", "fail_fast_path", "fail_byte_limit", "fail_alloc_limit", "fail_write_cost", "fail_slow_path", "fail_defer_skip"][i] as the outcome after being scheduled. "success" means the transaction was successfully scheduled to a bank. "fail_cu_limit" means Pack skipped the transaction because it would have exceeded the block CU limit. "fail_fast_path" means Pack skipped the transaction because of account conflicts using the fast bitvector check. "fail_byte_limit" means Pack skipped the transaction because it would have exceeded the block data size limit. "fail_alloc_limit" means Pack skipped the transaction because it would have exceeded the block account allocation limit. "fail_write_cost" means Pack skipped the transaction because it would have caused a writable account to exceed the per-account block write cost limit. "fail_slow_path" means Pack skipped the transaction because of account conflicts using the full slow check. "fail_defer_skip" means Pack skipped the transaction it previously exceeded the per-account block write cost limit too many times |
| end_slot_schedule_counts | number[] | end_slot_schedule_counts has the same meaning as slot_schedule_counts except only transactions that occur after the last successfully scheduled transaction in the slot are counted |
| pending_smallest_cost | number|null | The cost in compute units of the smallest eligible non-vote transaction under Tower, or any eligible transaction under Alpenglow, in pack's transaction buffer at the end of the slot. If the buffer is empty, this is null |
| pending_smallest_bytes | number|null | The size in bytes of the smallest eligible non-vote transaction under Tower, or any eligible transaction under Alpenglow, in pack's transaction buffer at the end of the slot. If the buffer is empty, this is null |
| pending_vote_smallest_cost | number|null | Under Tower, the cost in compute units of the smallest eligible vote transaction in pack's transaction buffer at the end of the slot, or null if the buffer is empty. This fixed-shape field is deprecated and always null under Alpenglow |
| pending_vote_smallest_bytes | number|null | Under Tower, the size in bytes of the smallest eligible vote transaction in pack's transaction buffer at the end of the slot, or null if the buffer is empty. This fixed-shape field is deprecated and always null under Alpenglow |
Transactions
| Field | Type | Description |
|---|---|---|
| start_timestamp_nanos | string | A UNIX timestamp, in nanoseconds, representing the time that the validator is first aware that it is leader. At this point the poh tile will signal the pack tile to begin filling the block for this slot with transactions |
| target_end_timestamp_nanos | string | A UNIX timestamp, in nanoseconds, representing the target time in nanoseconds that the pack tile should stop scheduling transactions for the slot. Transactions might still finish executing after this end time, if they started executing before it and ran over the deadline. In rare cases, transactions may also appear to begin after this timestamp due to slight clock drift between execution cores |
| txn_arrival_timestamps_nanos | string[] | An array of UNIX timestamps, in nanoseconds. txn_arrival_timestamps_nanos[i] is the time when the i-th transaction in the slot arrived at the transaction scheduler (i.e. pack) |
| txn_mb_start_timestamps_nanos | string[] | An array of UNIX timestamps, in nanoseconds. txn_mb_start_timestamps_nanos[i] is the time when the microblock for the i-th transaction in the slot was successfully scheduled for execution by pack. At this point, the microblock was sent off to a bank tile for execution. Since a microblock may contain multiple transactions (e.g. a bundle), all transactions from the same microblock will share the same start timestamp |
| txn_load_start_timestamps_nanos | string[] | An array of UNIX timestamps, in nanoseconds. txn_load_start_timestamps_nanos[i] is the time when the i-th transaction in the slot started loading relevant on-chain account data |
| txn_check_start_timestamps_nanos | string[] | An array of UNIX timestamps, in nanoseconds. txn_check_start_timestamps_nanos[i] is the time when the i-th transaction in the slot started validation checks, which include a final deduplication check as well as an expiration check. In Firedancer, the load phase occurs before validation checks, but in Frankendancer the check phase occurs before loading |
| txn_execute_start_timestamps_nanos | string[] | An array of UNIX timestamps, in nanoseconds. txn_execute_start_timestamps_nanos[i] is the time when the i-th transaction in the slot started executing. At this point, relevant on-chain data has been loaded for the transaction and it is ready to be fed into the Solana Virtual Machine (SVM) |
| txn_commit_start_timestamps_nanos | string[] | An array of UNIX timestamps, in nanoseconds. txn_commit_start_timestamps_nanos[i] is the time when the i-th transaction in the slot started committing the transaction (or canceling it) |
| txn_commit_end_timestamps_nanos | string[] | An array of UNIX timestamps, in nanoseconds. txn_commit_end_timestamps_nanos[i] is the time when the i-th transaction in the slot finished the commit/cancel phase |
| txn_mb_end_timestamps_nanos | string[] | An array of UNIX timestamps, in nanoseconds. txn_mb_end_timestamps_nanos[i] is the time when the microblock for the i-th transaction in the slot completed executing. At this point, the bank tile for this microblock was ready to communicate the execution result back to the pack. pack uses this result to track the progress of the growing block and also repurposes any unused compute units for other microblocks. The current implementation splits microblocks which originally contained multiple transactions (i.e. bundles) apart so that consumers always receive one transaction per microblock, so unlike txn_mb_start_timestamps_nanos this timestamp may be unique for a given transaction |
| txn_compute_units_requested | number[] | txn_compute_units_requested[i] is a strict upper bound on the total cost for the i-th transaction in the slot. The transaction cannot have succeeded if its incurred cost (known after execution) exceeds this bound. This bound is used by the pack tile to estimate the pace at which the block is being filled, and to filter out transactions that it knows will fail ahead of time |
| txn_compute_units_consumed | number[] | txn_compute_units_consumed[i] is the actual post-execution cost of the i-th transaction in the slot. While some transactions costs are known from the transaction payload itself (such as the cost incurred by the amount of instruction data), other costs (like execution costs or the cost due to loaded on-chain account data) are a function of the state of the blockchain at the time of execution. This value represents the actual cost after a transaction is executed. Consensus requires that all validators agree on this value for a given transaction in a slot. There are two special cases to consider for scheduled transactions that were not added to the produced block. Failed bundle transactions that successfully executed up to the point of failure will show actual consumed CUs. Subsequent failed bundle transactions will show 0 cus consumed. Non-bundle transactions that were not added to the block will also show 0 cus consumed |
| txn_transaction_fee | string[] | txn_transaction_fee[i] is the signature fee for the i-th transaction in the slot. Currently, this is the number of signatures in the transaction times 5000 lamports. This fee used to (and may in the future) include rewards from other parts of the transaction, which is why a more general name is used. 50% of this fee is burned and the other 50% is included in validator block rewards. The provided values reflect the fee balance after burning |
| txn_priority_fee | string[] | txn_priority_fee[i] is the priority fee in lamports for the i-th transaction in the slot. The priority fee is a static metric computed by multiplying the requested execution cost (derived from a provided computeBudget instruction, or from a protocol defined default) by the compute unit price (derived from a separate computeBudget instruction) |
| txn_tips | string[] | txn_tips[i] is the total tip in lamports for the i-th transaction in the slot. The tip is the increase (due to this transaction) in the total balance of all tip payment accounts across all block builders after any commission to the block builder is subtracted. This implies that both the validator and staker portions of the tip are included in this value. Non-bundle transactions may have a non-zero tip. Tips for transactions in failed bundles are included up to the point of failure |
| txn_error_code | number[] | txn_error_code[i] is the error code that explains the failure for the i-th transaction in the slot. See below for more details |
| txn_from_bundle | boolean[] | txn_from_bundle[i] is true if the i-th transaction in the slot came from a bundle and false otherwise. A bundle is a microblock with 1-5 transactions that atomically fail or succeed. It is sent to the validator from a compatible block engine (e.g. jito) that can additionally collect MEV rewards that are distributed to stakers (i.e. tips) |
| txn_is_simple_vote | boolean[] | Tower-only. txn_is_simple_vote[i] is true if the i-th transaction in the slot is a simple vote and false otherwise. This field is omitted under Alpenglow |
| txn_landed | boolean[] | txn_landed[i] is true if the i-th transaction in the slot was included in the produced block. A scheduled transaction may not be included in the block for any number of reasons (e.g. a failed bundle, a duplicate transaction, invalid fee-payer) |
| txn_bank_idx | number[] | txn_bank_idx[i] is the index of the bank tile that executed the i-th transaction in the slot |
| txn_microblock_id | string[] | txn_microblock_id[i] is the index of the microblock for the i-th transaction in the slot. Microblocks are collections of 1+ transactions. All of the transactions from a bundle share the same microblock. Microblock ids are monotonically increasing in the order they appear in the block and start at 0 for each slot |
| txn_signature | string[] | txn_signature[i] is the base58 signature of the i-th transaction in the slot |
| txn_source_ipv4 | string[] | txn_source_ipv4[i] is the source ipv4 address for the i-th transaction in the slot |
| txn_source_tpu | string[] | txn_source_tpu[i] is the transaction processing unit (TPU) which handled the i-th transaction in the slot |
The source tpu for a transaction can be one of the following
| TPU | Description |
|---|---|
| quic | the primary ingress tpu for user transactions. Utilizes the quic protocol to receive packets |
| udp | ingress transactions received as simple UDP packets |
| gossip | Tower-only vote transactions received from the gossip network; this value is not emitted under Alpenglow |
| bundle | bundle transactions received by the bundle tile from a block builder. Utilizes a grpc connection to receive packets |
| send | Tower-only vote transactions produced by this validator and received from the send tile; this value is not emitted under Alpenglow |
These are the possible error codes that might be included in txn_error_code and their meanings.
| Code Name | Code | Description |
|---|---|---|
| Success | 0 | The transaction successfully executed |
| AccountInUse | 1 | Includes a writable account that was already in use at the time this transaction was executed |
| AccountLoadedTwice | 2 | Lists at least one account pubkey more than once |
| AccountNotFound | 3 | Lists at least one account pubkey that was not found in the accounts database |
| ProgramAccountNotFound | 4 | Could not find or parse a listed program account |
| InsufficientFundsForFee | 5 | Lists a fee payer that does not have enough SOL to fund this transaction |
| InvalidAccountForFee | 6 | Lists a fee payer that may not be used to pay transaction fees |
| AlreadyProcessed | 7 | This transaction has been processed before (e.g. the transaction was sent twice) |
| BlockhashNotFound | 8 | Provides a block hash of a recent block in the chain, b, that this validator has not seen yet, or that is so old it has been discarded |
| InstructionError | 9 | Includes an instruction that failed to process |
| CallChainTooDeep | 10 | Includes a cross program invocation (CPI) chain that exceeds the maximum depth allowed |
| MissingSignatureForFee | 11 | Requires a fee but has no signature present |
| InvalidAccountIndex | 12 | Contains an invalid account reference in one of its instructions |
| SignatureFailure | 13 | Includes a signature that did not pass verification |
| InvalidProgramForExecution | 14 | Includes a program that may not be used for executing transactions |
| SanitizeFailure | 15 | Failed to parse a portion of the transaction payload |
| ClusterMaintenance | 16 | Cluster is undergoing an active maintenance window |
| AccountBorrowOutstanding | 17 | Transaction processing left an account with an outstanding borrowed reference |
| WouldExceedMaxBlockCostLimit | 18 | Exceeded the maximum compute unit cost allowed for this slot |
| UnsupportedVersion | 19 | Includes a transaction version that is not supported by this validator |
| InvalidWritableAccount | 20 | Includes an account marked as writable that is not in fact writable |
| WouldExceedMaxAccountCostLimit | 21 | Exceeded the maximum per-account compute unit cost allowed for this slot |
| WouldExceedAccountDataBlockLimit | 22 | Retrieved accounts data size exceeds the limit imposed for this slot |
| TooManyAccountLocks | 23 | Locked too many accounts |
| AddressLookupTableNotFound | 24 | Loads an address table account that doesn't exist |
| InvalidAddressLookupTableOwner | 25 | Loads an address table account with an invalid owner |
| InvalidAddressLookupTableData | 26 | Loads an address table account with invalid data |
| InvalidAddressLookupTableIndex | 27 | Address table lookup uses an invalid index |
| InvalidRentPayingAccount | 28 | Deprecated |
| WouldExceedMaxVoteCostLimit | 29 | Tower-only. Exceeded the maximum vote compute unit cost allowed for this slot; this code is not emitted under Alpenglow |
| WouldExceedAccountDataTotalLimit | 30 | Deprecated |
| DuplicateInstruction | 31 | Contains duplicate instructions |
| InsufficientFundsForRent | 32 | Deprecated |
| MaxLoadedAccountsDataSizeExceeded | 33 | Retrieved accounts data size exceeds the limit imposed for this transaction |
| InvalidLoadedAccountsDataSizeLimit | 34 | Requested an invalid data size (i.e. 0) |
| ResanitizationNeeded | 35 | Sanitized transaction differed before/after feature activation. Needs to be resanitized |
| ProgramExecutionTemporarilyRestricted | 36 | Execution of a program referenced by this transaction is restricted |
| UnbalancedTransaction | 37 | The total accounts balance before the transaction does not equal the total balance after |
| ProgramCacheHitMaxLimit | 38 | The program cache allocated for transaction batch for this transaction hit its load limit |
| CommitCancelled | 39 | This transaction was aborted during the commit stage |
| BundlePeer | 40 | This transaction was part of a bundle that failed |
| BlockhashNonceAlreadyAdvanced | 50 | This transaction references a nonce account that is already advanced |
| BlockhashNonceAdvanceFailed | 51 | This transaction is a nonce transaction but the advance instruction was not valid or failed |
| BlockhashNonceWrong | 52 | This transaction is a nonce transaction but the blockhash is not the correct one |