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