1use std::fmt;
6
7use anyhow::Result;
8use enum_dispatch::enum_dispatch;
9use iota_protocol_config::{ProtocolConfig, ProtocolVersion};
10use iota_sdk_types::{Identifier, ObjectId};
11use serde::{Deserialize, Serialize, de::DeserializeOwned};
12
13use self::{
14 iota_system_state_inner_v1::{IotaSystemStateV1, ValidatorV1},
15 iota_system_state_inner_v2::IotaSystemStateV2,
16 iota_system_state_summary::{IotaSystemStateSummary, IotaValidatorSummary},
17};
18#[cfg(not(target_arch = "wasm32"))]
19use crate::iota_system_state::epoch_start_iota_system_state::EpochStartSystemState;
20use crate::{
21 MoveTypeTagTrait,
22 committee::CommitteeWithNetworkMetadata,
23 dynamic_field::{Field, get_dynamic_field_from_store, get_dynamic_field_object_from_store},
24 error::IotaError,
25 id::UID,
26 object::{MoveObject, MoveObjectExt, Object},
27 storage::ObjectStore,
28 versioned::Versioned,
29};
30
31#[cfg(not(target_arch = "wasm32"))]
35pub mod epoch_start_iota_system_state;
36pub mod iota_system_state_inner_v1;
37pub mod iota_system_state_inner_v2;
38pub mod iota_system_state_summary;
39
40#[cfg(msim)]
41mod simtest_iota_system_state_inner;
42#[cfg(msim)]
43use self::simtest_iota_system_state_inner::{
44 SimTestIotaSystemStateDeepV1, SimTestIotaSystemStateShallowV1, SimTestIotaSystemStateV1,
45 SimTestValidatorDeepV1, SimTestValidatorV1,
46};
47
48pub const ADVANCE_EPOCH_FUNCTION_NAME: Identifier = Identifier::from_static("advance_epoch");
49pub const ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME: Identifier =
50 Identifier::from_static("advance_epoch_safe_mode");
51
52#[cfg(msim)]
53pub const IOTA_SYSTEM_STATE_SIM_TEST_V1: u64 = 18446744073709551605; #[cfg(msim)]
55pub const IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1: u64 = 18446744073709551606; #[cfg(msim)]
57pub const IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1: u64 = 18446744073709551607; #[derive(Debug, Serialize, Deserialize, Clone)]
68pub struct IotaSystemStateWrapper {
69 pub id: UID,
70 pub version: u64,
71}
72
73impl IotaSystemStateWrapper {
74 pub fn advance_epoch_safe_mode(
79 &self,
80 params: &AdvanceEpochParams,
81 object_store: &dyn ObjectStore,
82 protocol_config: &ProtocolConfig,
83 ) -> (Object, Object) {
84 let id = self.id.id.bytes;
85 let old_field_object = get_dynamic_field_object_from_store(object_store, id, &self.version)
86 .expect("Dynamic field object of wrapper should always be present in the object store");
87 let mut new_field_object = old_field_object.clone();
88 let move_object = new_field_object
89 .data
90 .as_struct_mut_opt()
91 .expect("Dynamic field object must be a Move object");
92 match self.version {
93 1 => {
94 Self::advance_epoch_safe_mode_impl::<IotaSystemStateV1>(
95 move_object,
96 params,
97 protocol_config,
98 );
99 }
100 2 => {
101 Self::advance_epoch_safe_mode_impl::<IotaSystemStateV2>(
102 move_object,
103 params,
104 protocol_config,
105 );
106 }
107 #[cfg(msim)]
108 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
109 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateV1>(
110 move_object,
111 params,
112 protocol_config,
113 );
114 }
115 #[cfg(msim)]
116 IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1 => {
117 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateShallowV1>(
118 move_object,
119 params,
120 protocol_config,
121 );
122 }
123 #[cfg(msim)]
124 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
125 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateDeepV1>(
126 move_object,
127 params,
128 protocol_config,
129 );
130 }
131 _ => unreachable!(),
132 }
133 (old_field_object, new_field_object)
134 }
135
136 fn advance_epoch_safe_mode_impl<T>(
137 move_object: &mut MoveObject,
138 params: &AdvanceEpochParams,
139 protocol_config: &ProtocolConfig,
140 ) where
141 T: Serialize + DeserializeOwned + IotaSystemStateTrait,
142 {
143 let mut field: Field<u64, T> =
144 bcs::from_bytes(move_object.contents()).expect("bcs deserialization should never fail");
145 tracing::info!(
146 "Advance epoch safe mode: current epoch: {}, protocol_version: {}, system_state_version: {}",
147 field.value.epoch(),
148 field.value.protocol_version(),
149 field.value.system_state_version()
150 );
151 field.value.advance_epoch_safe_mode(params);
152 tracing::info!(
153 "Safe mode activated. New epoch: {}, protocol_version: {}, system_state_version: {}",
154 field.value.epoch(),
155 field.value.protocol_version(),
156 field.value.system_state_version()
157 );
158 let new_contents = bcs::to_bytes(&field).expect("bcs serialization should never fail");
159 move_object
160 .update_contents(new_contents, protocol_config)
161 .expect("Update iota system object content cannot fail since it should be small");
162 }
163}
164
165#[enum_dispatch]
168pub trait IotaSystemStateTrait {
169 fn epoch(&self) -> u64;
170 fn reference_gas_price(&self) -> u64;
171 fn protocol_version(&self) -> u64;
172 fn system_state_version(&self) -> u64;
173 fn epoch_start_timestamp_ms(&self) -> u64;
174 fn epoch_duration_ms(&self) -> u64;
175 fn safe_mode(&self) -> bool;
176 fn advance_epoch_safe_mode(&mut self, params: &AdvanceEpochParams);
177 fn get_current_epoch_committee(&self) -> CommitteeWithNetworkMetadata;
178 fn get_pending_active_validators<S: ObjectStore + ?Sized>(
179 &self,
180 object_store: &S,
181 ) -> Result<Vec<IotaValidatorSummary>, IotaError>;
182 #[cfg(not(target_arch = "wasm32"))]
183 fn into_epoch_start_state(self) -> EpochStartSystemState;
184 fn into_iota_system_state_summary(self) -> IotaSystemStateSummary;
185}
186
187#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
193#[enum_dispatch(IotaSystemStateTrait)]
194pub enum IotaSystemState {
195 V1(IotaSystemStateV1),
196 V2(IotaSystemStateV2),
197 #[cfg(msim)]
198 SimTestV1(SimTestIotaSystemStateV1),
199 #[cfg(msim)]
200 SimTestShallowV1(SimTestIotaSystemStateShallowV1),
201 #[cfg(msim)]
202 SimTestDeepV1(SimTestIotaSystemStateDeepV1),
203}
204
205pub type IotaSystemStateInnerGenesis = IotaSystemStateV1;
207pub type IotaValidatorGenesis = ValidatorV1;
208
209impl IotaSystemState {
210 pub fn into_genesis_version_for_tooling(self) -> IotaSystemStateInnerGenesis {
216 match self {
217 IotaSystemState::V1(inner) => inner,
218 _ => unreachable!(),
220 }
221 }
222
223 pub fn version(&self) -> u64 {
224 self.system_state_version()
225 }
226}
227
228pub fn get_iota_system_state_wrapper(
229 object_store: &dyn ObjectStore,
230) -> Result<IotaSystemStateWrapper, IotaError> {
231 let wrapper = object_store
232 .try_get_object(&ObjectId::SYSTEM_STATE)?
233 .ok_or_else(|| {
235 IotaError::IotaSystemStateRead("IotaSystemStateWrapper object not found".to_owned())
236 })?;
237 let move_object = wrapper.data.as_struct_opt().ok_or_else(|| {
238 IotaError::IotaSystemStateRead(
239 "IotaSystemStateWrapper object must be a Move object".to_owned(),
240 )
241 })?;
242 let result = bcs::from_bytes::<IotaSystemStateWrapper>(move_object.contents())
243 .map_err(|err| IotaError::IotaSystemStateRead(err.to_string()))?;
244 Ok(result)
245}
246
247pub fn get_iota_system_state(object_store: &dyn ObjectStore) -> Result<IotaSystemState, IotaError> {
248 let wrapper = get_iota_system_state_wrapper(object_store)?;
249 let id = wrapper.id.id.bytes;
250 match wrapper.version {
251 1 => {
252 let result: IotaSystemStateV1 =
253 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
254 |err| {
255 IotaError::DynamicFieldRead(format!(
256 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
257 id, wrapper.version, err
258 ))
259 },
260 )?;
261 Ok(IotaSystemState::V1(result))
262 }
263 2 => {
264 let result: IotaSystemStateV2 =
265 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
266 |err| {
267 IotaError::DynamicFieldRead(format!(
268 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
269 id, wrapper.version, err
270 ))
271 },
272 )?;
273 Ok(IotaSystemState::V2(result))
274 }
275 #[cfg(msim)]
276 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
277 let result: SimTestIotaSystemStateV1 =
278 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
279 |err| {
280 IotaError::DynamicFieldRead(format!(
281 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
282 id, wrapper.version, err
283 ))
284 },
285 )?;
286 Ok(IotaSystemState::SimTestV1(result))
287 }
288 #[cfg(msim)]
289 IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1 => {
290 let result: SimTestIotaSystemStateShallowV1 =
291 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
292 |err| {
293 IotaError::DynamicFieldRead(format!(
294 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
295 id, wrapper.version, err
296 ))
297 },
298 )?;
299 Ok(IotaSystemState::SimTestShallowV1(result))
300 }
301 #[cfg(msim)]
302 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
303 let result: SimTestIotaSystemStateDeepV1 =
304 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
305 |err| {
306 IotaError::DynamicFieldRead(format!(
307 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
308 id, wrapper.version, err
309 ))
310 },
311 )?;
312 Ok(IotaSystemState::SimTestDeepV1(result))
313 }
314 _ => Err(IotaError::IotaSystemStateRead(format!(
315 "Unsupported IotaSystemState version: {}",
316 wrapper.version
317 ))),
318 }
319}
320
321pub fn get_validator_from_table<K>(
326 object_store: &dyn ObjectStore,
327 table_id: ObjectId,
328 key: &K,
329 protocol_version: Option<u64>,
330) -> Result<IotaValidatorSummary, IotaError>
331where
332 K: MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug,
333{
334 let field: Validator =
335 get_dynamic_field_from_store(object_store, table_id, key).map_err(|err| {
336 IotaError::IotaSystemStateRead(format!(
337 "Failed to load validator wrapper from table: {err:?}"
338 ))
339 })?;
340 let versioned = field.inner;
341 let version = versioned.version;
342 match version {
343 1 => {
344 let validator: ValidatorV1 =
345 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
346 .map_err(|err| {
347 IotaError::IotaSystemStateRead(format!(
348 "Failed to load inner validator from the wrapper: {err:?}"
349 ))
350 })?;
351 Ok(validator.into_iota_validator_summary(protocol_version))
352 }
353 #[cfg(msim)]
354 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
355 let validator: SimTestValidatorV1 =
356 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
357 .map_err(|err| {
358 IotaError::IotaSystemStateRead(format!(
359 "Failed to load inner validator from the wrapper: {:?}",
360 err
361 ))
362 })?;
363 Ok(validator.into_iota_validator_summary())
364 }
365 #[cfg(msim)]
366 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
367 let validator: SimTestValidatorDeepV1 =
368 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
369 .map_err(|err| {
370 IotaError::IotaSystemStateRead(format!(
371 "Failed to load inner validator from the wrapper: {:?}",
372 err
373 ))
374 })?;
375 Ok(validator.into_iota_validator_summary())
376 }
377 _ => Err(IotaError::IotaSystemStateRead(format!(
378 "Unsupported Validator version: {version}"
379 ))),
380 }
381}
382
383pub fn get_validators_from_table_vec<S, ValidatorType>(
384 object_store: &S,
385 table_id: ObjectId,
386 table_size: u64,
387) -> Result<Vec<ValidatorType>, IotaError>
388where
389 S: ObjectStore + ?Sized,
390 ValidatorType: Serialize + DeserializeOwned,
391{
392 let mut validators = vec![];
393 for i in 0..table_size {
394 let validator: ValidatorType = get_dynamic_field_from_store(&object_store, table_id, &i)
395 .map_err(|err| {
396 IotaError::IotaSystemStateRead(format!(
397 "Failed to load validator from table: {err:?}"
398 ))
399 })?;
400 validators.push(validator);
401 }
402 Ok(validators)
403}
404
405#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)]
406pub struct PoolTokenExchangeRate {
407 iota_amount: u64,
408 pool_token_amount: u64,
409}
410
411impl PoolTokenExchangeRate {
412 pub fn rate(&self) -> f64 {
414 if self.iota_amount == 0 {
415 1_f64
416 } else {
417 self.pool_token_amount as f64 / self.iota_amount as f64
418 }
419 }
420
421 pub fn new_for_testing(iota_amount: u64, pool_token_amount: u64) -> Self {
422 Self {
423 iota_amount,
424 pool_token_amount,
425 }
426 }
427}
428
429#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
430pub struct Validator {
431 pub inner: Versioned,
432}
433
434#[derive(Debug)]
435pub struct AdvanceEpochParams {
436 pub epoch: u64,
437 pub next_protocol_version: ProtocolVersion,
438 pub validator_subsidy: u64,
439 pub storage_charge: u64,
440 pub computation_charge: u64,
441 pub computation_charge_burned: u64,
442 pub storage_rebate: u64,
443 pub non_refundable_storage_fee: u64,
444 pub reward_slashing_rate: u64,
445 pub epoch_start_timestamp_ms: u64,
446 pub max_committee_members_count: u64,
447 pub eligible_active_validators: Vec<u64>,
448 pub scores: Vec<u64>,
449 pub adjust_rewards_by_score: bool,
450}
451
452#[cfg(msim)]
453pub mod advance_epoch_result_injection {
454 use std::cell::RefCell;
455
456 use crate::{
457 committee::EpochId,
458 error::{ExecutionError, ExecutionErrorKind},
459 };
460
461 thread_local! {
462 static OVERRIDE: RefCell<Option<(EpochId, EpochId)>> = const { RefCell::new(None) };
464 }
465
466 pub fn set_override(value: Option<(EpochId, EpochId)>) {
469 OVERRIDE.with(|o| *o.borrow_mut() = value);
470 }
471
472 pub fn maybe_modify_result(
476 result: Result<(), ExecutionError>,
477 current_epoch: EpochId,
478 ) -> Result<(), ExecutionError> {
479 if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
480 if current_epoch >= start && current_epoch < end {
481 return Err::<(), ExecutionError>(ExecutionError::new(
482 ExecutionErrorKind::FunctionNotFound,
483 None,
484 ));
485 }
486 }
487 result
488 }
489}