Skip to main content

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}