Skip to main content

sui_indexer_alt_reader/
checkpoints.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::HashMap;
5
6use anyhow::Context;
7use async_graphql::dataloader::Loader;
8use futures::future::try_join_all;
9use prost_types::FieldMask;
10use sui_rpc::field::FieldMaskUtil;
11use sui_rpc::proto::sui::rpc::v2 as proto;
12use sui_types::crypto::AuthorityQuorumSignInfo;
13use sui_types::messages_checkpoint::CheckpointContents;
14use sui_types::messages_checkpoint::CheckpointSummary;
15
16use crate::error::Error;
17use crate::ledger_grpc_reader::LedgerGrpcReader;
18
19/// Key for fetching a checkpoint's content by its sequence number.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub struct CheckpointKey(pub u64);
22
23#[async_trait::async_trait]
24impl Loader<CheckpointKey> for LedgerGrpcReader {
25    type Value = (
26        CheckpointSummary,
27        CheckpointContents,
28        AuthorityQuorumSignInfo<true>,
29    );
30    type Error = Error;
31
32    async fn load(
33        &self,
34        keys: &[CheckpointKey],
35    ) -> Result<HashMap<CheckpointKey, Self::Value>, Error> {
36        if keys.is_empty() {
37            return Ok(HashMap::new());
38        }
39
40        let futures = keys.iter().map(|key| async {
41            let request = proto::GetCheckpointRequest::by_sequence_number(key.0).with_read_mask(
42                FieldMask::from_paths(["summary.bcs", "signature", "contents.bcs"]),
43            );
44
45            match self.get_checkpoint(request).await {
46                Ok(response) => {
47                    let checkpoint = response.checkpoint.context("No checkpoint returned")?;
48
49                    let summary: CheckpointSummary = checkpoint
50                        .summary
51                        .as_ref()
52                        .and_then(|s| s.bcs.as_ref())
53                        .context("Missing summary.bcs")?
54                        .deserialize()
55                        .context("Failed to deserialize checkpoint summary")?;
56
57                    let contents: CheckpointContents = checkpoint
58                        .contents
59                        .as_ref()
60                        .and_then(|c| c.bcs.as_ref())
61                        .context("Missing contents.bcs")?
62                        .deserialize()
63                        .context("Failed to deserialize checkpoint contents")?;
64
65                    let signature: AuthorityQuorumSignInfo<true> = {
66                        let sdk_sig = sui_sdk_types::ValidatorAggregatedSignature::try_from(
67                            checkpoint.signature.as_ref().context("Missing signature")?,
68                        )
69                        .context("Failed to parse signature")?;
70                        AuthorityQuorumSignInfo::from(sdk_sig)
71                    };
72
73                    Ok(Some((*key, (summary, contents, signature))))
74                }
75                Err(status) if status.code() == tonic::Code::NotFound => Ok(None),
76                Err(e) => Err(Error::from(e)),
77            }
78        });
79
80        let results: Vec<_> = try_join_all(futures).await?;
81        Ok(results.into_iter().flatten().collect())
82    }
83}