sui_replay/
types.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use jsonrpsee::core::ClientError as JsonRpseeError;
5use move_binary_format::CompiledModule;
6use move_core_types::account_address::AccountAddress;
7use move_core_types::language_storage::{ModuleId, StructTag};
8use serde::Deserialize;
9use serde::Serialize;
10use std::fmt::Debug;
11use sui_json_rpc_types::SuiEvent;
12use sui_json_rpc_types::SuiTransactionBlockEffects;
13use sui_protocol_config::{Chain, ProtocolVersion};
14use sui_sdk::error::Error as SuiRpcError;
15use sui_types::base_types::{ObjectID, ObjectRef, SequenceNumber, SuiAddress, VersionNumber};
16use sui_types::digests::{ObjectDigest, TransactionDigest};
17use sui_types::error::{SuiError, SuiErrorKind, SuiObjectResponseError, SuiResult, UserInputError};
18use sui_types::object::Object;
19use sui_types::transaction::{InputObjectKind, SenderSignedData, TransactionKind};
20use thiserror::Error;
21use tokio::time::Duration;
22use tracing::{error, warn};
23
24use crate::config::ReplayableNetworkConfigSet;
25
26// TODO: make these configurable
27pub(crate) const RPC_TIMEOUT_ERR_SLEEP_RETRY_PERIOD: Duration = Duration::from_millis(100_000);
28pub(crate) const RPC_TIMEOUT_ERR_NUM_RETRIES: u32 = 3;
29pub(crate) const MAX_CONCURRENT_REQUESTS: usize = 1_000;
30
31// Struct tag used in system epoch change events
32pub(crate) const EPOCH_CHANGE_STRUCT_TAG: &str =
33    "0x3::sui_system_state_inner::SystemEpochInfoEvent";
34
35// TODO: A lot of the information in OnChainTransactionInfo is redundant from what's already in
36// SenderSignedData. We should consider removing them.
37#[derive(Clone, Debug, Serialize, Deserialize)]
38pub struct OnChainTransactionInfo {
39    pub tx_digest: TransactionDigest,
40    pub sender_signed_data: SenderSignedData,
41    pub sender: SuiAddress,
42    pub input_objects: Vec<InputObjectKind>,
43    pub kind: TransactionKind,
44    pub modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
45    pub shared_object_refs: Vec<ObjectRef>,
46    pub gas: Vec<(ObjectID, SequenceNumber, ObjectDigest)>,
47    #[serde(default)]
48    pub gas_owner: Option<SuiAddress>,
49    pub gas_budget: u64,
50    pub gas_price: u64,
51    pub executed_epoch: u64,
52    pub dependencies: Vec<TransactionDigest>,
53    #[serde(skip)]
54    pub receiving_objs: Vec<(ObjectID, SequenceNumber)>,
55    #[serde(skip)]
56    pub config_objects: Vec<(ObjectID, SequenceNumber)>,
57    // TODO: There are two problems with this being a json-rpc type:
58    // 1. The json-rpc type is not a perfect mirror with TransactionEffects since v2. We lost the
59    // ability to replay effects v2 specific forks. We need to fix this asap. Unfortunately at the moment
60    // it is really difficult to get the raw effects given a transaction digest.
61    // 2. This data structure is not bcs/bincode friendly. It makes it much more expensive to
62    // store the sandbox state for batch replay.
63    pub effects: SuiTransactionBlockEffects,
64    pub protocol_version: ProtocolVersion,
65    pub epoch_start_timestamp: u64,
66    pub reference_gas_price: u64,
67    #[serde(default = "unspecified_chain")]
68    pub chain: Chain,
69}
70
71fn unspecified_chain() -> Chain {
72    warn!("Unable to determine chain id. Defaulting to unknown");
73    Chain::Unknown
74}
75
76#[allow(clippy::large_enum_variant)]
77#[derive(Debug, Error, Clone)]
78pub enum ReplayEngineError {
79    #[error("SuiError: {:#?}", err)]
80    SuiError { err: SuiError },
81
82    #[error("SuiRpcError: {:#?}", err)]
83    SuiRpcError { err: String },
84
85    #[error("SuiObjectResponseError: {:#?}", err)]
86    SuiObjectResponseError { err: SuiObjectResponseError },
87
88    #[error("UserInputError: {:#?}", err)]
89    UserInputError { err: UserInputError },
90
91    #[error("GeneralError: {:#?}", err)]
92    GeneralError { err: String },
93
94    #[error("SuiRpcRequestTimeout")]
95    SuiRpcRequestTimeout,
96
97    #[error("ObjectNotExist: {:#?}", id)]
98    ObjectNotExist { id: ObjectID },
99
100    #[error("ObjectVersionNotFound: {:#?} version {}", id, version)]
101    ObjectVersionNotFound {
102        id: ObjectID,
103        version: SequenceNumber,
104    },
105
106    #[error(
107        "ObjectVersionTooHigh: {:#?}, requested version {}, latest version found {}",
108        id,
109        asked_version,
110        latest_version
111    )]
112    ObjectVersionTooHigh {
113        id: ObjectID,
114        asked_version: SequenceNumber,
115        latest_version: SequenceNumber,
116    },
117
118    #[error(
119        "ObjectDeleted: {:#?} at version {:#?} digest {:#?}",
120        id,
121        version,
122        digest
123    )]
124    ObjectDeleted {
125        id: ObjectID,
126        version: SequenceNumber,
127        digest: ObjectDigest,
128    },
129
130    #[error(
131        "EffectsForked: Effects for digest {} forked with diff {}",
132        digest,
133        diff
134    )]
135    EffectsForked {
136        digest: TransactionDigest,
137        diff: String,
138        on_chain: Box<SuiTransactionBlockEffects>,
139        local: Box<SuiTransactionBlockEffects>,
140    },
141
142    #[error(
143        "Transaction {:#?} not supported by replay. Reason: {:?}",
144        digest,
145        reason
146    )]
147    TransactionNotSupported {
148        digest: TransactionDigest,
149        reason: String,
150    },
151
152    #[error(
153        "Fatal! No framework versions for protocol version {protocol_version}. Make sure version tables are populated"
154    )]
155    FrameworkObjectVersionTableNotPopulated { protocol_version: u64 },
156
157    #[error("Protocol version not found for epoch {epoch}")]
158    ProtocolVersionNotFound { epoch: u64 },
159
160    #[error("Error querying system events for epoch {epoch}")]
161    ErrorQueryingSystemEvents { epoch: u64 },
162
163    #[error("Invalid epoch change transaction in events for epoch {epoch}")]
164    InvalidEpochChangeTx { epoch: u64 },
165
166    #[error("Unexpected event format {:#?}", event)]
167    UnexpectedEventFormat { event: SuiEvent },
168
169    #[error("Unable to find event for epoch {epoch}")]
170    EventNotFound { epoch: u64 },
171
172    #[error("Unable to find checkpoints for epoch {epoch}")]
173    UnableToDetermineCheckpoint { epoch: u64 },
174
175    #[error("Unable to query system events; {}", rpc_err)]
176    UnableToQuerySystemEvents { rpc_err: String },
177
178    #[error("Internal error or cache corrupted! Object {id}{} should be in cache.", version.map(|q| format!(" version {:#?}", q)).unwrap_or_default() )]
179    InternalCacheInvariantViolation {
180        id: ObjectID,
181        version: Option<SequenceNumber>,
182    },
183
184    #[error("Error getting dynamic fields loaded objects: {}", rpc_err)]
185    UnableToGetDynamicFieldLoadedObjects { rpc_err: String },
186
187    #[error("Unable to open yaml cfg file at {}: {}", path, err)]
188    UnableToOpenYamlFile { path: String, err: String },
189
190    #[error("Unable to write yaml file at {}: {}", path, err)]
191    UnableToWriteYamlFile { path: String, err: String },
192
193    #[error("Unable to convert string {} to URL {}", url, err)]
194    InvalidUrl { url: String, err: String },
195
196    #[error(
197        "Unable to execute transaction with existing network configs {:#?}",
198        cfgs
199    )]
200    UnableToExecuteWithNetworkConfigs { cfgs: ReplayableNetworkConfigSet },
201
202    #[error("Unable to get chain id: {}", err)]
203    UnableToGetChainId { err: String },
204}
205
206impl From<SuiObjectResponseError> for ReplayEngineError {
207    fn from(err: SuiObjectResponseError) -> Self {
208        match err {
209            SuiObjectResponseError::NotExists { object_id } => {
210                ReplayEngineError::ObjectNotExist { id: object_id }
211            }
212            SuiObjectResponseError::Deleted {
213                object_id,
214                digest,
215                version,
216            } => ReplayEngineError::ObjectDeleted {
217                id: object_id,
218                version,
219                digest,
220            },
221            _ => ReplayEngineError::SuiObjectResponseError { err },
222        }
223    }
224}
225
226impl From<ReplayEngineError> for SuiError {
227    fn from(err: ReplayEngineError) -> Self {
228        SuiError::from(SuiErrorKind::from(err))
229    }
230}
231
232impl From<ReplayEngineError> for SuiErrorKind {
233    fn from(err: ReplayEngineError) -> Self {
234        SuiErrorKind::Unknown(format!("{:#?}", err))
235    }
236}
237
238impl From<SuiError> for ReplayEngineError {
239    fn from(err: SuiError) -> Self {
240        ReplayEngineError::SuiError { err }
241    }
242}
243
244impl From<SuiErrorKind> for ReplayEngineError {
245    fn from(err: SuiErrorKind) -> Self {
246        SuiError::from(err).into()
247    }
248}
249
250impl From<SuiRpcError> for ReplayEngineError {
251    fn from(err: SuiRpcError) -> Self {
252        match err {
253            SuiRpcError::RpcError(JsonRpseeError::RequestTimeout) => {
254                ReplayEngineError::SuiRpcRequestTimeout
255            }
256            _ => ReplayEngineError::SuiRpcError {
257                err: format!("{:?}", err),
258            },
259        }
260    }
261}
262
263impl From<UserInputError> for ReplayEngineError {
264    fn from(err: UserInputError) -> Self {
265        ReplayEngineError::UserInputError { err }
266    }
267}
268
269impl From<anyhow::Error> for ReplayEngineError {
270    fn from(err: anyhow::Error) -> Self {
271        ReplayEngineError::GeneralError {
272            err: format!("{:#?}", err),
273        }
274    }
275}
276
277/// TODO: Limited set but will add more
278#[derive(Debug)]
279#[allow(clippy::large_enum_variant)]
280pub enum ExecutionStoreEvent {
281    BackingPackageGetPackageObject {
282        package_id: ObjectID,
283        result: SuiResult<Option<Object>>,
284    },
285    ChildObjectResolverStoreReadChildObject {
286        parent: ObjectID,
287        child: ObjectID,
288        result: SuiResult<Option<Object>>,
289    },
290    ParentSyncStoreGetLatestParentEntryRef {
291        object_id: ObjectID,
292        result: Option<ObjectRef>,
293    },
294    ResourceResolverGetResource {
295        address: AccountAddress,
296        typ: StructTag,
297        result: SuiResult<Option<Vec<u8>>>,
298    },
299    ModuleResolverGetModule {
300        module_id: ModuleId,
301        result: SuiResult<Option<Vec<u8>>>,
302    },
303    ObjectStoreGetObject {
304        object_id: ObjectID,
305        result: SuiResult<Option<Object>>,
306    },
307    ObjectStoreGetObjectByKey {
308        object_id: ObjectID,
309        version: VersionNumber,
310        result: SuiResult<Option<Object>>,
311    },
312    GetModuleGetModuleByModuleId {
313        id: ModuleId,
314        result: SuiResult<Option<CompiledModule>>,
315    },
316    ReceiveObject {
317        owner: ObjectID,
318        receive: ObjectID,
319        receive_at_version: SequenceNumber,
320        result: SuiResult<Option<Object>>,
321    },
322}