1use std::sync::Arc;
6
7use diesel::prelude::*;
8use iota_json_rpc_types::{
9 BalanceChange, IotaEvent, IotaExecutionStatus, IotaTransactionBlock,
10 IotaTransactionBlockEffects, IotaTransactionBlockEffectsAPI, IotaTransactionBlockEvents,
11 IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, ObjectChange,
12};
13use iota_package_resolver::{PackageStore, Resolver};
14use iota_sdk_types::{
15 Event, SenderSignedTransaction, TransactionDigest, TransactionEffects, TransactionEvents,
16 TypeTag,
17};
18use move_core_types::annotated_value::{MoveDatatypeLayout, MoveTypeLayout};
19#[cfg(feature = "shared_test_runtime")]
20use serde::Deserialize;
21
22use crate::{
23 errors::IndexerError,
24 schema::{optimistic_transactions, transactions, tx_global_order},
25 types::{IndexedBalanceChange, IndexedObjectChange, IndexedTransaction, IndexerResult},
26};
27
28#[derive(Clone, Debug, Queryable, Insertable, QueryableByName, Selectable)]
29#[diesel(table_name = tx_global_order)]
30pub struct TxGlobalOrder {
31 pub tx_sequence_number: Option<i64>,
34 pub tx_digest: Vec<u8>,
35 #[diesel(deserialize_as = i64)]
42 pub optimistic_sequence_number: Option<i64>,
43}
44
45pub const CHECKPOINT_TX_OPTIMISTIC_SEQ: i64 = -1;
49
50impl From<&IndexedTransaction> for TxGlobalOrder {
51 fn from(tx: &IndexedTransaction) -> Self {
52 Self {
53 tx_sequence_number: Some(tx.tx_sequence_number as i64),
54 tx_digest: tx.tx_digest.into_bytes().to_vec(),
55 optimistic_sequence_number: Some(CHECKPOINT_TX_OPTIMISTIC_SEQ),
56 }
57 }
58}
59
60#[derive(Clone, Debug, Queryable, Insertable, QueryableByName, Selectable)]
61#[diesel(table_name = transactions)]
62#[cfg_attr(feature = "shared_test_runtime", derive(Deserialize))]
63pub struct StoredTransaction {
64 pub tx_sequence_number: i64,
67 pub transaction_digest: Vec<u8>,
68 pub raw_transaction: Vec<u8>,
69 pub raw_effects: Vec<u8>,
70 pub checkpoint_sequence_number: i64,
71 pub timestamp_ms: i64,
72 pub object_changes: Vec<Option<Vec<u8>>>,
73 pub balance_changes: Vec<Option<Vec<u8>>>,
74 pub events: Vec<Option<Vec<u8>>>,
75 pub transaction_kind: i16,
76 pub success_command_count: i16,
77}
78
79#[derive(Clone, Debug, Queryable, Insertable, QueryableByName, Selectable)]
80#[diesel(table_name = optimistic_transactions)]
81pub struct OptimisticTransaction {
82 pub optimistic_sequence_number: i64,
83 pub transaction_digest: Vec<u8>,
84 pub raw_transaction: Vec<u8>,
85 pub raw_effects: Vec<u8>,
86 pub object_changes: Vec<Option<Vec<u8>>>,
87 pub balance_changes: Vec<Option<Vec<u8>>>,
88 pub events: Vec<Option<Vec<u8>>>,
89 pub transaction_kind: i16,
90 pub success_command_count: i16,
91}
92
93impl From<OptimisticTransaction> for StoredTransaction {
94 fn from(tx: OptimisticTransaction) -> Self {
95 StoredTransaction {
96 tx_sequence_number: tx.optimistic_sequence_number,
97 transaction_digest: tx.transaction_digest,
98 raw_transaction: tx.raw_transaction,
99 raw_effects: tx.raw_effects,
100 checkpoint_sequence_number: -1,
101 timestamp_ms: -1,
102 object_changes: tx.object_changes,
103 balance_changes: tx.balance_changes,
104 events: tx.events,
105 transaction_kind: tx.transaction_kind,
106 success_command_count: tx.success_command_count,
107 }
108 }
109}
110
111impl OptimisticTransaction {
112 pub fn from_stored(stored: StoredTransaction) -> Self {
113 OptimisticTransaction {
114 optimistic_sequence_number: stored.tx_sequence_number,
115 transaction_digest: stored.transaction_digest,
116 raw_transaction: stored.raw_transaction,
117 raw_effects: stored.raw_effects,
118 object_changes: stored.object_changes,
119 balance_changes: stored.balance_changes,
120 events: stored.events,
121 transaction_kind: stored.transaction_kind,
122 success_command_count: stored.success_command_count,
123 }
124 }
125
126 pub fn get_balance_len(&self) -> usize {
127 self.balance_changes.len()
128 }
129
130 pub fn get_balance_at_idx(&self, idx: usize) -> Option<Vec<u8>> {
131 self.balance_changes.get(idx).cloned().flatten()
132 }
133
134 pub fn get_object_len(&self) -> usize {
135 self.object_changes.len()
136 }
137
138 pub fn get_object_at_idx(&self, idx: usize) -> Option<Vec<u8>> {
139 self.object_changes.get(idx).cloned().flatten()
140 }
141
142 pub fn get_event_len(&self) -> usize {
143 self.events.len()
144 }
145
146 pub fn get_event_at_idx(&self, idx: usize) -> Option<Vec<u8>> {
147 self.events.get(idx).cloned().flatten()
148 }
149}
150
151pub type StoredTransactionEvents = Vec<Option<Vec<u8>>>;
152
153#[derive(Debug, Queryable)]
154pub struct TxSeq {
155 pub seq: i64,
156}
157
158impl Default for TxSeq {
159 fn default() -> Self {
160 Self { seq: -1 }
161 }
162}
163
164#[derive(Clone, Debug, Queryable)]
165pub struct StoredTransactionTimestamp {
166 pub tx_sequence_number: i64,
167 pub timestamp_ms: i64,
168}
169
170#[derive(Clone, Debug, Queryable)]
171pub struct StoredTransactionCheckpoint {
172 pub tx_sequence_number: i64,
173 pub checkpoint_sequence_number: i64,
174}
175
176#[derive(Clone, Debug, Queryable)]
177pub struct StoredTransactionSuccessCommandCount {
178 pub tx_sequence_number: i64,
179 pub checkpoint_sequence_number: i64,
180 pub success_command_count: i16,
181 pub timestamp_ms: i64,
182}
183
184impl From<&IndexedTransaction> for StoredTransaction {
185 fn from(tx: &IndexedTransaction) -> Self {
186 StoredTransaction {
187 tx_sequence_number: tx.tx_sequence_number as i64,
188 transaction_digest: tx.tx_digest.into_bytes().to_vec(),
189 raw_transaction: bcs::to_bytes(&tx.sender_signed_data).unwrap(),
190 raw_effects: bcs::to_bytes(&tx.effects).unwrap(),
191 checkpoint_sequence_number: tx.checkpoint_sequence_number as i64,
192 object_changes: tx
193 .object_changes
194 .iter()
195 .map(|oc| Some(bcs::to_bytes(&oc).unwrap()))
196 .collect(),
197 balance_changes: tx
198 .balance_change
199 .iter()
200 .map(|bc| Some(bcs::to_bytes(&bc).unwrap()))
201 .collect(),
202 events: tx
203 .events
204 .iter()
205 .map(|e| Some(bcs::to_bytes(&e).unwrap()))
206 .collect(),
207 timestamp_ms: tx.timestamp_ms as i64,
208 transaction_kind: tx.transaction_kind as i16,
209 success_command_count: tx.successful_tx_num as i16,
210 }
211 }
212}
213
214impl StoredTransaction {
215 pub fn get_balance_len(&self) -> usize {
216 self.balance_changes.len()
217 }
218
219 pub fn get_balance_at_idx(&self, idx: usize) -> Option<Vec<u8>> {
220 self.balance_changes.get(idx).cloned().flatten()
221 }
222
223 pub fn get_object_len(&self) -> usize {
224 self.object_changes.len()
225 }
226
227 pub fn get_object_at_idx(&self, idx: usize) -> Option<Vec<u8>> {
228 self.object_changes.get(idx).cloned().flatten()
229 }
230
231 pub fn get_event_len(&self) -> usize {
232 self.events.len()
233 }
234
235 pub fn get_event_at_idx(&self, idx: usize) -> Option<Vec<u8>> {
236 self.events.get(idx).cloned().flatten()
237 }
238
239 pub fn is_checkpointed_transaction(&self) -> bool {
242 self.checkpoint_sequence_number >= 0
243 }
244
245 pub async fn try_into_iota_transaction_block_response(
246 self,
247 options: IotaTransactionBlockResponseOptions,
248 package_resolver: &Arc<Resolver<impl PackageStore>>,
249 ) -> IndexerResult<IotaTransactionBlockResponse> {
250 let options = options.clone();
251 let tx_digest =
252 TransactionDigest::from_bytes(self.transaction_digest.as_slice()).map_err(|e| {
253 IndexerError::PersistentStorageDataCorruption(format!(
254 "Can't convert {:?} as tx_digest. Error: {e}",
255 self.transaction_digest
256 ))
257 })?;
258
259 let timestamp_ms = self
260 .is_checkpointed_transaction()
261 .then_some(self.timestamp_ms as u64);
262 let checkpoint = self
263 .is_checkpointed_transaction()
264 .then_some(self.checkpoint_sequence_number as u64);
265
266 let transaction = if options.show_input {
267 let sender_signed_tx = self.try_into_sender_signed_transaction()?;
268 let tx_block = IotaTransactionBlock::try_from_with_package_resolver(
269 sender_signed_tx,
270 package_resolver,
271 tx_digest,
272 )
273 .await?;
274 Some(tx_block)
275 } else {
276 None
277 };
278
279 let effects = if options.show_effects {
280 Some(
281 self.try_into_iota_transaction_effects(package_resolver)
282 .await?,
283 )
284 } else {
285 None
286 };
287
288 let raw_transaction = if options.show_raw_input {
289 self.raw_transaction
290 } else {
291 Default::default()
292 };
293
294 let events = if options.show_events {
295 let events = {
296 self
297 .events
298 .into_iter()
299 .map(|event| match event {
300 Some(event) => {
301 let event: Event = bcs::from_bytes(&event).map_err(|e| {
302 IndexerError::PersistentStorageDataCorruption(format!(
303 "Can't convert event bytes into Event. tx_digest={tx_digest} Error: {e}"
304 ))
305 })?;
306 Ok(event)
307 }
308 None => Err(IndexerError::PersistentStorageDataCorruption(format!(
309 "Event should not be null, tx_digest={tx_digest}"
310 ))),
311 })
312 .collect::<Result<Vec<Event>, IndexerError>>()?
313 };
314 let tx_events = TransactionEvents(events);
315
316 Some(
317 tx_events_to_iota_tx_events(tx_events, package_resolver, tx_digest, timestamp_ms)
318 .await?,
319 )
320 } else {
321 None
322 };
323
324 let object_changes = if options.show_object_changes {
325 let object_changes = {
326 self.object_changes.into_iter().map(|object_change| {
327 match object_change {
328 Some(object_change) => {
329 let object_change: IndexedObjectChange = bcs::from_bytes(&object_change)
330 .map_err(|e| IndexerError::PersistentStorageDataCorruption(
331 format!("Can't convert object_change bytes into IndexedObjectChange. tx_digest={tx_digest} Error: {e}")
332 ))?;
333 Ok(ObjectChange::from(object_change))
334 }
335 None => Err(IndexerError::PersistentStorageDataCorruption(format!("object_change should not be null, tx_digest={tx_digest}"))),
336 }
337 }).collect::<Result<Vec<ObjectChange>, IndexerError>>()?
338 };
339 Some(object_changes)
340 } else {
341 None
342 };
343
344 let balance_changes = if options.show_balance_changes {
345 let balance_changes = {
346 self.balance_changes.into_iter().map(|balance_change| {
347 match balance_change {
348 Some(balance_change) => {
349 let balance_change: IndexedBalanceChange = bcs::from_bytes(&balance_change)
350 .map_err(|e| IndexerError::PersistentStorageDataCorruption(
351 format!("Can't convert balance_change bytes into IndexedBalanceChange. tx_digest={tx_digest} Error: {e}")
352 ))?;
353 Ok(BalanceChange::from(balance_change))
354 }
355 None => Err(IndexerError::PersistentStorageDataCorruption(format!("balance_change should not be null, tx_digest={tx_digest}"))),
356 }
357 }).collect::<Result<Vec<BalanceChange>, IndexerError>>()?
358 };
359 Some(balance_changes)
360 } else {
361 None
362 };
363
364 let raw_effects = if options.show_raw_effects {
365 self.raw_effects
366 } else {
367 Default::default()
368 };
369
370 let errors = match effects.as_ref().map(|e| e.status()) {
371 Some(IotaExecutionStatus::Failure { error }) => vec![error.clone()],
372 _ => vec![],
373 };
374
375 Ok(IotaTransactionBlockResponse {
376 digest: tx_digest,
377 transaction,
378 raw_transaction,
379 effects,
380 events,
381 object_changes,
382 balance_changes,
383 timestamp_ms,
384 checkpoint,
385 confirmed_local_execution: None,
386 errors,
387 raw_effects,
388 })
389 }
390
391 pub fn try_into_sender_signed_transaction(&self) -> IndexerResult<SenderSignedTransaction> {
392 let sender_signed_tx: SenderSignedTransaction = bcs::from_bytes(&self.raw_transaction)
393 .map_err(|e| {
394 IndexerError::PersistentStorageDataCorruption(format!(
395 "Can't convert raw_transaction of {} into SenderSignedTransaction. Error: {e}",
396 self.tx_sequence_number
397 ))
398 })?;
399 Ok(sender_signed_tx)
400 }
401
402 pub async fn try_into_iota_transaction_effects(
403 &self,
404 package_resolver: &Arc<Resolver<impl PackageStore>>,
405 ) -> IndexerResult<IotaTransactionBlockEffects> {
406 let effects: TransactionEffects = bcs::from_bytes(&self.raw_effects).map_err(|e| {
407 IndexerError::PersistentStorageDataCorruption(format!(
408 "Can't convert raw_effects of {} into TransactionEffects. Error: {e}",
409 self.tx_sequence_number
410 ))
411 })?;
412 let effects =
413 IotaTransactionBlockEffects::from_native_with_clever_error(effects, package_resolver)
414 .await;
415 Ok(effects)
416 }
417
418 pub fn is_genesis(&self) -> bool {
420 self.tx_sequence_number == 0
421 }
422}
423
424pub fn stored_events_to_events(
425 stored_events: StoredTransactionEvents,
426) -> Result<Vec<Event>, IndexerError> {
427 stored_events
428 .into_iter()
429 .map(|event| match event {
430 Some(event) => {
431 let event: Event = bcs::from_bytes(&event).map_err(|e| {
432 IndexerError::PersistentStorageDataCorruption(format!(
433 "Can't convert event bytes into Event. Error: {e}",
434 ))
435 })?;
436 Ok(event)
437 }
438 None => Err(IndexerError::PersistentStorageDataCorruption(
439 "Event should not be null".to_string(),
440 )),
441 })
442 .collect::<Result<Vec<Event>, IndexerError>>()
443}
444
445pub async fn tx_events_to_iota_tx_events(
446 mut tx_events: TransactionEvents,
447 package_resolver: &Arc<Resolver<impl PackageStore>>,
448 tx_digest: TransactionDigest,
449 timestamp: Option<u64>,
450) -> Result<IotaTransactionBlockEvents, IndexerError> {
451 let mut iota_event_futures = vec![];
452
453 for tx_event in tx_events.iter() {
454 let package_resolver_clone = package_resolver.clone();
455 let event_type = tx_event.struct_tag.clone();
456 iota_event_futures.push(tokio::task::spawn(async move {
457 let resolver = package_resolver_clone;
458 resolver
459 .type_layout(TypeTag::Struct(Box::new(event_type)))
460 .await
461 }));
462 }
463 let event_move_type_layouts = futures::future::join_all(iota_event_futures)
464 .await
465 .into_iter()
466 .collect::<Result<Vec<_>, _>>()?
467 .into_iter()
468 .collect::<Result<Vec<_>, _>>()
469 .map_err(|e| {
470 IndexerError::ResolveMoveStruct(format!(
471 "Failed to convert to iota event with Error: {e}",
472 ))
473 })?;
474 let event_move_datatype_layouts = event_move_type_layouts
475 .into_iter()
476 .filter_map(|move_type_layout| match move_type_layout {
477 MoveTypeLayout::Struct(s) => Some(MoveDatatypeLayout::Struct(s)),
478 MoveTypeLayout::Enum(e) => Some(MoveDatatypeLayout::Enum(e)),
479 _ => None,
480 })
481 .collect::<Vec<_>>();
482 assert!(tx_events.len() == event_move_datatype_layouts.len());
483 let iota_events = tx_events
484 .drain(..)
485 .enumerate()
486 .zip(event_move_datatype_layouts)
487 .map(|((seq, tx_event), move_datatype_layout)| {
488 IotaEvent::try_from(
489 tx_event,
490 tx_digest,
491 seq as u64,
492 timestamp,
493 move_datatype_layout,
494 )
495 })
496 .collect::<Result<Vec<_>, _>>()?;
497 let iota_tx_events = IotaTransactionBlockEvents { data: iota_events };
498 Ok(iota_tx_events)
499}