1use std::fmt::Debug;
6
7use iota_json_rpc_types::{IotaEvent, IotaObjectResponseError, 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, 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::warn;
27
28use crate::config::ReplayableNetworkConfigSet;
29
30pub(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
35pub(crate) const EPOCH_CHANGE_STRUCT_TAGS: [&str; 2] = [
37 "0x3::iota_system_state_inner::SystemEpochInfoEventV2",
38 "0x3::iota_system_state_inner::SystemEpochInfoEventV1",
39];
40
41#[derive(Clone, Debug, Serialize, Deserialize)]
44pub struct OnChainTransactionInfo {
45 pub tx_digest: TransactionDigest,
46 pub sender_signed_data: SenderSignedData,
47 pub sender: IotaAddress,
48 pub input_objects: Vec<InputObjectKind>,
49 pub kind: TransactionKind,
50 pub modified_at_versions: Vec<(ObjectID, SequenceNumber)>,
51 pub shared_object_refs: Vec<ObjectRef>,
52 pub gas: Vec<ObjectRef>,
53 #[serde(default)]
54 pub gas_owner: Option<IotaAddress>,
55 pub gas_budget: u64,
56 pub gas_price: u64,
57 pub executed_epoch: u64,
58 pub dependencies: Vec<TransactionDigest>,
59 #[serde(skip)]
60 pub receiving_objs: Vec<(ObjectID, SequenceNumber)>,
61 #[serde(skip)]
62 pub config_objects: Vec<(ObjectID, SequenceNumber)>,
63 pub effects: IotaTransactionBlockEffects,
70 pub protocol_version: ProtocolVersion,
71 pub epoch_start_timestamp: u64,
72 pub reference_gas_price: u64,
73 #[serde(default = "unspecified_chain")]
74 pub chain: Chain,
75}
76
77fn unspecified_chain() -> Chain {
78 warn!("Unable to determine chain id. Defaulting to unknown");
79 Chain::Unknown
80}
81
82#[derive(Debug, Error, Clone)]
83pub enum ReplayEngineError {
84 #[error("IotaError: {:#?}", err)]
85 IotaError { err: IotaError },
86
87 #[error("IotaRpcError: {:#?}", err)]
88 IotaRpcError { err: String },
89
90 #[error("IotaObjectResponseError: {:#?}", err)]
91 IotaObjectResponseError { err: IotaObjectResponseError },
92
93 #[error("UserInputError: {:#?}", err)]
94 UserInputError { err: UserInputError },
95
96 #[error("GeneralError: {:#?}", err)]
97 GeneralError { err: String },
98
99 #[error("IotaRpcRequestTimeout")]
100 IotaRpcRequestTimeout,
101
102 #[error("ObjectNotExist: {:#?}", id)]
103 ObjectNotExist { id: ObjectID },
104
105 #[error("ObjectVersionNotFound: {:#?} version {}", id, version)]
106 ObjectVersionNotFound {
107 id: ObjectID,
108 version: SequenceNumber,
109 },
110
111 #[error(
112 "ObjectVersionTooHigh: {:#?}, requested version {}, latest version found {}",
113 id,
114 asked_version,
115 latest_version
116 )]
117 ObjectVersionTooHigh {
118 id: ObjectID,
119 asked_version: SequenceNumber,
120 latest_version: SequenceNumber,
121 },
122
123 #[error(
124 "ObjectDeleted: {:#?} at version {:#?} digest {:#?}",
125 id,
126 version,
127 digest
128 )]
129 ObjectDeleted {
130 id: ObjectID,
131 version: SequenceNumber,
132 digest: ObjectDigest,
133 },
134
135 #[error(
136 "EffectsForked: Effects for digest {} forked with diff {}",
137 digest,
138 diff
139 )]
140 EffectsForked {
141 digest: TransactionDigest,
142 diff: String,
143 on_chain: Box<IotaTransactionBlockEffects>,
144 local: Box<IotaTransactionBlockEffects>,
145 },
146
147 #[error(
148 "Transaction {:#?} not supported by replay. Reason: {:?}",
149 digest,
150 reason
151 )]
152 TransactionNotSupported {
153 digest: TransactionDigest,
154 reason: String,
155 },
156
157 #[error(
158 "Fatal! No framework versions for protocol version {protocol_version}. Make sure version tables are populated"
159 )]
160 FrameworkObjectVersionTableNotPopulated { protocol_version: u64 },
161
162 #[error("Protocol version not found for epoch {epoch}")]
163 ProtocolVersionNotFound { epoch: u64 },
164
165 #[error("Error querying system events for epoch {epoch}")]
166 ErrorQueryingSystemEvents { epoch: u64 },
167
168 #[error("Invalid epoch change transaction in events for epoch {epoch}")]
169 InvalidEpochChangeTx { epoch: u64 },
170
171 #[error("Unexpected event format {:#?}", event)]
172 UnexpectedEventFormat { event: Box<IotaEvent> },
173
174 #[error("Unable to find event for epoch {epoch}")]
175 EventNotFound { epoch: u64 },
176
177 #[error("Unable to find checkpoints for epoch {epoch}")]
178 UnableToDetermineCheckpoint { epoch: u64 },
179
180 #[error("Unable to query system events; {}", rpc_err)]
181 UnableToQuerySystemEvents { rpc_err: String },
182
183 #[error("Internal error or cache corrupted! Object {id}{} should be in cache.", version.map(|q| format!(" version {q:#?}")).unwrap_or_default()
184 )]
185 InternalCacheInvariantViolation {
186 id: ObjectID,
187 version: Option<SequenceNumber>,
188 },
189
190 #[error("Error getting dynamic fields loaded objects: {}", rpc_err)]
191 UnableToGetDynamicFieldLoadedObjects { rpc_err: String },
192
193 #[error("Unable to open yaml cfg file at {}: {}", path, err)]
194 UnableToOpenYamlFile { path: String, err: String },
195
196 #[error("Unable to write yaml file at {}: {}", path, err)]
197 UnableToWriteYamlFile { path: String, err: String },
198
199 #[error("Unable to convert string {} to URL {}", url, err)]
200 InvalidUrl { url: String, err: String },
201
202 #[error(
203 "Unable to execute transaction with existing network configs {:#?}",
204 cfgs
205 )]
206 UnableToExecuteWithNetworkConfigs { cfgs: ReplayableNetworkConfigSet },
207
208 #[error("Unable to get chain id: {}", err)]
209 UnableToGetChainId { err: String },
210}
211
212impl From<IotaObjectResponseError> for ReplayEngineError {
213 fn from(err: IotaObjectResponseError) -> Self {
214 match err {
215 IotaObjectResponseError::NotExists { object_id } => {
216 ReplayEngineError::ObjectNotExist { id: object_id }
217 }
218 IotaObjectResponseError::Deleted {
219 object_id,
220 digest,
221 version,
222 } => ReplayEngineError::ObjectDeleted {
223 id: object_id,
224 version,
225 digest,
226 },
227 _ => ReplayEngineError::IotaObjectResponseError { err },
228 }
229 }
230}
231
232impl From<ReplayEngineError> for IotaError {
233 fn from(err: ReplayEngineError) -> Self {
234 IotaError::Unknown(format!("{err:#?}"))
235 }
236}
237
238impl From<IotaError> for ReplayEngineError {
239 fn from(err: IotaError) -> Self {
240 ReplayEngineError::IotaError { err }
241 }
242}
243impl From<IotaRpcError> for ReplayEngineError {
244 fn from(err: IotaRpcError) -> Self {
245 match err {
246 IotaRpcError::Rpc(JsonRpseeError::RequestTimeout) => {
247 ReplayEngineError::IotaRpcRequestTimeout
248 }
249 _ => ReplayEngineError::IotaRpcError {
250 err: format!("{err:?}"),
251 },
252 }
253 }
254}
255
256impl From<UserInputError> for ReplayEngineError {
257 fn from(err: UserInputError) -> Self {
258 ReplayEngineError::UserInputError { err }
259 }
260}
261
262impl From<anyhow::Error> for ReplayEngineError {
263 fn from(err: anyhow::Error) -> Self {
264 ReplayEngineError::GeneralError {
265 err: format!("{err:#?}"),
266 }
267 }
268}
269
270#[derive(Debug)]
272#[expect(clippy::large_enum_variant)]
273pub enum ExecutionStoreEvent {
274 BackingPackageGetPackageObject {
275 package_id: ObjectID,
276 result: IotaResult<Option<Object>>,
277 },
278 ChildObjectResolverStoreReadChildObject {
279 parent: ObjectID,
280 child: ObjectID,
281 result: IotaResult<Option<Object>>,
282 },
283 ResourceResolverGetResource {
284 address: AccountAddress,
285 typ: StructTag,
286 result: IotaResult<Option<Vec<u8>>>,
287 },
288 ModuleResolverGetModule {
289 module_id: ModuleId,
290 result: IotaResult<Option<Vec<u8>>>,
291 },
292 ObjectStoreGetObject {
293 object_id: ObjectID,
294 result: IotaResult<Option<Object>>,
295 },
296 ObjectStoreGetObjectByKey {
297 object_id: ObjectID,
298 version: VersionNumber,
299 result: IotaResult<Option<Object>>,
300 },
301 GetModuleGetModuleByModuleId {
302 id: ModuleId,
303 result: IotaResult<Option<CompiledModule>>,
304 },
305 ReceiveObject {
306 owner: ObjectID,
307 receive: ObjectID,
308 receive_at_version: SequenceNumber,
309 result: IotaResult<Option<Object>>,
310 },
311}