sui_analytics_indexer/handlers/tables/
df.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::HashMap;
5use std::sync::Arc;
6
7use anyhow::Context as _;
8use anyhow::Result;
9use async_trait::async_trait;
10use fastcrypto::encoding::Base64;
11use fastcrypto::encoding::Encoding;
12use sui_indexer_alt_framework::pipeline::Processor;
13use sui_json_rpc_types::SuiMoveValue;
14use sui_types::TypeTag;
15use sui_types::base_types::EpochId;
16use sui_types::base_types::ObjectID;
17use sui_types::dynamic_field::DynamicFieldName;
18use sui_types::dynamic_field::DynamicFieldType;
19use sui_types::dynamic_field::visitor as DFV;
20use sui_types::full_checkpoint_content::Checkpoint;
21use sui_types::object::Object;
22use sui_types::object::Owner;
23use sui_types::object::bounded_visitor::BoundedVisitor;
24use tap::tap::TapFallible;
25use tracing::warn;
26
27use crate::Row;
28use crate::package_store::PackageCache;
29use crate::pipeline::Pipeline;
30use crate::tables::DynamicFieldRow;
31
32pub struct DynamicFieldProcessor {
33    package_cache: Arc<PackageCache>,
34}
35
36impl DynamicFieldProcessor {
37    pub fn new(package_cache: Arc<PackageCache>) -> Self {
38        Self { package_cache }
39    }
40}
41
42impl DynamicFieldProcessor {
43    async fn process_dynamic_field(
44        &self,
45        epoch: u64,
46        checkpoint: u64,
47        timestamp_ms: u64,
48        object: &Object,
49        all_written_objects: &HashMap<ObjectID, Object>,
50    ) -> Result<Option<DynamicFieldRow>> {
51        let move_obj_opt = object.data.try_as_move();
52        let Some(move_object) = move_obj_opt else {
53            return Ok(None);
54        };
55        if !move_object.type_().is_dynamic_field() {
56            return Ok(None);
57        }
58
59        let layout = self
60            .package_cache
61            .resolver_for_epoch(epoch)
62            .type_layout(move_object.type_().clone().into())
63            .await?;
64        let object_id = object.id();
65
66        let field = DFV::FieldVisitor::deserialize(move_object.contents(), &layout)?;
67
68        let type_ = field.kind;
69        let name_type: TypeTag = field.name_layout.into();
70        let bcs_name = field.name_bytes.to_owned();
71
72        let name_value = BoundedVisitor::deserialize_value(field.name_bytes, field.name_layout)
73            .tap_err(|e| {
74                warn!("{e}");
75            })?;
76        let name = DynamicFieldName {
77            type_: name_type,
78            value: SuiMoveValue::from(name_value).to_json_value(),
79        };
80        let name_json = serde_json::to_string(&name)?;
81        let owner_id = match &object.owner {
82            Owner::AddressOwner(address) => Some(*address),
83            Owner::ObjectOwner(address) => Some(*address),
84            Owner::Shared { .. } => None,
85            Owner::Immutable => None,
86            Owner::ConsensusAddressOwner { owner, .. } => Some(*owner),
87        };
88        let Some(parent_id) = owner_id else {
89            return Ok(None);
90        };
91        let row = match type_ {
92            DynamicFieldType::DynamicField => DynamicFieldRow {
93                parent_object_id: parent_id.to_string(),
94                transaction_digest: object.previous_transaction.base58_encode(),
95                checkpoint,
96                epoch,
97                timestamp_ms,
98                name: name_json,
99                bcs_name: Base64::encode(bcs_name),
100                type_,
101                object_id: object.id().to_string(),
102                version: object.version().value(),
103                digest: object.digest().to_string(),
104                object_type: move_object.clone().into_type().into_type_params()[1]
105                    .to_canonical_string(/* with_prefix */ true),
106            },
107            DynamicFieldType::DynamicObject => {
108                let object = all_written_objects.get(&object_id)
109                    .with_context(|| format!("Failed to find object_id {object_id:?} when trying to create dynamic field info"))?;
110                let version = object.version().value();
111                let digest = object.digest().to_string();
112                let object_type = object.data.type_().unwrap().clone();
113                DynamicFieldRow {
114                    parent_object_id: parent_id.to_string(),
115                    transaction_digest: object.previous_transaction.base58_encode(),
116                    checkpoint,
117                    epoch,
118                    timestamp_ms,
119                    name: name_json,
120                    bcs_name: Base64::encode(bcs_name),
121                    type_,
122                    object_id: object.id().to_string(),
123                    digest,
124                    version,
125                    object_type: object_type.to_canonical_string(true),
126                }
127            }
128        };
129        Ok(Some(row))
130    }
131}
132
133#[async_trait]
134impl Processor for DynamicFieldProcessor {
135    const NAME: &'static str = Pipeline::DynamicField.name();
136    type Value = DynamicFieldRow;
137
138    async fn process(&self, checkpoint: &Arc<Checkpoint>) -> Result<Vec<Self::Value>> {
139        let epoch = checkpoint.summary.data().epoch;
140        let checkpoint_seq = checkpoint.summary.data().sequence_number;
141        let timestamp_ms = checkpoint.summary.data().timestamp_ms;
142
143        let mut entries = Vec::new();
144
145        for checkpoint_transaction in &checkpoint.transactions {
146            let output_objects: Vec<&Object> = checkpoint_transaction
147                .output_objects(&checkpoint.object_set)
148                .collect();
149
150            let all_objects: HashMap<_, _> = output_objects
151                .iter()
152                .map(|x| (x.id(), (*x).clone()))
153                .collect();
154
155            for object in &output_objects {
156                if let Some(row) = self
157                    .process_dynamic_field(
158                        epoch,
159                        checkpoint_seq,
160                        timestamp_ms,
161                        object,
162                        &all_objects,
163                    )
164                    .await?
165                {
166                    entries.push(row);
167                }
168            }
169        }
170
171        Ok(entries)
172    }
173}
174
175impl Row for DynamicFieldRow {
176    fn get_epoch(&self) -> EpochId {
177        self.epoch
178    }
179
180    fn get_checkpoint(&self) -> u64 {
181        self.checkpoint
182    }
183}