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_sdk_types::{
11 Address, ObjectDigest, ObjectId, ObjectReference, SenderSignedTransaction, TransactionDigest,
12 TransactionKind, Version,
13};
14use iota_types::{
15 base_types::VersionNumber,
16 error::{IotaError, IotaResult, UserInputError},
17 object::Object,
18 transaction::InputObjectKind,
19};
20use jsonrpsee::core::ClientError as JsonRpseeError;
21use move_binary_format::CompiledModule;
22use move_core_types::{
23 account_address::AccountAddress,
24 language_storage::{ModuleId, StructTag},
25};
26use serde::{Deserialize, Serialize};
27use thiserror::Error;
28use tokio::time::Duration;
29use tracing::warn;
30
31use crate::config::ReplayableNetworkConfigSet;
32
33pub(crate) const RPC_TIMEOUT_ERR_SLEEP_RETRY_PERIOD: Duration = Duration::from_millis(100_000);
35pub(crate) const RPC_TIMEOUT_ERR_NUM_RETRIES: u32 = 3;
36pub(crate) const MAX_CONCURRENT_REQUESTS: usize = 1_000;
37
38pub(crate) const EPOCH_CHANGE_STRUCT_TAGS: [&str; 2] = [
40 "0x3::iota_system_state_inner::SystemEpochInfoEventV2",
41 "0x3::iota_system_state_inner::SystemEpochInfoEventV1",
42];
43
44#[derive(Clone, Debug, Serialize, Deserialize)]
47pub struct OnChainTransactionInfo {
48 pub tx_digest: TransactionDigest,
49 pub sender_signed_data: SenderSignedTransaction,
50 pub sender: Address,
51 pub input_objects: Vec<InputObjectKind>,
52 pub kind: TransactionKind,
53 pub modified_at_versions: Vec<(ObjectId, Version)>,
54 pub shared_object_refs: Vec<ObjectReference>,
55 pub gas: Vec<ObjectReference>,
56 #[serde(default)]
57 pub gas_owner: Option<Address>,
58 pub gas_budget: u64,
59 pub gas_price: u64,
60 pub executed_epoch: u64,
61 pub dependencies: Vec<TransactionDigest>,
62 #[serde(skip)]
63 pub receiving_objs: Vec<(ObjectId, Version)>,
64 #[serde(skip)]
65 pub config_objects: Vec<(ObjectId, Version)>,
66 pub effects: IotaTransactionBlockEffects,
73 pub protocol_version: ProtocolVersion,
74 pub epoch_start_timestamp: u64,
75 pub reference_gas_price: u64,
76 #[serde(default = "unspecified_chain")]
77 pub chain: Chain,
78}
79
80fn unspecified_chain() -> Chain {
81 warn!("Unable to determine chain id. Defaulting to unknown");
82 Chain::Unknown
83}
84
85#[derive(Debug, Error, Clone)]
86pub enum ReplayEngineError {
87 #[error("IotaError: {:#?}", err)]
88 IotaError { err: IotaError },
89
90 #[error("IotaRpcError: {:#?}", err)]
91 IotaRpcError { err: String },
92
93 #[error("IotaObjectResponseError: {:#?}", err)]
94 IotaObjectResponseError { err: IotaObjectResponseError },
95
96 #[error("UserInputError: {:#?}", err)]
97 UserInputError { err: UserInputError },
98
99 #[error("GeneralError: {:#?}", err)]
100 GeneralError { err: String },
101
102 #[error("IotaRpcRequestTimeout")]
103 IotaRpcRequestTimeout,
104
105 #[error("ObjectNotExist: {:#?}", id)]
106 ObjectNotExist { id: ObjectId },
107
108 #[error("ObjectVersionNotFound: {:#?} version {}", id, version)]
109 ObjectVersionNotFound { id: ObjectId, version: Version },
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: Version,
120 latest_version: Version,
121 },
122
123 #[error(
124 "ObjectDeleted: {:#?} at version {:#?} digest {:#?}",
125 id,
126 version,
127 digest
128 )]
129 ObjectDeleted {
130 id: ObjectId,
131 version: Version,
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<Version>,
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: version.into(),
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: Version,
309 result: IotaResult<Option<Object>>,
310 },
311}