iota_replay/
types.rs

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