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}