Repository: MystenLabs/sui
File: crates/sui-core/src/rpc_index.rs (Rust)

Code before:

        let mut epoch = self.epochs.get(&checkpoint.epoch)?.unwrap_or_default();
        epoch.epoch = checkpoint.epoch;

        if epoch.protocol_version.is_none() {
            epoch.protocol_version = Some(system_state.protocol_version());
        }

        if epoch.start_timestamp_ms.is_none() {
            epoch.start_timestamp_ms = Some(system_state.epoch_start_timestamp_ms());
        }

        if epoch.reference_gas_price.is_none() {
            epoch.reference_gas_price = Some(system_state.reference_gas_price());
        }

        if epoch.system_state.is_none() {
            epoch.system_state = Some(system_state);
        }

        self.epochs.insert(&epoch.epoch, &epoch)?;

        Ok(())
    }

    fn index_transactions(
        &self,
        checkpoint: &CheckpointData,
        batch: &mut typed_store::rocks::DBBatch,
        index_events: bool,
    ) -> Result<(), StorageError> {
        let cp = checkpoint.checkpoint_summary.sequence_number;
        let mut current_accumulator_version: Option<u64> = None;

        // iterate in reverse order, process accumulator settlements first
        for (tx_idx, tx) in checkpoint.transactions.iter().enumerate().rev() {
            let info = TransactionInfo::new(
                tx.transaction.transaction_data(),
                &tx.effects,
                &tx.input_objects,
                &tx.output_objects,
                cp,
            );

            let digest = tx.transaction.digest();
            batch.insert_batch(&self.transactions, [(digest, info)])?;

            if index_events {
                if let Some(version) = self.extract_accumulator_version(tx) {
                    current_accumulator_version = Some(version);
                }

Review comment (bug):
In lieu of the consistent store, we might want to store the address balance and coin object balance as separate fields on the BalanceIndexInfo struct. 

I think if we combine the two balance types together in the index, we would have to do two reads to implement the coin-object-balance / address-balance breakout in the response. With two reads, we would end up with consistency issues. I.e. if the index is updated between the two reads, we'd return a response for a state that the balance was never actually in, which could be confusing or broken for some use cases.

Code after:
        // iterate in reverse order, process accumulator settlements first
        for (tx_idx, tx) in checkpoint.transactions.iter().enumerate().rev() {
            let info = TransactionInfo::new(
                tx.transaction.transaction_data(),
                &tx.effects,
                &tx.input_objects,
                &tx.output_objects,
                cp,
            );

            let balance_changes = info.balance_changes.iter().filter_map(|change| {
                if let TypeTag::Struct(coin_type) = &change.coin_type {
                    Some((
                        BalanceKey {
                            owner: change.address,
                            coin_type: coin_type.as_ref().to_owned(),
                        },
                        BalanceIndexInfo {
                            balance_delta: change.amount,
                        },
                    ))
                } else {
                    None
                }
            });
            batch.partial_merge_batch(&self.balance, balance_changes)?;

            let digest = tx.transaction.digest();
            batch.insert_batch(&self.transactions, [(digest, info)])?;

            if index_events {
                if let Some(version) = self.extract_accumulator_version(tx) {
                    current_accumulator_version = Some(version);
                }

                self.index_transaction_events(
                    tx,
                    cp,
                    tx_idx as u32,
                    current_accumulator_version,
                    batch,
                )?;
            }
        }

        Ok(())
    }

    fn index_objects(
        &self,
        checkpoint: &CheckpointData,