agave_geyser_plugin_interface/geyser_plugin_interface.rs
1//! The interface for Geyser plugins. A plugin must implement
2//! the GeyserPlugin trait to work with the runtime.
3//! In addition, the dynamic library must export a "C" function _create_plugin which
4//! creates the implementation of the plugin.
5use {
6 solana_clock::{Slot, UnixTimestamp},
7 solana_hash::Hash,
8 solana_message::v0::LoadedAddresses,
9 solana_signature::Signature,
10 solana_transaction::{sanitized::SanitizedTransaction, versioned::VersionedTransaction},
11 solana_transaction_status::{Reward, RewardsAndNumPartitions, TransactionStatusMeta},
12 std::{any::Any, error, io},
13 thiserror::Error,
14};
15#[derive(Debug, Clone, PartialEq, Eq)]
16#[repr(C)]
17/// Information about an account being updated
18pub struct ReplicaAccountInfo<'a> {
19 /// The Pubkey for the account
20 pub pubkey: &'a [u8],
21
22 /// The lamports for the account
23 pub lamports: u64,
24
25 /// The Pubkey of the owner program account
26 pub owner: &'a [u8],
27
28 /// This account's data contains a loaded program (and is now read-only)
29 pub executable: bool,
30
31 /// The epoch at which this account will next owe rent
32 pub rent_epoch: u64,
33
34 /// The data held in this account.
35 pub data: &'a [u8],
36
37 /// A global monotonically increasing atomic number, which can be used
38 /// to tell the order of the account update. For example, when an
39 /// account is updated in the same slot multiple times, the update
40 /// with higher write_version should supersede the one with lower
41 /// write_version.
42 pub write_version: u64,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46#[repr(C)]
47/// Information about an account being updated
48/// (extended with transaction signature doing this update)
49pub struct ReplicaAccountInfoV2<'a> {
50 /// The Pubkey for the account
51 pub pubkey: &'a [u8],
52
53 /// The lamports for the account
54 pub lamports: u64,
55
56 /// The Pubkey of the owner program account
57 pub owner: &'a [u8],
58
59 /// This account's data contains a loaded program (and is now read-only)
60 pub executable: bool,
61
62 /// The epoch at which this account will next owe rent
63 pub rent_epoch: u64,
64
65 /// The data held in this account.
66 pub data: &'a [u8],
67
68 /// A global monotonically increasing atomic number, which can be used
69 /// to tell the order of the account update. For example, when an
70 /// account is updated in the same slot multiple times, the update
71 /// with higher write_version should supersede the one with lower
72 /// write_version.
73 pub write_version: u64,
74
75 /// First signature of the transaction caused this account modification
76 pub txn_signature: Option<&'a Signature>,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
80#[repr(C)]
81/// Information about an account being updated
82/// (extended with reference to transaction doing this update)
83pub struct ReplicaAccountInfoV3<'a> {
84 /// The Pubkey for the account
85 pub pubkey: &'a [u8],
86
87 /// The lamports for the account
88 pub lamports: u64,
89
90 /// The Pubkey of the owner program account
91 pub owner: &'a [u8],
92
93 /// This account's data contains a loaded program (and is now read-only)
94 pub executable: bool,
95
96 /// The epoch at which this account will next owe rent
97 pub rent_epoch: u64,
98
99 /// The data held in this account.
100 pub data: &'a [u8],
101
102 /// A global monotonically increasing atomic number, which can be used
103 /// to tell the order of the account update. For example, when an
104 /// account is updated in the same slot multiple times, the update
105 /// with higher write_version should supersede the one with lower
106 /// write_version.
107 pub write_version: u64,
108
109 /// Reference to transaction causing this account modification
110 pub txn: Option<&'a SanitizedTransaction>,
111}
112
113/// A wrapper to future-proof ReplicaAccountInfo handling.
114/// If there were a change to the structure of ReplicaAccountInfo,
115/// there would be new enum entry for the newer version, forcing
116/// plugin implementations to handle the change.
117#[repr(u32)]
118pub enum ReplicaAccountInfoVersions<'a> {
119 V0_0_1(&'a ReplicaAccountInfo<'a>),
120 V0_0_2(&'a ReplicaAccountInfoV2<'a>),
121 V0_0_3(&'a ReplicaAccountInfoV3<'a>),
122}
123
124/// Information about a transaction
125#[derive(Clone, Debug)]
126#[repr(C)]
127pub struct ReplicaTransactionInfo<'a> {
128 /// The first signature of the transaction, used for identifying the transaction.
129 pub signature: &'a Signature,
130
131 /// Indicates if the transaction is a simple vote transaction.
132 pub is_vote: bool,
133
134 /// The sanitized transaction.
135 pub transaction: &'a SanitizedTransaction,
136
137 /// Metadata of the transaction status.
138 pub transaction_status_meta: &'a TransactionStatusMeta,
139}
140
141/// Information about a transaction, including index in block
142#[derive(Clone, Debug)]
143#[repr(C)]
144pub struct ReplicaTransactionInfoV2<'a> {
145 /// The first signature of the transaction, used for identifying the transaction.
146 pub signature: &'a Signature,
147
148 /// Indicates if the transaction is a simple vote transaction.
149 pub is_vote: bool,
150
151 /// The sanitized transaction.
152 pub transaction: &'a SanitizedTransaction,
153
154 /// Metadata of the transaction status.
155 pub transaction_status_meta: &'a TransactionStatusMeta,
156
157 /// The transaction's index in the block
158 pub index: usize,
159}
160
161/// Information about a transaction, including index in block
162#[derive(Clone, Debug)]
163#[repr(C)]
164pub struct ReplicaTransactionInfoV3<'a> {
165 /// The transaction signature, used for identifying the transaction.
166 pub signature: &'a Signature,
167
168 /// The transaction message hash, used for identifying the transaction.
169 pub message_hash: &'a Hash,
170
171 /// Indicates if the transaction is a simple vote transaction.
172 pub is_vote: bool,
173
174 /// The versioned transaction.
175 pub transaction: &'a VersionedTransaction,
176
177 /// Metadata of the transaction status.
178 pub transaction_status_meta: &'a TransactionStatusMeta,
179
180 /// The transaction's index in the block
181 pub index: usize,
182}
183
184/// A wrapper to future-proof ReplicaTransactionInfo handling.
185/// If there were a change to the structure of ReplicaTransactionInfo,
186/// there would be new enum entry for the newer version, forcing
187/// plugin implementations to handle the change.
188#[repr(u32)]
189pub enum ReplicaTransactionInfoVersions<'a> {
190 V0_0_1(&'a ReplicaTransactionInfo<'a>),
191 V0_0_2(&'a ReplicaTransactionInfoV2<'a>),
192 V0_0_3(&'a ReplicaTransactionInfoV3<'a>),
193}
194
195/// Information about a transaction after deshredding (when entries are formed from shreds).
196/// This is sent before any execution occurs.
197/// Unlike ReplicaTransactionInfo, this does not include TransactionStatusMeta
198/// since execution has not happened yet.
199#[derive(Clone, Debug)]
200#[repr(C)]
201pub struct ReplicaDeshredTransactionInfo<'a> {
202 /// The transaction signature, used for identifying the transaction.
203 pub signature: &'a Signature,
204
205 /// Indicates if the transaction is a simple vote transaction.
206 pub is_vote: bool,
207
208 /// The versioned transaction.
209 pub transaction: &'a VersionedTransaction,
210
211 /// Addresses loaded from address lookup tables for V0 transactions.
212 /// Resolution uses the rooted bank, so address lookup tables created between
213 /// the root slot and the current slot will not resolve. This field is `None`
214 /// for legacy transactions, when the transaction has no address table lookups,
215 /// when ALT resolution is not enabled by the plugin, or when resolution fails
216 /// (e.g. the lookup table account does not exist at the root slot).
217 pub loaded_addresses: Option<&'a LoadedAddresses>,
218}
219
220/// Extends ReplicaDeshredTransactionInfo with metadata about the completed data set that
221/// produced the transaction.
222///
223/// A completed data set is a contiguous range of data shreds whose combined payload deserializes
224/// to a single `Vec<Entry>`. Multiple transactions can share the same completed-data-set range,
225/// and completed data sets for the same slot may be observed out of order. These fields describe
226/// the data-set container; they are not a block-wide transaction index.
227#[derive(Clone, Debug)]
228#[repr(C)]
229pub struct ReplicaDeshredTransactionInfoV2<'a> {
230 /// The transaction signature, used for identifying the transaction.
231 pub signature: &'a Signature,
232
233 /// Indicates if the transaction is a simple vote transaction.
234 pub is_vote: bool,
235
236 /// The versioned transaction.
237 pub transaction: &'a VersionedTransaction,
238
239 /// Addresses loaded from address lookup tables for V0 transactions.
240 pub loaded_addresses: Option<&'a LoadedAddresses>,
241
242 /// The inclusive starting shred index of the completed data set containing this transaction.
243 pub completed_data_set_starting_shred_index: u32,
244
245 /// The exclusive ending shred index of the completed data set containing this transaction.
246 pub completed_data_set_ending_shred_index_exclusive: u32,
247}
248
249/// A wrapper to future-proof ReplicaDeshredTransactionInfo handling.
250#[repr(u32)]
251pub enum ReplicaDeshredTransactionInfoVersions<'a> {
252 V0_0_1(&'a ReplicaDeshredTransactionInfo<'a>),
253 V0_0_2(&'a ReplicaDeshredTransactionInfoV2<'a>),
254}
255
256#[derive(Clone, Debug)]
257#[repr(C)]
258pub struct ReplicaEntryInfo<'a> {
259 /// The slot number of the block containing this Entry
260 pub slot: Slot,
261 /// The Entry's index in the block
262 pub index: usize,
263 /// The number of hashes since the previous Entry
264 pub num_hashes: u64,
265 /// The Entry's SHA-256 hash, generated from the previous Entry's hash with
266 /// `solana_entry::entry::next_hash()`
267 pub hash: &'a [u8],
268 /// The number of executed transactions in the Entry
269 pub executed_transaction_count: u64,
270}
271
272#[derive(Clone, Debug)]
273#[repr(C)]
274pub struct ReplicaEntryInfoV2<'a> {
275 /// The slot number of the block containing this Entry
276 pub slot: Slot,
277 /// The Entry's index in the block
278 pub index: usize,
279 /// The number of hashes since the previous Entry
280 pub num_hashes: u64,
281 /// The Entry's SHA-256 hash, generated from the previous Entry's hash with
282 /// `solana_entry::entry::next_hash()`
283 pub hash: &'a [u8],
284 /// The number of executed transactions in the Entry
285 pub executed_transaction_count: u64,
286 /// The index-in-block of the first executed transaction in this Entry
287 pub starting_transaction_index: usize,
288}
289
290/// A wrapper to future-proof ReplicaEntryInfo handling. To make a change to the structure of
291/// ReplicaEntryInfo, add an new enum variant wrapping a newer version, which will force plugin
292/// implementations to handle the change.
293#[repr(u32)]
294pub enum ReplicaEntryInfoVersions<'a> {
295 V0_0_1(&'a ReplicaEntryInfo<'a>),
296 V0_0_2(&'a ReplicaEntryInfoV2<'a>),
297}
298
299#[derive(Clone, Debug)]
300#[repr(C)]
301pub struct ReplicaBlockInfo<'a> {
302 pub slot: Slot,
303 pub blockhash: &'a str,
304 pub rewards: &'a [Reward],
305 pub block_time: Option<UnixTimestamp>,
306 pub block_height: Option<u64>,
307}
308
309/// Extending ReplicaBlockInfo by sending the executed_transaction_count.
310#[derive(Clone, Debug)]
311#[repr(C)]
312pub struct ReplicaBlockInfoV2<'a> {
313 pub parent_slot: Slot,
314 pub parent_blockhash: &'a str,
315 pub slot: Slot,
316 pub blockhash: &'a str,
317 pub rewards: &'a [Reward],
318 pub block_time: Option<UnixTimestamp>,
319 pub block_height: Option<u64>,
320 pub executed_transaction_count: u64,
321}
322
323/// Extending ReplicaBlockInfo by sending the entries_count.
324#[derive(Clone, Debug)]
325#[repr(C)]
326pub struct ReplicaBlockInfoV3<'a> {
327 pub parent_slot: Slot,
328 pub parent_blockhash: &'a str,
329 pub slot: Slot,
330 pub blockhash: &'a str,
331 pub rewards: &'a [Reward],
332 pub block_time: Option<UnixTimestamp>,
333 pub block_height: Option<u64>,
334 pub executed_transaction_count: u64,
335 pub entry_count: u64,
336}
337
338/// Extending ReplicaBlockInfo by sending RewardsAndNumPartitions.
339#[derive(Clone, Debug)]
340#[repr(C)]
341pub struct ReplicaBlockInfoV4<'a> {
342 pub parent_slot: Slot,
343 pub parent_blockhash: &'a str,
344 pub slot: Slot,
345 pub blockhash: &'a str,
346 pub rewards: &'a RewardsAndNumPartitions,
347 pub block_time: Option<UnixTimestamp>,
348 pub block_height: Option<u64>,
349 pub executed_transaction_count: u64,
350 pub entry_count: u64,
351}
352
353#[repr(u32)]
354pub enum ReplicaBlockInfoVersions<'a> {
355 V0_0_1(&'a ReplicaBlockInfo<'a>),
356 V0_0_2(&'a ReplicaBlockInfoV2<'a>),
357 V0_0_3(&'a ReplicaBlockInfoV3<'a>),
358 V0_0_4(&'a ReplicaBlockInfoV4<'a>),
359}
360
361/// Errors returned by plugin calls
362#[derive(Error, Debug)]
363#[repr(u32)]
364pub enum GeyserPluginError {
365 /// Error opening the configuration file; for example, when the file
366 /// is not found or when the validator process has no permission to read it.
367 #[error("Error opening config file. Error detail: ({0}).")]
368 ConfigFileOpenError(#[from] io::Error),
369
370 /// Error in reading the content of the config file or the content
371 /// is not in the expected format.
372 #[error("Error reading config file. Error message: ({msg})")]
373 ConfigFileReadError { msg: String },
374
375 /// Error when updating the account.
376 #[error("Error updating account. Error message: ({msg})")]
377 AccountsUpdateError { msg: String },
378
379 /// Error when updating the slot status
380 #[error("Error updating slot status. Error message: ({msg})")]
381 SlotStatusUpdateError { msg: String },
382
383 /// Any custom error defined by the plugin.
384 #[error("Plugin-defined custom error. Error message: ({0})")]
385 Custom(Box<dyn error::Error + Send + Sync>),
386
387 /// Error when updating the transaction.
388 #[error("Error updating transaction. Error message: ({msg})")]
389 TransactionUpdateError { msg: String },
390}
391
392/// The current status of a slot
393#[derive(Debug, Clone, PartialEq, Eq)]
394#[repr(u32)]
395pub enum SlotStatus {
396 /// The highest slot of the heaviest fork processed by the node. Ledger state at this slot is
397 /// not derived from a confirmed or finalized block, but if multiple forks are present, is from
398 /// the fork the validator believes is most likely to finalize.
399 Processed,
400
401 /// The highest slot having reached max vote lockout.
402 Rooted,
403
404 /// The highest slot that has been voted on by supermajority of the cluster, ie. is confirmed.
405 Confirmed,
406
407 /// First Shred Received
408 FirstShredReceived,
409
410 /// All shreds for the slot have been received.
411 Completed,
412
413 /// A new bank fork is created with the slot
414 CreatedBank,
415
416 /// A slot is marked dead
417 Dead(String),
418}
419
420impl SlotStatus {
421 pub fn as_str(&self) -> &'static str {
422 match self {
423 SlotStatus::Confirmed => "confirmed",
424 SlotStatus::Processed => "processed",
425 SlotStatus::Rooted => "rooted",
426 SlotStatus::FirstShredReceived => "first_shred_received",
427 SlotStatus::Completed => "completed",
428 SlotStatus::CreatedBank => "created_bank",
429 SlotStatus::Dead(_error) => "dead",
430 }
431 }
432}
433
434pub type Result<T> = std::result::Result<T, GeyserPluginError>;
435
436/// Defines a Geyser plugin, to stream data from the runtime.
437/// Geyser plugins must describe desired behavior for load and unload,
438/// as well as how they will handle streamed data.
439pub trait GeyserPlugin: Any + Send + Sync + std::fmt::Debug {
440 /// The callback to allow the plugin to setup the logging configuration using the logger
441 /// and log level specified by the validator. Will be called first on load/reload, before any other
442 /// callback, and only called once.
443 /// # Examples
444 ///
445 /// ```
446 /// use agave_geyser_plugin_interface::geyser_plugin_interface::{GeyserPlugin,
447 /// GeyserPluginError, Result};
448 ///
449 /// #[derive(Debug)]
450 /// struct SamplePlugin;
451 /// impl GeyserPlugin for SamplePlugin {
452 /// fn setup_logger(&self, logger: &'static dyn log::Log, level: log::LevelFilter) -> Result<()> {
453 /// log::set_max_level(level);
454 /// if let Err(err) = log::set_logger(logger) {
455 /// return Err(GeyserPluginError::Custom(Box::new(err)));
456 /// }
457 /// Ok(())
458 /// }
459 /// fn name(&self) -> &'static str {
460 /// &"sample"
461 /// }
462 /// }
463 /// ```
464 #[allow(unused_variables)]
465 fn setup_logger(&self, logger: &'static dyn log::Log, level: log::LevelFilter) -> Result<()> {
466 Ok(())
467 }
468
469 fn name(&self) -> &'static str;
470
471 /// The callback called when a plugin is loaded by the system,
472 /// used for doing whatever initialization is required by the plugin.
473 /// The _config_file contains the name of the
474 /// of the config file. The config must be in JSON format and
475 /// include a field "libpath" indicating the full path
476 /// name of the shared library implementing this interface.
477 fn on_load(&mut self, _config_file: &str, _is_reload: bool) -> Result<()> {
478 Ok(())
479 }
480
481 /// The callback called right before a plugin is unloaded by the system
482 /// Used for doing cleanup before unload.
483 fn on_unload(&mut self) {}
484
485 /// Called when an account is updated at a slot.
486 /// When `is_startup` is true, it indicates the account is loaded from
487 /// snapshots when the validator starts up. When `is_startup` is false,
488 /// the account is updated during transaction processing.
489 #[allow(unused_variables)]
490 fn update_account(
491 &self,
492 account: ReplicaAccountInfoVersions,
493 slot: Slot,
494 is_startup: bool,
495 ) -> Result<()> {
496 Ok(())
497 }
498
499 /// Called when all accounts are notified of during startup.
500 fn notify_end_of_startup(&self) -> Result<()> {
501 Ok(())
502 }
503
504 /// Called when a slot status is updated
505 #[allow(unused_variables)]
506 fn update_slot_status(
507 &self,
508 slot: Slot,
509 parent: Option<u64>,
510 status: &SlotStatus,
511 ) -> Result<()> {
512 Ok(())
513 }
514
515 /// Called when a transaction is processed in a slot.
516 #[allow(unused_variables)]
517 fn notify_transaction(
518 &self,
519 transaction: ReplicaTransactionInfoVersions,
520 slot: Slot,
521 ) -> Result<()> {
522 Ok(())
523 }
524
525 /// Called when an entry is executed.
526 #[allow(unused_variables)]
527 fn notify_entry(&self, entry: ReplicaEntryInfoVersions) -> Result<()> {
528 Ok(())
529 }
530
531 /// Called when block's metadata is updated.
532 #[allow(unused_variables)]
533 fn notify_block_metadata(&self, blockinfo: ReplicaBlockInfoVersions) -> Result<()> {
534 Ok(())
535 }
536
537 /// Check if the plugin is interested in account data
538 /// Default is true -- if the plugin is not interested in
539 /// account data, please return false.
540 fn account_data_notifications_enabled(&self) -> bool {
541 true
542 }
543
544 /// Check if the plugin is interested in account data from snapshot
545 /// Default is true -- if the plugin is not interested in
546 /// account data snapshot, please return false because startup would be
547 /// improved significantly.
548 fn account_data_snapshot_notifications_enabled(&self) -> bool {
549 true
550 }
551
552 /// Check if the plugin is interested in transaction data
553 /// Default is false -- if the plugin is interested in
554 /// transaction data, please return true.
555 fn transaction_notifications_enabled(&self) -> bool {
556 false
557 }
558
559 /// Check if the plugin is interested in entry data
560 /// Default is false -- if the plugin is interested in
561 /// entry data, return true.
562 fn entry_notifications_enabled(&self) -> bool {
563 false
564 }
565
566 /// Called when a transaction is deshredded (entries formed from shreds).
567 /// This is triggered before any execution occurs. Unlike notify_transaction,
568 /// this does not include execution metadata (TransactionStatusMeta).
569 #[allow(unused_variables)]
570 fn notify_deshred_transaction(
571 &self,
572 transaction: ReplicaDeshredTransactionInfoVersions,
573 slot: Slot,
574 ) -> Result<()> {
575 Ok(())
576 }
577
578 /// Check if the plugin is interested in deshred transaction data.
579 /// Default is false -- if the plugin is interested in receiving
580 /// transactions when they are deshredded, return true.
581 fn deshred_transaction_notifications_enabled(&self) -> bool {
582 false
583 }
584
585 /// Check if the plugin wants address lookup table (ALT) resolution for
586 /// deshred transactions. Default is false. When true, the validator will
587 /// resolve V0 transaction address lookups using the rooted bank and
588 /// populate `loaded_addresses` in `ReplicaDeshredTransactionInfo`.
589 /// This adds accounts DB I/O on the shred insertion path, so plugins
590 /// that only need the raw transaction should leave this disabled.
591 fn deshred_transaction_alt_resolution_enabled(&self) -> bool {
592 false
593 }
594}