1use {
20 crate::error::CarbonResult,
21 async_trait::async_trait,
22 chrono::{DateTime, Utc},
23 solana_account::Account,
24 solana_clock::Slot,
25 solana_hash::Hash,
26 solana_pubkey::Pubkey,
27 solana_signature::Signature,
28 solana_transaction::versioned::VersionedTransaction,
29 solana_transaction_status::{Rewards, TransactionStatusMeta},
30 tokio_util::sync::CancellationToken,
31};
32
33#[derive(Debug, Clone)]
37pub struct DatasourceDisconnection {
38 pub source: String,
39 pub disconnect_time: DateTime<Utc>,
40 pub last_slot_before_disconnect: Slot,
41 pub first_slot_after_reconnect: Slot,
42 pub missed_slots: u64,
44}
45
46#[async_trait]
53pub trait Datasource: Send + Sync {
54 async fn consume(
55 &self,
56 id: DatasourceId,
57 sender: tokio::sync::mpsc::Sender<(Update, DatasourceId)>,
58 cancellation_token: CancellationToken,
59 ) -> CarbonResult<()>;
60
61 fn update_types(&self) -> Vec<UpdateType>;
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Hash)]
68pub struct DatasourceId(String);
69
70impl DatasourceId {
71 pub fn new_unique() -> Self {
72 Self(uuid::Uuid::new_v4().to_string())
73 }
74
75 pub fn new_named(name: &str) -> Self {
76 Self(name.to_string())
77 }
78}
79
80#[derive(Debug, Clone)]
82pub enum Update {
83 Account(AccountUpdate),
84 Transaction(Box<TransactionUpdate>),
85 AccountDeletion(AccountDeletion),
86 BlockDetails(BlockDetails),
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
93pub enum UpdateType {
94 AccountUpdate,
95 Transaction,
96 AccountDeletion,
97 BlockDetails,
98}
99
100impl Update {
101 pub fn update_type(&self) -> UpdateType {
102 match self {
103 Update::Account(_) => UpdateType::AccountUpdate,
104 Update::Transaction(_) => UpdateType::Transaction,
105 Update::AccountDeletion(_) => UpdateType::AccountDeletion,
106 Update::BlockDetails(_) => UpdateType::BlockDetails,
107 }
108 }
109}
110
111#[derive(Debug, Clone)]
113pub struct AccountUpdate {
114 pub pubkey: Pubkey,
115 pub account: Account,
116 pub slot: u64,
117 pub transaction_signature: Option<Signature>,
118}
119
120impl AccountUpdate {
121 pub fn into_update(self) -> Update {
123 let Self {
124 pubkey,
125 account,
126 slot,
127 transaction_signature,
128 } = self;
129
130 if account.lamports == 0 {
131 Update::AccountDeletion(AccountDeletion {
132 pubkey,
133 account,
134 slot,
135 transaction_signature,
136 })
137 } else {
138 Update::Account(Self {
139 pubkey,
140 account,
141 slot,
142 transaction_signature,
143 })
144 }
145 }
146}
147
148#[derive(Debug, Clone)]
150pub struct TransactionUpdate {
151 pub signature: Signature,
152 pub transaction: VersionedTransaction,
153 pub meta: TransactionStatusMeta,
154 pub is_vote: bool,
155 pub slot: u64,
156 pub index: Option<u64>,
157 pub block_time: Option<i64>,
158 pub block_hash: Option<Hash>,
159}
160
161#[derive(Debug, Clone)]
163pub struct AccountDeletion {
164 pub pubkey: Pubkey,
165 pub account: Account,
166 pub slot: u64,
167 pub transaction_signature: Option<Signature>,
168}
169
170#[derive(Debug, Clone)]
172pub struct BlockDetails {
173 pub slot: u64,
174 pub block_hash: Option<Hash>,
175 pub previous_block_hash: Option<Hash>,
176 pub rewards: Option<Rewards>,
177 pub num_reward_partitions: Option<u64>,
178 pub block_time: Option<i64>,
179 pub block_height: Option<u64>,
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn converts_zero_lamport_updates_to_deletions() {
188 let pubkey = Pubkey::new_unique();
189 let owner = Pubkey::new_unique();
190 let transaction_signature = Some(Signature::new_unique());
191 let account = Account {
192 lamports: 0,
193 data: vec![1, 2, 3],
194 owner,
195 executable: false,
196 rent_epoch: 42,
197 };
198
199 let update = AccountUpdate {
200 pubkey,
201 account,
202 slot: 7,
203 transaction_signature,
204 }
205 .into_update();
206
207 let Update::AccountDeletion(deletion) = update else {
208 panic!("expected account deletion");
209 };
210 assert_eq!(deletion.pubkey, pubkey);
211 assert_eq!(deletion.account.owner, owner);
212 assert_eq!(deletion.account.data, vec![1, 2, 3]);
213 assert_eq!(deletion.slot, 7);
214 assert_eq!(deletion.transaction_signature, transaction_signature);
215 }
216
217 #[test]
218 fn keeps_funded_account_updates() {
219 let update = AccountUpdate {
220 pubkey: Pubkey::new_unique(),
221 account: Account {
222 lamports: 1,
223 ..Account::default()
224 },
225 slot: 7,
226 transaction_signature: None,
227 }
228 .into_update();
229
230 assert!(matches!(update, Update::Account(_)));
231 }
232}