sui_indexer_alt/
bootstrap.rs1use std::time::Duration;
5
6use crate::Indexer;
7use anyhow::{Context, Result, bail};
8use diesel::{OptionalExtension, QueryDsl, SelectableHelper};
9use diesel_async::RunQueryDsl;
10use sui_indexer_alt_framework::postgres::Db;
11use sui_indexer_alt_framework::types::{
12 full_checkpoint_content::Checkpoint,
13 sui_system_state::{SuiSystemStateTrait, get_sui_system_state},
14 transaction::TransactionKind,
15};
16use sui_indexer_alt_schema::{
17 checkpoints::StoredGenesis,
18 epochs::StoredEpochStart,
19 schema::{kv_epoch_starts, kv_genesis},
20};
21use sui_types::transaction::TransactionDataAPI;
22use tracing::info;
23
24pub struct BootstrapGenesis {
25 pub stored_genesis: StoredGenesis,
26 pub stored_epoch_start: StoredEpochStart,
27}
28
29pub async fn bootstrap(
34 indexer: &Indexer<Db>,
35 retry_interval: Duration,
36 bootstrap_genesis: Option<BootstrapGenesis>,
37) -> Result<StoredGenesis> {
38 info!("Bootstrapping indexer with genesis information");
39
40 let Ok(mut conn) = indexer.store().connect().await else {
41 bail!("Bootstrap failed to get connection for DB");
42 };
43
44 if let Some(genesis) = kv_genesis::table
46 .select(StoredGenesis::as_select())
47 .first(&mut conn)
48 .await
49 .optional()?
50 {
51 info!(
52 chain = genesis.chain()?.as_str(),
53 protocol = ?genesis.initial_protocol_version(),
54 "Indexer already bootstrapped",
55 );
56
57 return Ok(genesis);
58 }
59
60 let BootstrapGenesis {
61 stored_genesis,
62 stored_epoch_start,
63 } = match bootstrap_genesis {
64 Some(bootstrap_genesis) => bootstrap_genesis,
66 None => {
71 let genesis_checkpoint = indexer
72 .ingestion_client()
73 .wait_for(0, retry_interval)
74 .await
75 .context("Failed to fetch genesis checkpoint")?;
76
77 let Checkpoint {
78 summary: checkpoint_summary,
79 transactions,
80 ..
81 } = genesis_checkpoint.as_ref();
82
83 let Some(genesis_transaction) = transactions
84 .iter()
85 .find(|tx| matches!(tx.transaction.kind(), TransactionKind::Genesis(_)))
86 else {
87 bail!("Could not find Genesis transaction");
88 };
89
90 let output_objects: Vec<_> = genesis_transaction
91 .output_objects(&genesis_checkpoint.object_set)
92 .cloned()
93 .collect();
94
95 let sui_system_state = get_sui_system_state(&output_objects.as_slice())
96 .context("Failed to get Genesis SystemState")?;
97
98 let stored_genesis = StoredGenesis {
99 genesis_digest: checkpoint_summary.digest().inner().to_vec(),
100 initial_protocol_version: sui_system_state.protocol_version() as i64,
101 };
102 let stored_epoch_start = StoredEpochStart {
103 epoch: 0,
104 protocol_version: sui_system_state.protocol_version() as i64,
105 cp_lo: 0,
106 start_timestamp_ms: sui_system_state.epoch_start_timestamp_ms() as i64,
107 reference_gas_price: sui_system_state.reference_gas_price() as i64,
108 system_state: bcs::to_bytes(&sui_system_state)
109 .context("Failed to serialize SystemState")?,
110 };
111
112 BootstrapGenesis {
113 stored_genesis,
114 stored_epoch_start,
115 }
116 }
117 };
118
119 info!(
120 chain = stored_genesis.chain()?.as_str(),
121 protocol = ?stored_genesis.initial_protocol_version(),
122 "Bootstrapped indexer",
123 );
124
125 diesel::insert_into(kv_genesis::table)
126 .values(&stored_genesis)
127 .on_conflict_do_nothing()
128 .execute(&mut conn)
129 .await
130 .context("Failed to write genesis record")?;
131
132 diesel::insert_into(kv_epoch_starts::table)
133 .values(&stored_epoch_start)
134 .on_conflict_do_nothing()
135 .execute(&mut conn)
136 .await
137 .context("Failed to write genesis epoch start record")?;
138
139 Ok(stored_genesis)
140}