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_opt_mut_struct()
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 pub fn for_testing(epoch: u64, protocol_version: u64) -> Self {
232 use iota_sdk_types::ObjectId;
233
234 use crate::{
235 balance::{Balance, Supply},
236 coin::TreasuryCap,
237 collection_types::{Bag, Table, TableVec, VecMap},
238 gas_coin::IotaTreasuryCap,
239 id::UID,
240 iota_system_state::iota_system_state_inner_v1::{
241 IotaSystemStateV1, StorageFundV1, SystemParametersV1, ValidatorSetV1,
242 },
243 system_admin_cap::IotaSystemAdminCap,
244 };
245 IotaSystemState::V1(IotaSystemStateV1 {
246 epoch,
247 protocol_version,
248 system_state_version: 1,
249 iota_treasury_cap: IotaTreasuryCap {
250 inner: TreasuryCap {
251 id: UID::new(ObjectId::ZERO),
252 total_supply: Supply { value: 0 },
253 },
254 },
255 validators: ValidatorSetV1 {
256 total_stake: 0,
257 active_validators: Vec::new(),
258 pending_active_validators: TableVec::default(),
259 pending_removals: Vec::new(),
260 staking_pool_mappings: Table::default(),
261 inactive_validators: Table::default(),
262 validator_candidates: Table::default(),
263 at_risk_validators: VecMap {
264 contents: Vec::new(),
265 },
266 extra_fields: Bag::default(),
267 },
268 storage_fund: StorageFundV1 {
269 total_object_storage_rebates: Balance::new(0),
270 non_refundable_balance: Balance::new(0),
271 },
272 parameters: SystemParametersV1 {
273 epoch_duration_ms: 0,
274 min_validator_count: 0,
275 max_validator_count: 0,
276 min_validator_joining_stake: 0,
277 validator_low_stake_threshold: 0,
278 validator_very_low_stake_threshold: 0,
279 validator_low_stake_grace_period: 0,
280 extra_fields: Bag::default(),
281 },
282 iota_system_admin_cap: IotaSystemAdminCap::default(),
283 reference_gas_price: 0,
284 validator_report_records: VecMap {
285 contents: Vec::new(),
286 },
287 safe_mode: false,
288 safe_mode_storage_charges: Balance::new(0),
289 safe_mode_computation_rewards: Balance::new(0),
290 safe_mode_storage_rebates: 0,
291 safe_mode_non_refundable_storage_fee: 0,
292 epoch_start_timestamp_ms: 0,
293 extra_fields: Bag::default(),
294 })
295 }
296}
297
298fn get_iota_system_state_wrapper_with_object(
301 object_store: &dyn ObjectStore,
302) -> Result<(Object, IotaSystemStateWrapper), IotaError> {
303 let wrapper_object = object_store
304 .try_get_object(&ObjectId::SYSTEM_STATE)?
305 .ok_or_else(|| {
307 IotaError::IotaSystemStateRead("IotaSystemStateWrapper object not found".to_owned())
308 })?;
309 let move_object = wrapper_object.data.as_opt_struct().ok_or_else(|| {
310 IotaError::IotaSystemStateRead(
311 "IotaSystemStateWrapper object must be a Move object".to_owned(),
312 )
313 })?;
314 let wrapper = bcs::from_bytes::<IotaSystemStateWrapper>(move_object.contents())
315 .map_err(|err| IotaError::IotaSystemStateRead(err.to_string()))?;
316 Ok((wrapper_object, wrapper))
317}
318
319pub fn get_iota_system_state_wrapper(
320 object_store: &dyn ObjectStore,
321) -> Result<IotaSystemStateWrapper, IotaError> {
322 Ok(get_iota_system_state_wrapper_with_object(object_store)?.1)
323}
324
325pub fn get_iota_system_state(object_store: &dyn ObjectStore) -> Result<IotaSystemState, IotaError> {
326 let wrapper = get_iota_system_state_wrapper(object_store)?;
327 let id = wrapper.id.id.bytes;
328 match wrapper.version {
329 1 => {
330 let result: IotaSystemStateV1 =
331 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
332 |err| {
333 IotaError::DynamicFieldRead(format!(
334 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
335 id, wrapper.version, err
336 ))
337 },
338 )?;
339 Ok(IotaSystemState::V1(result))
340 }
341 2 => {
342 let result: IotaSystemStateV2 =
343 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
344 |err| {
345 IotaError::DynamicFieldRead(format!(
346 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
347 id, wrapper.version, err
348 ))
349 },
350 )?;
351 Ok(IotaSystemState::V2(result))
352 }
353 #[cfg(msim)]
354 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
355 let result: SimTestIotaSystemStateV1 =
356 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
357 |err| {
358 IotaError::DynamicFieldRead(format!(
359 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
360 id, wrapper.version, err
361 ))
362 },
363 )?;
364 Ok(IotaSystemState::SimTestV1(result))
365 }
366 #[cfg(msim)]
367 IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1 => {
368 let result: SimTestIotaSystemStateShallowV1 =
369 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
370 |err| {
371 IotaError::DynamicFieldRead(format!(
372 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
373 id, wrapper.version, err
374 ))
375 },
376 )?;
377 Ok(IotaSystemState::SimTestShallowV1(result))
378 }
379 #[cfg(msim)]
380 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
381 let result: SimTestIotaSystemStateDeepV1 =
382 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
383 |err| {
384 IotaError::DynamicFieldRead(format!(
385 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
386 id, wrapper.version, err
387 ))
388 },
389 )?;
390 Ok(IotaSystemState::SimTestDeepV1(result))
391 }
392 _ => Err(IotaError::IotaSystemStateRead(format!(
393 "Unsupported IotaSystemState version: {}",
394 wrapper.version
395 ))),
396 }
397}
398
399pub fn get_iota_system_state_objects(
405 object_store: &dyn ObjectStore,
406) -> Result<[Object; 2], IotaError> {
407 let (wrapper_object, wrapper) = get_iota_system_state_wrapper_with_object(object_store)?;
408 let inner_object =
411 get_dynamic_field_object_from_store(object_store, wrapper.id.id.bytes, &wrapper.version)?;
412 Ok([wrapper_object, inner_object])
413}
414
415pub fn get_validator_from_table<K>(
420 object_store: &dyn ObjectStore,
421 table_id: ObjectId,
422 key: &K,
423 protocol_version: Option<u64>,
424) -> Result<IotaValidatorSummary, IotaError>
425where
426 K: MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug,
427{
428 let field: Validator =
429 get_dynamic_field_from_store(object_store, table_id, key).map_err(|err| {
430 IotaError::IotaSystemStateRead(format!(
431 "Failed to load validator wrapper from table: {err:?}"
432 ))
433 })?;
434 let versioned = field.inner;
435 let version = versioned.version;
436 match version {
437 1 => {
438 let validator: ValidatorV1 =
439 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
440 .map_err(|err| {
441 IotaError::IotaSystemStateRead(format!(
442 "Failed to load inner validator from the wrapper: {err:?}"
443 ))
444 })?;
445 Ok(validator.into_iota_validator_summary(protocol_version))
446 }
447 #[cfg(msim)]
448 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
449 let validator: SimTestValidatorV1 =
450 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
451 .map_err(|err| {
452 IotaError::IotaSystemStateRead(format!(
453 "Failed to load inner validator from the wrapper: {err:?}"
454 ))
455 })?;
456 Ok(validator.into_iota_validator_summary())
457 }
458 #[cfg(msim)]
459 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
460 let validator: SimTestValidatorDeepV1 =
461 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
462 .map_err(|err| {
463 IotaError::IotaSystemStateRead(format!(
464 "Failed to load inner validator from the wrapper: {err:?}"
465 ))
466 })?;
467 Ok(validator.into_iota_validator_summary())
468 }
469 _ => Err(IotaError::IotaSystemStateRead(format!(
470 "Unsupported Validator version: {version}"
471 ))),
472 }
473}
474
475pub fn get_validators_from_table_vec<S, ValidatorType>(
476 object_store: &S,
477 table_id: ObjectId,
478 table_size: u64,
479) -> Result<Vec<ValidatorType>, IotaError>
480where
481 S: ObjectStore + ?Sized,
482 ValidatorType: Serialize + DeserializeOwned,
483{
484 let mut validators = vec![];
485 for i in 0..table_size {
486 let validator: ValidatorType = get_dynamic_field_from_store(&object_store, table_id, &i)
487 .map_err(|err| {
488 IotaError::IotaSystemStateRead(format!(
489 "Failed to load validator from table: {err:?}"
490 ))
491 })?;
492 validators.push(validator);
493 }
494 Ok(validators)
495}
496
497#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)]
498pub struct PoolTokenExchangeRate {
499 iota_amount: u64,
500 pool_token_amount: u64,
501}
502
503impl PoolTokenExchangeRate {
504 pub fn rate(&self) -> f64 {
506 if self.iota_amount == 0 {
507 1_f64
508 } else {
509 self.pool_token_amount as f64 / self.iota_amount as f64
510 }
511 }
512
513 pub fn new_for_testing(iota_amount: u64, pool_token_amount: u64) -> Self {
514 Self {
515 iota_amount,
516 pool_token_amount,
517 }
518 }
519}
520
521#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
522pub struct Validator {
523 pub inner: Versioned,
524}
525
526#[derive(Debug)]
527pub struct AdvanceEpochParams {
528 pub epoch: u64,
529 pub next_protocol_version: ProtocolVersion,
530 pub validator_subsidy: u64,
531 pub storage_charge: u64,
532 pub computation_charge: u64,
533 pub computation_charge_burned: u64,
534 pub storage_rebate: u64,
535 pub non_refundable_storage_fee: u64,
536 pub reward_slashing_rate: u64,
537 pub epoch_start_timestamp_ms: u64,
538 pub max_committee_members_count: u64,
539 pub eligible_active_validators: Vec<u64>,
540 pub scores: Vec<u64>,
541 pub adjust_rewards_by_score: bool,
542}
543
544#[cfg(msim)]
545pub mod advance_epoch_result_injection {
546 use std::cell::RefCell;
547
548 use crate::{
549 committee::EpochId,
550 error::{ExecutionError, ExecutionErrorKind},
551 };
552
553 thread_local! {
554 static OVERRIDE: RefCell<Option<(EpochId, EpochId)>> = const { RefCell::new(None) };
556 }
557
558 pub fn set_override(value: Option<(EpochId, EpochId)>) {
561 OVERRIDE.with(|o| *o.borrow_mut() = value);
562 }
563
564 pub fn maybe_modify_result(
568 result: Result<(), ExecutionError>,
569 current_epoch: EpochId,
570 ) -> Result<(), ExecutionError> {
571 if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
572 if current_epoch >= start && current_epoch < end {
573 return Err::<(), ExecutionError>(ExecutionError::new(
574 ExecutionErrorKind::FunctionNotFound,
575 None,
576 ));
577 }
578 }
579 result
580 }
581}