1use std::fmt;
6
7use anyhow::Result;
8use enum_dispatch::enum_dispatch;
9use iota_protocol_config::{ProtocolConfig, ProtocolVersion};
10use iota_sdk_move_types::{
11 iota_framework::dynamic_field::Field, iota_system::validator_wrapper::Validator,
12};
13use iota_sdk_types::{Identifier, MoveStruct, ObjectId};
14use serde::{Deserialize, Serialize, de::DeserializeOwned};
15
16use self::{
17 iota_system_state_inner_v1::{IotaSystemStateV1, ValidatorV1},
18 iota_system_state_inner_v2::IotaSystemStateV2,
19 iota_system_state_summary::{IotaSystemStateSummary, IotaValidatorSummary},
20};
21#[cfg(not(target_arch = "wasm32"))]
22use crate::iota_system_state::epoch_start_iota_system_state::EpochStartSystemState;
23use crate::{
24 MoveTypeTagTrait,
25 committee::CommitteeWithNetworkMetadata,
26 dynamic_field::{get_dynamic_field_from_store, get_dynamic_field_object_from_store},
27 error::IotaError,
28 id::UID,
29 object::{MoveStructExt, Object},
30 storage::ObjectStore,
31};
32
33#[cfg(not(target_arch = "wasm32"))]
37pub mod epoch_start_iota_system_state;
38pub mod iota_system_state_inner_v1;
39pub mod iota_system_state_inner_v2;
40pub mod iota_system_state_summary;
41
42#[cfg(msim)]
43mod simtest_iota_system_state_inner;
44#[cfg(msim)]
45use self::simtest_iota_system_state_inner::{
46 SimTestIotaSystemStateDeepV1, SimTestIotaSystemStateShallowV1, SimTestIotaSystemStateV1,
47 SimTestValidatorDeepV1, SimTestValidatorV1,
48};
49
50pub const ADVANCE_EPOCH_FUNCTION_NAME: Identifier = Identifier::from_static("advance_epoch");
51pub const ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME: Identifier =
52 Identifier::from_static("advance_epoch_safe_mode");
53
54#[cfg(msim)]
55pub const IOTA_SYSTEM_STATE_SIM_TEST_V1: u64 = 18446744073709551605; #[cfg(msim)]
57pub const IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1: u64 = 18446744073709551606; #[cfg(msim)]
59pub const IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1: u64 = 18446744073709551607; #[derive(Debug, Serialize, Deserialize, Clone)]
70pub struct IotaSystemStateWrapper {
71 pub id: UID,
72 pub version: u64,
73}
74
75impl IotaSystemStateWrapper {
76 pub fn advance_epoch_safe_mode(
81 &self,
82 params: &AdvanceEpochParams,
83 object_store: &dyn ObjectStore,
84 protocol_config: &ProtocolConfig,
85 ) -> (Object, Object) {
86 let id = self.id.id.bytes;
87 let old_field_object = get_dynamic_field_object_from_store(object_store, id, &self.version)
88 .expect("Dynamic field object of wrapper should always be present in the object store");
89 let mut new_field_object = old_field_object.clone();
90 let move_struct = new_field_object
91 .data
92 .as_opt_mut_struct()
93 .expect("Dynamic field object must be a Move object");
94 match self.version {
95 1 => {
96 Self::advance_epoch_safe_mode_impl::<IotaSystemStateV1>(
97 move_struct,
98 params,
99 protocol_config,
100 );
101 }
102 2 => {
103 Self::advance_epoch_safe_mode_impl::<IotaSystemStateV2>(
104 move_struct,
105 params,
106 protocol_config,
107 );
108 }
109 #[cfg(msim)]
110 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
111 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateV1>(
112 move_struct,
113 params,
114 protocol_config,
115 );
116 }
117 #[cfg(msim)]
118 IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1 => {
119 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateShallowV1>(
120 move_struct,
121 params,
122 protocol_config,
123 );
124 }
125 #[cfg(msim)]
126 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
127 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateDeepV1>(
128 move_struct,
129 params,
130 protocol_config,
131 );
132 }
133 _ => unreachable!(),
134 }
135 (old_field_object, new_field_object)
136 }
137
138 fn advance_epoch_safe_mode_impl<T>(
139 move_struct: &mut MoveStruct,
140 params: &AdvanceEpochParams,
141 protocol_config: &ProtocolConfig,
142 ) where
143 T: Serialize + DeserializeOwned + IotaSystemStateTrait,
144 {
145 let mut field: Field<u64, T> =
146 bcs::from_bytes(move_struct.contents()).expect("bcs deserialization should never fail");
147 tracing::info!(
148 "Advance epoch safe mode: current epoch: {}, protocol_version: {}, system_state_version: {}",
149 field.value.epoch(),
150 field.value.protocol_version(),
151 field.value.system_state_version()
152 );
153 field.value.advance_epoch_safe_mode(params);
154 tracing::info!(
155 "Safe mode activated. New epoch: {}, protocol_version: {}, system_state_version: {}",
156 field.value.epoch(),
157 field.value.protocol_version(),
158 field.value.system_state_version()
159 );
160 let new_contents = bcs::to_bytes(&field).expect("bcs serialization should never fail");
161 move_struct
162 .update_contents_advance_epoch_safe_mode(new_contents, protocol_config)
163 .expect(
164 "Update iota system object content cannot fail since it should be small or unbounded",
165 );
166 }
167}
168
169#[enum_dispatch]
172pub trait IotaSystemStateTrait {
173 fn epoch(&self) -> u64;
174 fn reference_gas_price(&self) -> u64;
175 fn protocol_version(&self) -> u64;
176 fn system_state_version(&self) -> u64;
177 fn epoch_start_timestamp_ms(&self) -> u64;
178 fn epoch_duration_ms(&self) -> u64;
179 fn safe_mode(&self) -> bool;
180 fn advance_epoch_safe_mode(&mut self, params: &AdvanceEpochParams);
181 fn get_current_epoch_committee(&self) -> CommitteeWithNetworkMetadata;
182 fn get_pending_active_validators<S: ObjectStore + ?Sized>(
183 &self,
184 object_store: &S,
185 ) -> Result<Vec<IotaValidatorSummary>, IotaError>;
186 #[cfg(not(target_arch = "wasm32"))]
187 fn into_epoch_start_state(self) -> EpochStartSystemState;
188 fn into_iota_system_state_summary(self) -> IotaSystemStateSummary;
189}
190
191#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
197#[enum_dispatch(IotaSystemStateTrait)]
198pub enum IotaSystemState {
199 V1(IotaSystemStateV1),
200 V2(IotaSystemStateV2),
201 #[cfg(msim)]
202 SimTestV1(SimTestIotaSystemStateV1),
203 #[cfg(msim)]
204 SimTestShallowV1(SimTestIotaSystemStateShallowV1),
205 #[cfg(msim)]
206 SimTestDeepV1(SimTestIotaSystemStateDeepV1),
207}
208
209pub type IotaSystemStateInnerGenesis = IotaSystemStateV1;
211pub type IotaValidatorGenesis = ValidatorV1;
212
213impl IotaSystemState {
214 pub fn into_genesis_version_for_tooling(self) -> IotaSystemStateInnerGenesis {
220 match self {
221 IotaSystemState::V1(inner) => inner,
222 _ => unreachable!(),
224 }
225 }
226
227 pub fn version(&self) -> u64 {
228 self.system_state_version()
229 }
230
231 pub fn for_testing(epoch: u64, protocol_version: u64) -> Self {
236 use iota_sdk_move_types::iota_framework::{
237 system_admin_cap::IotaSystemAdminCap, vec_map::VecMap,
238 };
239 use iota_sdk_types::ObjectId;
240
241 use crate::{
242 balance::{Balance, Supply},
243 coin::TreasuryCap,
244 collection_types::{Bag, Table, TableVec},
245 gas_coin::IotaTreasuryCap,
246 id::UID,
247 iota_system_state::iota_system_state_inner_v1::{
248 IotaSystemStateV1, StorageFundV1, SystemParametersV1, ValidatorSetV1,
249 },
250 };
251 IotaSystemState::V1(IotaSystemStateV1 {
252 epoch,
253 protocol_version,
254 system_state_version: 1,
255 iota_treasury_cap: IotaTreasuryCap {
256 inner: TreasuryCap {
257 id: UID::new(ObjectId::ZERO),
258 total_supply: Supply { value: 0 },
259 },
260 },
261 validators: ValidatorSetV1 {
262 total_stake: 0,
263 active_validators: Vec::new(),
264 pending_active_validators: TableVec::default(),
265 pending_removals: Vec::new(),
266 staking_pool_mappings: Table::default(),
267 inactive_validators: Table::default(),
268 validator_candidates: Table::default(),
269 at_risk_validators: VecMap {
270 contents: Vec::new(),
271 },
272 extra_fields: Bag::default(),
273 },
274 storage_fund: StorageFundV1 {
275 total_object_storage_rebates: Balance::new(0),
276 non_refundable_balance: Balance::new(0),
277 },
278 parameters: SystemParametersV1 {
279 epoch_duration_ms: 0,
280 min_validator_count: 0,
281 max_validator_count: 0,
282 min_validator_joining_stake: 0,
283 validator_low_stake_threshold: 0,
284 validator_very_low_stake_threshold: 0,
285 validator_low_stake_grace_period: 0,
286 extra_fields: Bag::default(),
287 },
288 iota_system_admin_cap: IotaSystemAdminCap::default(),
289 reference_gas_price: 0,
290 validator_report_records: VecMap {
291 contents: Vec::new(),
292 },
293 safe_mode: false,
294 safe_mode_storage_charges: Balance::new(0),
295 safe_mode_computation_rewards: Balance::new(0),
296 safe_mode_storage_rebates: 0,
297 safe_mode_non_refundable_storage_fee: 0,
298 epoch_start_timestamp_ms: 0,
299 extra_fields: Bag::default(),
300 })
301 }
302}
303
304fn get_iota_system_state_wrapper_with_object(
307 object_store: &dyn ObjectStore,
308) -> Result<(Object, IotaSystemStateWrapper), IotaError> {
309 let wrapper_object = object_store
310 .try_get_object(&ObjectId::SYSTEM_STATE)?
311 .ok_or_else(|| {
313 IotaError::IotaSystemStateRead("IotaSystemStateWrapper object not found".to_owned())
314 })?;
315 let move_object = wrapper_object.data.as_opt_struct().ok_or_else(|| {
316 IotaError::IotaSystemStateRead(
317 "IotaSystemStateWrapper object must be a Move object".to_owned(),
318 )
319 })?;
320 let wrapper = bcs::from_bytes::<IotaSystemStateWrapper>(move_object.contents())
321 .map_err(|err| IotaError::IotaSystemStateRead(err.to_string()))?;
322 Ok((wrapper_object, wrapper))
323}
324
325pub fn get_iota_system_state_wrapper(
326 object_store: &dyn ObjectStore,
327) -> Result<IotaSystemStateWrapper, IotaError> {
328 Ok(get_iota_system_state_wrapper_with_object(object_store)?.1)
329}
330
331pub fn get_iota_system_state(object_store: &dyn ObjectStore) -> Result<IotaSystemState, IotaError> {
332 let wrapper = get_iota_system_state_wrapper(object_store)?;
333 let id = wrapper.id.id.bytes;
334 match wrapper.version {
335 1 => {
336 let result: IotaSystemStateV1 =
337 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
338 |err| {
339 IotaError::DynamicFieldRead(format!(
340 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
341 id, wrapper.version, err
342 ))
343 },
344 )?;
345 Ok(IotaSystemState::V1(result))
346 }
347 2 => {
348 let result: IotaSystemStateV2 =
349 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
350 |err| {
351 IotaError::DynamicFieldRead(format!(
352 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
353 id, wrapper.version, err
354 ))
355 },
356 )?;
357 Ok(IotaSystemState::V2(result))
358 }
359 #[cfg(msim)]
360 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
361 let result: SimTestIotaSystemStateV1 =
362 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
363 |err| {
364 IotaError::DynamicFieldRead(format!(
365 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
366 id, wrapper.version, err
367 ))
368 },
369 )?;
370 Ok(IotaSystemState::SimTestV1(result))
371 }
372 #[cfg(msim)]
373 IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1 => {
374 let result: SimTestIotaSystemStateShallowV1 =
375 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
376 |err| {
377 IotaError::DynamicFieldRead(format!(
378 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
379 id, wrapper.version, err
380 ))
381 },
382 )?;
383 Ok(IotaSystemState::SimTestShallowV1(result))
384 }
385 #[cfg(msim)]
386 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
387 let result: SimTestIotaSystemStateDeepV1 =
388 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
389 |err| {
390 IotaError::DynamicFieldRead(format!(
391 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
392 id, wrapper.version, err
393 ))
394 },
395 )?;
396 Ok(IotaSystemState::SimTestDeepV1(result))
397 }
398 _ => Err(IotaError::IotaSystemStateRead(format!(
399 "Unsupported IotaSystemState version: {}",
400 wrapper.version
401 ))),
402 }
403}
404
405pub fn get_iota_system_state_objects(
411 object_store: &dyn ObjectStore,
412) -> Result<[Object; 2], IotaError> {
413 let (wrapper_object, wrapper) = get_iota_system_state_wrapper_with_object(object_store)?;
414 let inner_object =
417 get_dynamic_field_object_from_store(object_store, wrapper.id.id.bytes, &wrapper.version)?;
418 Ok([wrapper_object, inner_object])
419}
420
421pub fn get_validator_from_table<K>(
426 object_store: &dyn ObjectStore,
427 table_id: ObjectId,
428 key: &K,
429 protocol_version: Option<u64>,
430) -> Result<IotaValidatorSummary, IotaError>
431where
432 K: MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug,
433{
434 let field: Validator =
435 get_dynamic_field_from_store(object_store, table_id, key).map_err(|err| {
436 IotaError::IotaSystemStateRead(format!(
437 "Failed to load validator wrapper from table: {err:?}"
438 ))
439 })?;
440 let versioned = field.inner;
441 let version = versioned.version;
442 match version {
443 1 => {
444 let validator: ValidatorV1 =
445 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
446 .map_err(|err| {
447 IotaError::IotaSystemStateRead(format!(
448 "Failed to load inner validator from the wrapper: {err:?}"
449 ))
450 })?;
451 Ok(validator.into_iota_validator_summary(protocol_version))
452 }
453 #[cfg(msim)]
454 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
455 let validator: SimTestValidatorV1 =
456 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
457 .map_err(|err| {
458 IotaError::IotaSystemStateRead(format!(
459 "Failed to load inner validator from the wrapper: {err:?}"
460 ))
461 })?;
462 Ok(validator.into_iota_validator_summary())
463 }
464 #[cfg(msim)]
465 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
466 let validator: SimTestValidatorDeepV1 =
467 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
468 .map_err(|err| {
469 IotaError::IotaSystemStateRead(format!(
470 "Failed to load inner validator from the wrapper: {err:?}"
471 ))
472 })?;
473 Ok(validator.into_iota_validator_summary())
474 }
475 _ => Err(IotaError::IotaSystemStateRead(format!(
476 "Unsupported Validator version: {version}"
477 ))),
478 }
479}
480
481pub fn get_validators_from_table_vec<S, ValidatorType>(
482 object_store: &S,
483 table_id: ObjectId,
484 table_size: u64,
485) -> Result<Vec<ValidatorType>, IotaError>
486where
487 S: ObjectStore + ?Sized,
488 ValidatorType: Serialize + DeserializeOwned,
489{
490 let mut validators = vec![];
491 for i in 0..table_size {
492 let validator: ValidatorType = get_dynamic_field_from_store(&object_store, table_id, &i)
493 .map_err(|err| {
494 IotaError::IotaSystemStateRead(format!(
495 "Failed to load validator from table: {err:?}"
496 ))
497 })?;
498 validators.push(validator);
499 }
500 Ok(validators)
501}
502
503#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)]
504pub struct PoolTokenExchangeRate {
505 iota_amount: u64,
506 pool_token_amount: u64,
507}
508
509impl PoolTokenExchangeRate {
510 pub fn rate(&self) -> f64 {
512 if self.iota_amount == 0 {
513 1_f64
514 } else {
515 self.pool_token_amount as f64 / self.iota_amount as f64
516 }
517 }
518
519 pub fn new_for_testing(iota_amount: u64, pool_token_amount: u64) -> Self {
520 Self {
521 iota_amount,
522 pool_token_amount,
523 }
524 }
525}
526
527#[derive(Debug)]
528pub struct AdvanceEpochParams {
529 pub epoch: u64,
530 pub next_protocol_version: ProtocolVersion,
531 pub validator_subsidy: u64,
532 pub storage_charge: u64,
533 pub computation_charge: u64,
534 pub computation_charge_burned: u64,
535 pub storage_rebate: u64,
536 pub non_refundable_storage_fee: u64,
537 pub reward_slashing_rate: u64,
538 pub epoch_start_timestamp_ms: u64,
539 pub max_committee_members_count: u64,
540 pub eligible_active_validators: Vec<u64>,
541 pub scores: Vec<u64>,
542 pub adjust_rewards_by_score: bool,
543}
544
545#[cfg(msim)]
546pub mod advance_epoch_result_injection {
547 use std::cell::RefCell;
548
549 use crate::{
550 committee::EpochId,
551 error::{ExecutionError, ExecutionErrorKind},
552 execution::ResultWithTimings,
553 };
554
555 thread_local! {
556 static OVERRIDE: RefCell<Option<(EpochId, EpochId)>> = const { RefCell::new(None) };
558 }
559
560 pub fn set_override(value: Option<(EpochId, EpochId)>) {
563 OVERRIDE.with(|o| *o.borrow_mut() = value);
564 }
565
566 pub fn maybe_modify_result(
570 result: ResultWithTimings<(), ExecutionError>,
571 current_epoch: EpochId,
572 ) -> ResultWithTimings<(), ExecutionError> {
573 if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
574 if current_epoch >= start && current_epoch < end {
575 return Err((
576 ExecutionError::new(ExecutionErrorKind::FunctionNotFound, None),
577 vec![],
578 ));
579 }
580 }
581 result
582 }
583}