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_sdk::{
7        clock::{Slot, UnixTimestamp},
8        signature::Signature,
9        transaction::SanitizedTransaction,
10    },
11    solana_transaction_status::{Reward, RewardsAndNumPartitions, TransactionStatusMeta},
12    std::{any::Any, error, io},
13    thiserror::Error,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[repr(C)]
18/// Information about an account being updated
19pub struct ReplicaAccountInfo<'a> {
20    /// The Pubkey for the account
21    pub pubkey: &'a [u8],
22
23    /// The lamports for the account
24    pub lamports: u64,
25
26    /// The Pubkey of the owner program account
27    pub owner: &'a [u8],
28
29    /// This account's data contains a loaded program (and is now read-only)
30    pub executable: bool,
31
32    /// The epoch at which this account will next owe rent
33    pub rent_epoch: u64,
34
35    /// The data held in this account.
36    pub data: &'a [u8],
37
38    /// A global monotonically increasing atomic number, which can be used
39    /// to tell the order of the account update. For example, when an
40    /// account is updated in the same slot multiple times, the update
41    /// with higher write_version should supersede the one with lower
42    /// write_version.
43    pub write_version: u64,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47#[repr(C)]
48/// Information about an account being updated
49/// (extended with transaction signature doing this update)
50pub struct ReplicaAccountInfoV2<'a> {
51    /// The Pubkey for the account
52    pub pubkey: &'a [u8],
53
54    /// The lamports for the account
55    pub lamports: u64,
56
57    /// The Pubkey of the owner program account
58    pub owner: &'a [u8],
59
60    /// This account's data contains a loaded program (and is now read-only)
61    pub executable: bool,
62
63    /// The epoch at which this account will next owe rent
64    pub rent_epoch: u64,
65
66    /// The data held in this account.
67    pub data: &'a [u8],
68
69    /// A global monotonically increasing atomic number, which can be used
70    /// to tell the order of the account update. For example, when an
71    /// account is updated in the same slot multiple times, the update
72    /// with higher write_version should supersede the one with lower
73    /// write_version.
74    pub write_version: u64,
75
76    /// First signature of the transaction caused this account modification
77    pub txn_signature: Option<&'a Signature>,
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
81#[repr(C)]
82/// Information about an account being updated
83/// (extended with reference to transaction doing this update)
84pub struct ReplicaAccountInfoV3<'a> {
85    /// The Pubkey for the account
86    pub pubkey: &'a [u8],
87
88    /// The lamports for the account
89    pub lamports: u64,
90
91    /// The Pubkey of the owner program account
92    pub owner: &'a [u8],
93
94    /// This account's data contains a loaded program (and is now read-only)
95    pub executable: bool,
96
97    /// The epoch at which this account will next owe rent
98    pub rent_epoch: u64,
99
100    /// The data held in this account.
101    pub data: &'a [u8],
102
103    /// A global monotonically increasing atomic number, which can be used
104    /// to tell the order of the account update. For example, when an
105    /// account is updated in the same slot multiple times, the update
106    /// with higher write_version should supersede the one with lower
107    /// write_version.
108    pub write_version: u64,
109
110    /// Reference to transaction causing this account modification
111    pub txn: Option<&'a SanitizedTransaction>,
112}
113
114/// A wrapper to future-proof ReplicaAccountInfo handling.
115/// If there were a change to the structure of ReplicaAccountInfo,
116/// there would be new enum entry for the newer version, forcing
117/// plugin implementations to handle the change.
118#[repr(u32)]
119pub enum ReplicaAccountInfoVersions<'a> {
120    V0_0_1(&'a ReplicaAccountInfo<'a>),
121    V0_0_2(&'a ReplicaAccountInfoV2<'a>),
122    V0_0_3(&'a ReplicaAccountInfoV3<'a>),
123}
124
125/// Information about a transaction
126#[derive(Clone, Debug)]
127#[repr(C)]
128pub struct ReplicaTransactionInfo<'a> {
129    /// The first signature of the transaction, used for identifying the transaction.
130    pub signature: &'a Signature,
131
132    /// Indicates if the transaction is a simple vote transaction.
133    pub is_vote: bool,
134
135    /// The sanitized transaction.
136    pub transaction: &'a SanitizedTransaction,
137
138    /// Metadata of the transaction status.
139    pub transaction_status_meta: &'a TransactionStatusMeta,
140}
141
142/// Information about a transaction, including index in block
143#[derive(Clone, Debug)]
144#[repr(C)]
145pub struct ReplicaTransactionInfoV2<'a> {
146    /// The first signature of the transaction, used for identifying the transaction.
147    pub signature: &'a Signature,
148
149    /// Indicates if the transaction is a simple vote transaction.
150    pub is_vote: bool,
151
152    /// The sanitized transaction.
153    pub transaction: &'a SanitizedTransaction,
154
155    /// Metadata of the transaction status.
156    pub transaction_status_meta: &'a TransactionStatusMeta,
157
158    /// The transaction's index in the block
159    pub index: usize,
160}
161
162/// A wrapper to future-proof ReplicaTransactionInfo handling.
163/// If there were a change to the structure of ReplicaTransactionInfo,
164/// there would be new enum entry for the newer version, forcing
165/// plugin implementations to handle the change.
166#[repr(u32)]
167pub enum ReplicaTransactionInfoVersions<'a> {
168    V0_0_1(&'a ReplicaTransactionInfo<'a>),
169    V0_0_2(&'a ReplicaTransactionInfoV2<'a>),
170}
171
172#[derive(Clone, Debug)]
173#[repr(C)]
174pub struct ReplicaEntryInfo<'a> {
175    /// The slot number of the block containing this Entry
176    pub slot: Slot,
177    /// The Entry's index in the block
178    pub index: usize,
179    /// The number of hashes since the previous Entry
180    pub num_hashes: u64,
181    /// The Entry's SHA-256 hash, generated from the previous Entry's hash with
182    /// `solana_entry::entry::next_hash()`
183    pub hash: &'a [u8],
184    /// The number of executed transactions in the Entry
185    pub executed_transaction_count: u64,
186}
187
188#[derive(Clone, Debug)]
189#[repr(C)]
190pub struct ReplicaEntryInfoV2<'a> {
191    /// The slot number of the block containing this Entry
192    pub slot: Slot,
193    /// The Entry's index in the block
194    pub index: usize,
195    /// The number of hashes since the previous Entry
196    pub num_hashes: u64,
197    /// The Entry's SHA-256 hash, generated from the previous Entry's hash with
198    /// `solana_entry::entry::next_hash()`
199    pub hash: &'a [u8],
200    /// The number of executed transactions in the Entry
201    pub executed_transaction_count: u64,
202    /// The index-in-block of the first executed transaction in this Entry
203    pub starting_transaction_index: usize,
204}
205
206/// A wrapper to future-proof ReplicaEntryInfo handling. To make a change to the structure of
207/// ReplicaEntryInfo, add an new enum variant wrapping a newer version, which will force plugin
208/// implementations to handle the change.
209#[repr(u32)]
210pub enum ReplicaEntryInfoVersions<'a> {
211    V0_0_1(&'a ReplicaEntryInfo<'a>),
212    V0_0_2(&'a ReplicaEntryInfoV2<'a>),
213}
214
215#[derive(Clone, Debug)]
216#[repr(C)]
217pub struct ReplicaBlockInfo<'a> {
218    pub slot: Slot,
219    pub blockhash: &'a str,
220    pub rewards: &'a [Reward],
221    pub block_time: Option<UnixTimestamp>,
222    pub block_height: Option<u64>,
223}
224
225/// Extending ReplicaBlockInfo by sending the executed_transaction_count.
226#[derive(Clone, Debug)]
227#[repr(C)]
228pub struct ReplicaBlockInfoV2<'a> {
229    pub parent_slot: Slot,
230    pub parent_blockhash: &'a str,
231    pub slot: Slot,
232    pub blockhash: &'a str,
233    pub rewards: &'a [Reward],
234    pub block_time: Option<UnixTimestamp>,
235    pub block_height: Option<u64>,
236    pub executed_transaction_count: u64,
237}
238
239/// Extending ReplicaBlockInfo by sending the entries_count.
240#[derive(Clone, Debug)]
241#[repr(C)]
242pub struct ReplicaBlockInfoV3<'a> {
243    pub parent_slot: Slot,
244    pub parent_blockhash: &'a str,
245    pub slot: Slot,
246    pub blockhash: &'a str,
247    pub rewards: &'a [Reward],
248    pub block_time: Option<UnixTimestamp>,
249    pub block_height: Option<u64>,
250    pub executed_transaction_count: u64,
251    pub entry_count: u64,
252}
253
254/// Extending ReplicaBlockInfo by sending RewardsAndNumPartitions.
255#[derive(Clone, Debug)]
256#[repr(C)]
257pub struct ReplicaBlockInfoV4<'a> {
258    pub parent_slot: Slot,
259    pub parent_blockhash: &'a str,
260    pub slot: Slot,
261    pub blockhash: &'a str,
262    pub rewards: &'a RewardsAndNumPartitions,
263    pub block_time: Option<UnixTimestamp>,
264    pub block_height: Option<u64>,
265    pub executed_transaction_count: u64,
266    pub entry_count: u64,
267}
268
269#[repr(u32)]
270pub enum ReplicaBlockInfoVersions<'a> {
271    V0_0_1(&'a ReplicaBlockInfo<'a>),
272    V0_0_2(&'a ReplicaBlockInfoV2<'a>),
273    V0_0_3(&'a ReplicaBlockInfoV3<'a>),
274    V0_0_4(&'a ReplicaBlockInfoV4<'a>),
275}
276
277/// Errors returned by plugin calls
278#[derive(Error, Debug)]
279#[repr(u32)]
280pub enum GeyserPluginError {
281    /// Error opening the configuration file; for example, when the file
282    /// is not found or when the validator process has no permission to read it.
283    #[error("Error opening config file. Error detail: ({0}).")]
284    ConfigFileOpenError(#[from] io::Error),
285
286    /// Error in reading the content of the config file or the content
287    /// is not in the expected format.
288    #[error("Error reading config file. Error message: ({msg})")]
289    ConfigFileReadError { msg: String },
290
291    /// Error when updating the account.
292    #[error("Error updating account. Error message: ({msg})")]
293    AccountsUpdateError { msg: String },
294
295    /// Error when updating the slot status
296    #[error("Error updating slot status. Error message: ({msg})")]
297    SlotStatusUpdateError { msg: String },
298
299    /// Any custom error defined by the plugin.
300    #[error("Plugin-defined custom error. Error message: ({0})")]
301    Custom(Box<dyn error::Error + Send + Sync>),
302
303    /// Error when updating the transaction.
304    #[error("Error updating transaction. Error message: ({msg})")]
305    TransactionUpdateError { msg: String },
306}
307
308/// The current status of a slot
309#[derive(Debug, Clone, PartialEq, Eq)]
310#[repr(u32)]
311pub enum SlotStatus {
312    /// The highest slot of the heaviest fork processed by the node. Ledger state at this slot is
313    /// not derived from a confirmed or finalized block, but if multiple forks are present, is from
314    /// the fork the validator believes is most likely to finalize.
315    Processed,
316
317    /// The highest slot having reached max vote lockout.
318    Rooted,
319
320    /// The highest slot that has been voted on by supermajority of the cluster, ie. is confirmed.
321    Confirmed,
322
323    /// First Shred Received
324    FirstShredReceived,
325
326    /// All shreds for the slot have been received.
327    Completed,
328
329    /// A new bank fork is created with the slot
330    CreatedBank,
331
332    /// A slot is marked dead
333    Dead(String),
334}
335
336impl SlotStatus {
337    pub fn as_str(&self) -> &'static str {
338        match self {
339            SlotStatus::Confirmed => "confirmed",
340            SlotStatus::Processed => "processed",
341            SlotStatus::Rooted => "rooted",
342            SlotStatus::FirstShredReceived => "first_shread_received",
343            SlotStatus::Completed => "completed",
344            SlotStatus::CreatedBank => "created_bank",
345            SlotStatus::Dead(_error) => "dead",
346        }
347    }
348}
349
350pub type Result<T> = std::result::Result<T, GeyserPluginError>;
351
352/// Defines a Geyser plugin, to stream data from the runtime.
353/// Geyser plugins must describe desired behavior for load and unload,
354/// as well as how they will handle streamed data.
355pub trait GeyserPlugin: Any + Send + Sync + std::fmt::Debug {
356    /// The callback to allow the plugin to setup the logging configuration using the logger
357    /// and log level specified by the validator. Will be called first on load/reload, before any other
358    /// callback, and only called once.
359    /// # Examples
360    ///
361    /// ```
362    /// use agave_geyser_plugin_interface::geyser_plugin_interface::{GeyserPlugin,
363    /// GeyserPluginError, Result};
364    ///
365    /// #[derive(Debug)]
366    /// struct SamplePlugin;
367    /// impl GeyserPlugin for SamplePlugin {
368    ///     fn setup_logger(&self, logger: &'static dyn log::Log, level: log::LevelFilter) -> Result<()> {
369    ///        log::set_max_level(level);
370    ///        if let Err(err) = log::set_logger(logger) {
371    ///            return Err(GeyserPluginError::Custom(Box::new(err)));
372    ///        }
373    ///        Ok(())
374    ///     }
375    ///     fn name(&self) -> &'static str {
376    ///         &"sample"
377    ///     }
378    /// }
379    /// ```
380    #[allow(unused_variables)]
381    fn setup_logger(&self, logger: &'static dyn log::Log, level: log::LevelFilter) -> Result<()> {
382        Ok(())
383    }
384
385    fn name(&self) -> &'static str;
386
387    /// The callback called when a plugin is loaded by the system,
388    /// used for doing whatever initialization is required by the plugin.
389    /// The _config_file contains the name of the
390    /// of the config file. The config must be in JSON format and
391    /// include a field "libpath" indicating the full path
392    /// name of the shared library implementing this interface.
393    fn on_load(&mut self, _config_file: &str, _is_reload: bool) -> Result<()> {
394        Ok(())
395    }
396
397    /// The callback called right before a plugin is unloaded by the system
398    /// Used for doing cleanup before unload.
399    fn on_unload(&mut self) {}
400
401    /// Called when an account is updated at a slot.
402    /// When `is_startup` is true, it indicates the account is loaded from
403    /// snapshots when the validator starts up. When `is_startup` is false,
404    /// the account is updated during transaction processing.
405    #[allow(unused_variables)]
406    fn update_account(
407        &self,
408        account: ReplicaAccountInfoVersions,
409        slot: Slot,
410        is_startup: bool,
411    ) -> Result<()> {
412        Ok(())
413    }
414
415    /// Called when all accounts are notified of during startup.
416    fn notify_end_of_startup(&self) -> Result<()> {
417        Ok(())
418    }
419
420    /// Called when a slot status is updated
421    #[allow(unused_variables)]
422    fn update_slot_status(
423        &self,
424        slot: Slot,
425        parent: Option<u64>,
426        status: &SlotStatus,
427    ) -> Result<()> {
428        Ok(())
429    }
430
431    /// Called when a transaction is processed in a slot.
432    #[allow(unused_variables)]
433    fn notify_transaction(
434        &self,
435        transaction: ReplicaTransactionInfoVersions,
436        slot: Slot,
437    ) -> Result<()> {
438        Ok(())
439    }
440
441    /// Called when an entry is executed.
442    #[allow(unused_variables)]
443    fn notify_entry(&self, entry: ReplicaEntryInfoVersions) -> Result<()> {
444        Ok(())
445    }
446
447    /// Called when block's metadata is updated.
448    #[allow(unused_variables)]
449    fn notify_block_metadata(&self, blockinfo: ReplicaBlockInfoVersions) -> Result<()> {
450        Ok(())
451    }
452
453    /// Check if the plugin is interested in account data
454    /// Default is true -- if the plugin is not interested in
455    /// account data, please return false.
456    fn account_data_notifications_enabled(&self) -> bool {
457        true
458    }
459
460    /// Check if the plugin is interested in transaction data
461    /// Default is false -- if the plugin is interested in
462    /// transaction data, please return true.
463    fn transaction_notifications_enabled(&self) -> bool {
464        false
465    }
466
467    /// Check if the plugin is interested in entry data
468    /// Default is false -- if the plugin is interested in
469    /// entry data, return true.
470    fn entry_notifications_enabled(&self) -> bool {
471        false
472    }
473}