1use move_core_types::language_storage::StructTag;
5use rand::Rng;
6use serde::{Deserialize, Serialize};
7use serde_with::serde_as;
8use sui_json_rpc_types::{
9 ObjectChange, SuiTransactionBlockResponse, SuiTransactionBlockResponseOptions,
10};
11use sui_types::base_types::{ObjectDigest, SequenceNumber};
12use sui_types::base_types::{ObjectID, SuiAddress};
13use sui_types::crypto::AggregateAuthoritySignature;
14use sui_types::digests::TransactionDigest;
15use sui_types::dynamic_field::DynamicFieldType;
16use sui_types::effects::TransactionEffects;
17use sui_types::messages_checkpoint::{
18 CertifiedCheckpointSummary, CheckpointCommitment, CheckpointContents, CheckpointDigest,
19 CheckpointSequenceNumber, EndOfEpochData,
20};
21use sui_types::move_package::MovePackage;
22use sui_types::object::{Object, Owner};
23use sui_types::sui_serde::SuiStructTag;
24use sui_types::transaction::SenderSignedData;
25
26use crate::errors::IndexerError;
27
28pub type IndexerResult<T> = Result<T, IndexerError>;
29
30#[derive(Debug, Default)]
31pub struct IndexedCheckpoint {
32 pub sequence_number: u64,
34 pub checkpoint_digest: CheckpointDigest,
35 pub epoch: u64,
36 pub tx_digests: Vec<TransactionDigest>,
37 pub network_total_transactions: u64,
38 pub previous_checkpoint_digest: Option<CheckpointDigest>,
39 pub timestamp_ms: u64,
40 pub total_gas_cost: i64, pub computation_cost: u64,
42 pub storage_cost: u64,
43 pub storage_rebate: u64,
44 pub non_refundable_storage_fee: u64,
45 pub checkpoint_commitments: Vec<CheckpointCommitment>,
46 pub validator_signature: AggregateAuthoritySignature,
47 pub successful_tx_num: usize,
48 pub end_of_epoch_data: Option<EndOfEpochData>,
49 pub end_of_epoch: bool,
50 pub min_tx_sequence_number: u64,
51 pub max_tx_sequence_number: u64,
52 pub certified_checkpoint: Option<CertifiedCheckpointSummary>,
54 pub checkpoint_contents: Option<CheckpointContents>,
55}
56
57impl IndexedCheckpoint {
58 pub fn from_sui_checkpoint(
59 checkpoint: &CertifiedCheckpointSummary,
60 contents: &CheckpointContents,
61 successful_tx_num: usize,
62 ) -> Self {
63 let total_gas_cost = checkpoint.epoch_rolling_gas_cost_summary.computation_cost as i64
64 + checkpoint.epoch_rolling_gas_cost_summary.storage_cost as i64
65 - checkpoint.epoch_rolling_gas_cost_summary.storage_rebate as i64;
66 let tx_digests = contents.iter().map(|t| t.transaction).collect::<Vec<_>>();
67 let max_tx_sequence_number = checkpoint.network_total_transactions - 1;
68 let min_tx_sequence_number = max_tx_sequence_number + 1u64 - tx_digests.len() as u64;
70 let auth_sig = &checkpoint.auth_sig().signature;
71 Self {
72 sequence_number: checkpoint.sequence_number,
73 checkpoint_digest: *checkpoint.digest(),
74 epoch: checkpoint.epoch,
75 tx_digests,
76 previous_checkpoint_digest: checkpoint.previous_digest,
77 end_of_epoch_data: checkpoint.end_of_epoch_data.clone(),
78 end_of_epoch: checkpoint.end_of_epoch_data.clone().is_some(),
79 total_gas_cost,
80 computation_cost: checkpoint.epoch_rolling_gas_cost_summary.computation_cost,
81 storage_cost: checkpoint.epoch_rolling_gas_cost_summary.storage_cost,
82 storage_rebate: checkpoint.epoch_rolling_gas_cost_summary.storage_rebate,
83 non_refundable_storage_fee: checkpoint
84 .epoch_rolling_gas_cost_summary
85 .non_refundable_storage_fee,
86 successful_tx_num,
87 network_total_transactions: checkpoint.network_total_transactions,
88 timestamp_ms: checkpoint.timestamp_ms,
89 validator_signature: auth_sig.clone(),
90 checkpoint_commitments: checkpoint.checkpoint_commitments.clone(),
91 min_tx_sequence_number,
92 max_tx_sequence_number,
93 certified_checkpoint: Some(checkpoint.clone()),
94 checkpoint_contents: Some(contents.clone()),
95 }
96 }
97}
98
99#[derive(Debug, Clone)]
100pub struct IndexedEvent {
101 pub tx_sequence_number: u64,
102 pub event_sequence_number: u64,
103 pub checkpoint_sequence_number: u64,
104 pub transaction_digest: TransactionDigest,
105 pub sender: SuiAddress,
106 pub package: ObjectID,
107 pub module: String,
108 pub event_type: String,
109 pub event_type_package: ObjectID,
110 pub event_type_module: String,
111 pub event_type_name: String,
113 pub bcs: Vec<u8>,
114 pub timestamp_ms: u64,
115}
116
117impl IndexedEvent {
118 pub fn from_event(
119 tx_sequence_number: u64,
120 event_sequence_number: u64,
121 checkpoint_sequence_number: u64,
122 transaction_digest: TransactionDigest,
123 event: &sui_types::event::Event,
124 timestamp_ms: u64,
125 ) -> Self {
126 Self {
127 tx_sequence_number,
128 event_sequence_number,
129 checkpoint_sequence_number,
130 transaction_digest,
131 sender: event.sender,
132 package: event.package_id,
133 module: event.transaction_module.to_string(),
134 event_type: event.type_.to_canonical_string(true),
135 event_type_package: event.type_.address.into(),
136 event_type_module: event.type_.module.to_string(),
137 event_type_name: event.type_.name.to_string(),
138 bcs: event.contents.clone(),
139 timestamp_ms,
140 }
141 }
142}
143
144#[derive(Debug, Clone)]
145pub struct EventIndex {
146 pub tx_sequence_number: u64,
147 pub event_sequence_number: u64,
148 pub sender: SuiAddress,
149 pub emit_package: ObjectID,
150 pub emit_module: String,
151 pub type_package: ObjectID,
152 pub type_module: String,
153 pub type_name: String,
155 pub type_instantiation: String,
157}
158
159impl EventIndex {
161 pub fn random() -> Self {
162 let mut rng = rand::thread_rng();
163 EventIndex {
164 tx_sequence_number: rng.r#gen(),
165 event_sequence_number: rng.r#gen(),
166 sender: SuiAddress::random_for_testing_only(),
167 emit_package: ObjectID::random(),
168 emit_module: rng.r#gen::<u64>().to_string(),
169 type_package: ObjectID::random(),
170 type_module: rng.r#gen::<u64>().to_string(),
171 type_name: rng.r#gen::<u64>().to_string(),
172 type_instantiation: rng.r#gen::<u64>().to_string(),
173 }
174 }
175}
176
177impl EventIndex {
178 pub fn from_event(
179 tx_sequence_number: u64,
180 event_sequence_number: u64,
181 event: &sui_types::event::Event,
182 ) -> Self {
183 let type_instantiation = event
184 .type_
185 .to_canonical_string(true)
186 .splitn(3, "::")
187 .collect::<Vec<_>>()[2]
188 .to_string();
189 Self {
190 tx_sequence_number,
191 event_sequence_number,
192 sender: event.sender,
193 emit_package: event.package_id,
194 emit_module: event.transaction_module.to_string(),
195 type_package: event.type_.address.into(),
196 type_module: event.type_.module.to_string(),
197 type_name: event.type_.name.to_string(),
198 type_instantiation,
199 }
200 }
201}
202
203#[derive(Debug, Copy, Clone)]
204pub enum OwnerType {
205 Immutable = 0,
206 Address = 1,
207 Object = 2,
208 Shared = 3,
209}
210
211pub enum ObjectStatus {
212 Active = 0,
213 WrappedOrDeleted = 1,
214}
215
216impl TryFrom<i16> for ObjectStatus {
217 type Error = IndexerError;
218
219 fn try_from(value: i16) -> Result<Self, Self::Error> {
220 Ok(match value {
221 0 => ObjectStatus::Active,
222 1 => ObjectStatus::WrappedOrDeleted,
223 value => {
224 return Err(IndexerError::PersistentStorageDataCorruptionError(format!(
225 "{value} as ObjectStatus"
226 )));
227 }
228 })
229 }
230}
231
232impl TryFrom<i16> for OwnerType {
233 type Error = IndexerError;
234
235 fn try_from(value: i16) -> Result<Self, Self::Error> {
236 Ok(match value {
237 0 => OwnerType::Immutable,
238 1 => OwnerType::Address,
239 2 => OwnerType::Object,
240 3 => OwnerType::Shared,
241 value => {
242 return Err(IndexerError::PersistentStorageDataCorruptionError(format!(
243 "{value} as OwnerType"
244 )));
245 }
246 })
247 }
248}
249
250pub fn owner_to_owner_info(owner: &Owner) -> (OwnerType, Option<SuiAddress>) {
252 match owner {
253 Owner::AddressOwner(address) => (OwnerType::Address, Some(*address)),
254 Owner::ObjectOwner(address) => (OwnerType::Object, Some(*address)),
255 Owner::Shared { .. } => (OwnerType::Shared, None),
256 Owner::Immutable => (OwnerType::Immutable, None),
257 Owner::ConsensusAddressOwner { owner, .. } => (OwnerType::Address, Some(*owner)),
258 }
259}
260
261#[derive(Debug, Copy, Clone)]
262pub enum DynamicFieldKind {
263 DynamicField = 0,
264 DynamicObject = 1,
265}
266
267#[derive(Clone, Debug)]
268pub struct IndexedObject {
269 pub checkpoint_sequence_number: CheckpointSequenceNumber,
270 pub object: Object,
271 pub df_kind: Option<DynamicFieldType>,
272}
273
274impl IndexedObject {
275 pub fn random() -> Self {
276 let mut rng = rand::thread_rng();
277 let random_address = SuiAddress::random_for_testing_only();
278 IndexedObject {
279 checkpoint_sequence_number: rng.r#gen(),
280 object: Object::with_owner_for_testing(random_address),
281 df_kind: {
282 let random_value = rng.gen_range(0..3);
283 match random_value {
284 0 => Some(DynamicFieldType::DynamicField),
285 1 => Some(DynamicFieldType::DynamicObject),
286 _ => None,
287 }
288 },
289 }
290 }
291}
292
293impl IndexedObject {
294 pub fn from_object(
295 checkpoint_sequence_number: CheckpointSequenceNumber,
296 object: Object,
297 df_kind: Option<DynamicFieldType>,
298 ) -> Self {
299 Self {
300 checkpoint_sequence_number,
301 object,
302 df_kind,
303 }
304 }
305}
306
307#[derive(Clone, Debug)]
308pub struct IndexedDeletedObject {
309 pub object_id: ObjectID,
310 pub object_version: u64,
311 pub checkpoint_sequence_number: u64,
312}
313
314impl IndexedDeletedObject {
315 pub fn random() -> Self {
316 let mut rng = rand::thread_rng();
317 IndexedDeletedObject {
318 object_id: ObjectID::random(),
319 object_version: rng.r#gen(),
320 checkpoint_sequence_number: rng.r#gen(),
321 }
322 }
323}
324
325#[derive(Debug)]
326pub struct IndexedPackage {
327 pub package_id: ObjectID,
328 pub move_package: MovePackage,
329 pub checkpoint_sequence_number: u64,
330}
331
332#[derive(Debug, Clone)]
333pub enum TransactionKind {
334 SystemTransaction = 0,
335 ProgrammableTransaction = 1,
336}
337
338#[derive(Debug, Clone)]
339pub struct IndexedTransaction {
340 pub tx_sequence_number: u64,
341 pub tx_digest: TransactionDigest,
342 pub sender_signed_data: SenderSignedData,
343 pub effects: TransactionEffects,
344 pub checkpoint_sequence_number: u64,
345 pub timestamp_ms: u64,
346 pub object_changes: Vec<IndexedObjectChange>,
347 pub balance_change: Vec<sui_json_rpc_types::BalanceChange>,
348 pub events: Vec<sui_types::event::Event>,
349 pub transaction_kind: TransactionKind,
350 pub successful_tx_num: u64,
351}
352
353#[derive(Debug, Clone)]
354pub struct TxIndex {
355 pub tx_sequence_number: u64,
356 pub tx_kind: TransactionKind,
357 pub transaction_digest: TransactionDigest,
358 pub checkpoint_sequence_number: u64,
359 pub input_objects: Vec<ObjectID>,
360 pub changed_objects: Vec<ObjectID>,
361 pub affected_objects: Vec<ObjectID>,
362 pub payers: Vec<SuiAddress>,
363 pub sender: SuiAddress,
364 pub recipients: Vec<SuiAddress>,
365 pub move_calls: Vec<(ObjectID, String, String)>,
366}
367
368impl TxIndex {
369 pub fn random() -> Self {
370 let mut rng = rand::thread_rng();
371 TxIndex {
372 tx_sequence_number: rng.r#gen(),
373 tx_kind: if rng.gen_bool(0.5) {
374 TransactionKind::SystemTransaction
375 } else {
376 TransactionKind::ProgrammableTransaction
377 },
378 transaction_digest: TransactionDigest::random(),
379 checkpoint_sequence_number: rng.r#gen(),
380 input_objects: (0..1000).map(|_| ObjectID::random()).collect(),
381 changed_objects: (0..1000).map(|_| ObjectID::random()).collect(),
382 affected_objects: (0..1000).map(|_| ObjectID::random()).collect(),
383 payers: (0..rng.gen_range(0..100))
384 .map(|_| SuiAddress::random_for_testing_only())
385 .collect(),
386 sender: SuiAddress::random_for_testing_only(),
387 recipients: (0..rng.gen_range(0..1000))
388 .map(|_| SuiAddress::random_for_testing_only())
389 .collect(),
390 move_calls: (0..rng.gen_range(0..1000))
391 .map(|_| {
392 (
393 ObjectID::random(),
394 rng.r#gen::<u64>().to_string(),
395 rng.r#gen::<u64>().to_string(),
396 )
397 })
398 .collect(),
399 }
400 }
401}
402
403#[serde_as]
405#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
406pub enum IndexedObjectChange {
407 Published {
408 package_id: ObjectID,
409 version: SequenceNumber,
410 digest: ObjectDigest,
411 modules: Vec<String>,
412 },
413 Transferred {
414 sender: SuiAddress,
415 recipient: Owner,
416 #[serde_as(as = "SuiStructTag")]
417 object_type: StructTag,
418 object_id: ObjectID,
419 version: SequenceNumber,
420 digest: ObjectDigest,
421 },
422 Mutated {
424 sender: SuiAddress,
425 owner: Owner,
426 #[serde_as(as = "SuiStructTag")]
427 object_type: StructTag,
428 object_id: ObjectID,
429 version: SequenceNumber,
430 previous_version: SequenceNumber,
431 digest: ObjectDigest,
432 },
433 Deleted {
435 sender: SuiAddress,
436 #[serde_as(as = "SuiStructTag")]
437 object_type: StructTag,
438 object_id: ObjectID,
439 version: SequenceNumber,
440 },
441 Wrapped {
443 sender: SuiAddress,
444 #[serde_as(as = "SuiStructTag")]
445 object_type: StructTag,
446 object_id: ObjectID,
447 version: SequenceNumber,
448 },
449 Created {
451 sender: SuiAddress,
452 owner: Owner,
453 #[serde_as(as = "SuiStructTag")]
454 object_type: StructTag,
455 object_id: ObjectID,
456 version: SequenceNumber,
457 digest: ObjectDigest,
458 },
459}
460
461impl From<ObjectChange> for IndexedObjectChange {
462 fn from(oc: ObjectChange) -> Self {
463 match oc {
464 ObjectChange::Published {
465 package_id,
466 version,
467 digest,
468 modules,
469 } => Self::Published {
470 package_id,
471 version,
472 digest,
473 modules,
474 },
475 ObjectChange::Transferred {
476 sender,
477 recipient,
478 object_type,
479 object_id,
480 version,
481 digest,
482 } => Self::Transferred {
483 sender,
484 recipient,
485 object_type,
486 object_id,
487 version,
488 digest,
489 },
490 ObjectChange::Mutated {
491 sender,
492 owner,
493 object_type,
494 object_id,
495 version,
496 previous_version,
497 digest,
498 } => Self::Mutated {
499 sender,
500 owner,
501 object_type,
502 object_id,
503 version,
504 previous_version,
505 digest,
506 },
507 ObjectChange::Deleted {
508 sender,
509 object_type,
510 object_id,
511 version,
512 } => Self::Deleted {
513 sender,
514 object_type,
515 object_id,
516 version,
517 },
518 ObjectChange::Wrapped {
519 sender,
520 object_type,
521 object_id,
522 version,
523 } => Self::Wrapped {
524 sender,
525 object_type,
526 object_id,
527 version,
528 },
529 ObjectChange::Created {
530 sender,
531 owner,
532 object_type,
533 object_id,
534 version,
535 digest,
536 } => Self::Created {
537 sender,
538 owner,
539 object_type,
540 object_id,
541 version,
542 digest,
543 },
544 }
545 }
546}
547
548impl From<IndexedObjectChange> for ObjectChange {
549 fn from(val: IndexedObjectChange) -> Self {
550 match val {
551 IndexedObjectChange::Published {
552 package_id,
553 version,
554 digest,
555 modules,
556 } => ObjectChange::Published {
557 package_id,
558 version,
559 digest,
560 modules,
561 },
562 IndexedObjectChange::Transferred {
563 sender,
564 recipient,
565 object_type,
566 object_id,
567 version,
568 digest,
569 } => ObjectChange::Transferred {
570 sender,
571 recipient,
572 object_type,
573 object_id,
574 version,
575 digest,
576 },
577 IndexedObjectChange::Mutated {
578 sender,
579 owner,
580 object_type,
581 object_id,
582 version,
583 previous_version,
584 digest,
585 } => ObjectChange::Mutated {
586 sender,
587 owner,
588 object_type,
589 object_id,
590 version,
591 previous_version,
592 digest,
593 },
594 IndexedObjectChange::Deleted {
595 sender,
596 object_type,
597 object_id,
598 version,
599 } => ObjectChange::Deleted {
600 sender,
601 object_type,
602 object_id,
603 version,
604 },
605 IndexedObjectChange::Wrapped {
606 sender,
607 object_type,
608 object_id,
609 version,
610 } => ObjectChange::Wrapped {
611 sender,
612 object_type,
613 object_id,
614 version,
615 },
616 IndexedObjectChange::Created {
617 sender,
618 owner,
619 object_type,
620 object_id,
621 version,
622 digest,
623 } => ObjectChange::Created {
624 sender,
625 owner,
626 object_type,
627 object_id,
628 version,
629 digest,
630 },
631 }
632 }
633}
634
635pub struct SuiTransactionBlockResponseWithOptions {
637 pub response: SuiTransactionBlockResponse,
638 pub options: SuiTransactionBlockResponseOptions,
639}
640
641impl From<SuiTransactionBlockResponseWithOptions> for SuiTransactionBlockResponse {
642 fn from(value: SuiTransactionBlockResponseWithOptions) -> Self {
643 let SuiTransactionBlockResponseWithOptions { response, options } = value;
644
645 SuiTransactionBlockResponse {
646 digest: response.digest,
647 transaction: options.show_input.then_some(response.transaction).flatten(),
648 raw_transaction: if options.show_raw_input {
649 response.raw_transaction
650 } else {
651 vec![]
652 },
653 effects: options.show_effects.then_some(response.effects).flatten(),
654 events: options.show_events.then_some(response.events).flatten(),
655 object_changes: options
656 .show_object_changes
657 .then_some(response.object_changes)
658 .flatten(),
659 balance_changes: options
660 .show_balance_changes
661 .then_some(response.balance_changes)
662 .flatten(),
663 timestamp_ms: response.timestamp_ms,
664 confirmed_local_execution: response.confirmed_local_execution,
665 checkpoint: response.checkpoint,
666 errors: vec![],
667 raw_effects: if options.show_raw_effects {
668 response.raw_effects
669 } else {
670 vec![]
671 },
672 }
673 }
674}