1use std::fmt;
6
7use anyhow::Result;
8use enum_dispatch::enum_dispatch;
9use iota_protocol_config::{ProtocolConfig, ProtocolVersion};
10use iota_sdk_types::{Identifier, MoveStruct, 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::{MoveStructExt, 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_struct = 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_struct,
96 params,
97 protocol_config,
98 );
99 }
100 2 => {
101 Self::advance_epoch_safe_mode_impl::<IotaSystemStateV2>(
102 move_struct,
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_struct,
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_struct,
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_struct,
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_struct: &mut MoveStruct,
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_struct.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_struct
160 .update_contents_advance_epoch_safe_mode(new_contents, protocol_config)
161 .expect(
162 "Update iota system object content cannot fail since it should be small or unbounded",
163 );
164 }
165}
166
167#[enum_dispatch]
170pub trait IotaSystemStateTrait {
171 fn epoch(&self) -> u64;
172 fn reference_gas_price(&self) -> u64;
173 fn protocol_version(&self) -> u64;
174 fn system_state_version(&self) -> u64;
175 fn epoch_start_timestamp_ms(&self) -> u64;
176 fn epoch_duration_ms(&self) -> u64;
177 fn safe_mode(&self) -> bool;
178 fn advance_epoch_safe_mode(&mut self, params: &AdvanceEpochParams);
179 fn get_current_epoch_committee(&self) -> CommitteeWithNetworkMetadata;
180 fn get_pending_active_validators<S: ObjectStore + ?Sized>(
181 &self,
182 object_store: &S,
183 ) -> Result<Vec<IotaValidatorSummary>, IotaError>;
184 #[cfg(not(target_arch = "wasm32"))]
185 fn into_epoch_start_state(self) -> EpochStartSystemState;
186 fn into_iota_system_state_summary(self) -> IotaSystemStateSummary;
187}
188
189#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
195#[enum_dispatch(IotaSystemStateTrait)]
196pub enum IotaSystemState {
197 V1(IotaSystemStateV1),
198 V2(IotaSystemStateV2),
199 #[cfg(msim)]
200 SimTestV1(SimTestIotaSystemStateV1),
201 #[cfg(msim)]
202 SimTestShallowV1(SimTestIotaSystemStateShallowV1),
203 #[cfg(msim)]
204 SimTestDeepV1(SimTestIotaSystemStateDeepV1),
205}
206
207pub type IotaSystemStateInnerGenesis = IotaSystemStateV1;
209pub type IotaValidatorGenesis = ValidatorV1;
210
211impl IotaSystemState {
212 pub fn into_genesis_version_for_tooling(self) -> IotaSystemStateInnerGenesis {
218 match self {
219 IotaSystemState::V1(inner) => inner,
220 _ => unreachable!(),
222 }
223 }
224
225 pub fn version(&self) -> u64 {
226 self.system_state_version()
227 }
228
229 pub fn for_testing(epoch: u64, protocol_version: u64) -> Self {
234 use iota_sdk_types::ObjectId;
235
236 use crate::{
237 balance::{Balance, Supply},
238 coin::TreasuryCap,
239 collection_types::{Bag, Table, TableVec, VecMap},
240 gas_coin::IotaTreasuryCap,
241 id::UID,
242 iota_system_state::iota_system_state_inner_v1::{
243 IotaSystemStateV1, StorageFundV1, SystemParametersV1, ValidatorSetV1,
244 },
245 system_admin_cap::IotaSystemAdminCap,
246 };
247 IotaSystemState::V1(IotaSystemStateV1 {
248 epoch,
249 protocol_version,
250 system_state_version: 1,
251 iota_treasury_cap: IotaTreasuryCap {
252 inner: TreasuryCap {
253 id: UID::new(ObjectId::ZERO),
254 total_supply: Supply { value: 0 },
255 },
256 },
257 validators: ValidatorSetV1 {
258 total_stake: 0,
259 active_validators: Vec::new(),
260 pending_active_validators: TableVec::default(),
261 pending_removals: Vec::new(),
262 staking_pool_mappings: Table::default(),
263 inactive_validators: Table::default(),
264 validator_candidates: Table::default(),
265 at_risk_validators: VecMap {
266 contents: Vec::new(),
267 },
268 extra_fields: Bag::default(),
269 },
270 storage_fund: StorageFundV1 {
271 total_object_storage_rebates: Balance::new(0),
272 non_refundable_balance: Balance::new(0),
273 },
274 parameters: SystemParametersV1 {
275 epoch_duration_ms: 0,
276 min_validator_count: 0,
277 max_validator_count: 0,
278 min_validator_joining_stake: 0,
279 validator_low_stake_threshold: 0,
280 validator_very_low_stake_threshold: 0,
281 validator_low_stake_grace_period: 0,
282 extra_fields: Bag::default(),
283 },
284 iota_system_admin_cap: IotaSystemAdminCap::default(),
285 reference_gas_price: 0,
286 validator_report_records: VecMap {
287 contents: Vec::new(),
288 },
289 safe_mode: false,
290 safe_mode_storage_charges: Balance::new(0),
291 safe_mode_computation_rewards: Balance::new(0),
292 safe_mode_storage_rebates: 0,
293 safe_mode_non_refundable_storage_fee: 0,
294 epoch_start_timestamp_ms: 0,
295 extra_fields: Bag::default(),
296 })
297 }
298}
299
300fn get_iota_system_state_wrapper_with_object(
303 object_store: &dyn ObjectStore,
304) -> Result<(Object, IotaSystemStateWrapper), IotaError> {
305 let wrapper_object = object_store
306 .try_get_object(&ObjectId::SYSTEM_STATE)?
307 .ok_or_else(|| {
309 IotaError::IotaSystemStateRead("IotaSystemStateWrapper object not found".to_owned())
310 })?;
311 let move_object = wrapper_object.data.as_opt_struct().ok_or_else(|| {
312 IotaError::IotaSystemStateRead(
313 "IotaSystemStateWrapper object must be a Move object".to_owned(),
314 )
315 })?;
316 let wrapper = bcs::from_bytes::<IotaSystemStateWrapper>(move_object.contents())
317 .map_err(|err| IotaError::IotaSystemStateRead(err.to_string()))?;
318 Ok((wrapper_object, wrapper))
319}
320
321pub fn get_iota_system_state_wrapper(
322 object_store: &dyn ObjectStore,
323) -> Result<IotaSystemStateWrapper, IotaError> {
324 Ok(get_iota_system_state_wrapper_with_object(object_store)?.1)
325}
326
327pub fn get_iota_system_state(object_store: &dyn ObjectStore) -> Result<IotaSystemState, IotaError> {
328 let wrapper = get_iota_system_state_wrapper(object_store)?;
329 let id = wrapper.id.id.bytes;
330 match wrapper.version {
331 1 => {
332 let result: IotaSystemStateV1 =
333 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
334 |err| {
335 IotaError::DynamicFieldRead(format!(
336 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
337 id, wrapper.version, err
338 ))
339 },
340 )?;
341 Ok(IotaSystemState::V1(result))
342 }
343 2 => {
344 let result: IotaSystemStateV2 =
345 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
346 |err| {
347 IotaError::DynamicFieldRead(format!(
348 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
349 id, wrapper.version, err
350 ))
351 },
352 )?;
353 Ok(IotaSystemState::V2(result))
354 }
355 #[cfg(msim)]
356 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
357 let result: SimTestIotaSystemStateV1 =
358 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
359 |err| {
360 IotaError::DynamicFieldRead(format!(
361 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
362 id, wrapper.version, err
363 ))
364 },
365 )?;
366 Ok(IotaSystemState::SimTestV1(result))
367 }
368 #[cfg(msim)]
369 IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1 => {
370 let result: SimTestIotaSystemStateShallowV1 =
371 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
372 |err| {
373 IotaError::DynamicFieldRead(format!(
374 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
375 id, wrapper.version, err
376 ))
377 },
378 )?;
379 Ok(IotaSystemState::SimTestShallowV1(result))
380 }
381 #[cfg(msim)]
382 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
383 let result: SimTestIotaSystemStateDeepV1 =
384 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
385 |err| {
386 IotaError::DynamicFieldRead(format!(
387 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
388 id, wrapper.version, err
389 ))
390 },
391 )?;
392 Ok(IotaSystemState::SimTestDeepV1(result))
393 }
394 _ => Err(IotaError::IotaSystemStateRead(format!(
395 "Unsupported IotaSystemState version: {}",
396 wrapper.version
397 ))),
398 }
399}
400
401pub fn get_iota_system_state_objects(
407 object_store: &dyn ObjectStore,
408) -> Result<[Object; 2], IotaError> {
409 let (wrapper_object, wrapper) = get_iota_system_state_wrapper_with_object(object_store)?;
410 let inner_object =
413 get_dynamic_field_object_from_store(object_store, wrapper.id.id.bytes, &wrapper.version)?;
414 Ok([wrapper_object, inner_object])
415}
416
417pub fn get_validator_from_table<K>(
422 object_store: &dyn ObjectStore,
423 table_id: ObjectId,
424 key: &K,
425 protocol_version: Option<u64>,
426) -> Result<IotaValidatorSummary, IotaError>
427where
428 K: MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug,
429{
430 let field: Validator =
431 get_dynamic_field_from_store(object_store, table_id, key).map_err(|err| {
432 IotaError::IotaSystemStateRead(format!(
433 "Failed to load validator wrapper from table: {err:?}"
434 ))
435 })?;
436 let versioned = field.inner;
437 let version = versioned.version;
438 match version {
439 1 => {
440 let validator: ValidatorV1 =
441 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
442 .map_err(|err| {
443 IotaError::IotaSystemStateRead(format!(
444 "Failed to load inner validator from the wrapper: {err:?}"
445 ))
446 })?;
447 Ok(validator.into_iota_validator_summary(protocol_version))
448 }
449 #[cfg(msim)]
450 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
451 let validator: SimTestValidatorV1 =
452 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
453 .map_err(|err| {
454 IotaError::IotaSystemStateRead(format!(
455 "Failed to load inner validator from the wrapper: {err:?}"
456 ))
457 })?;
458 Ok(validator.into_iota_validator_summary())
459 }
460 #[cfg(msim)]
461 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
462 let validator: SimTestValidatorDeepV1 =
463 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
464 .map_err(|err| {
465 IotaError::IotaSystemStateRead(format!(
466 "Failed to load inner validator from the wrapper: {err:?}"
467 ))
468 })?;
469 Ok(validator.into_iota_validator_summary())
470 }
471 _ => Err(IotaError::IotaSystemStateRead(format!(
472 "Unsupported Validator version: {version}"
473 ))),
474 }
475}
476
477pub fn get_validators_from_table_vec<S, ValidatorType>(
478 object_store: &S,
479 table_id: ObjectId,
480 table_size: u64,
481) -> Result<Vec<ValidatorType>, IotaError>
482where
483 S: ObjectStore + ?Sized,
484 ValidatorType: Serialize + DeserializeOwned,
485{
486 let mut validators = vec![];
487 for i in 0..table_size {
488 let validator: ValidatorType = get_dynamic_field_from_store(&object_store, table_id, &i)
489 .map_err(|err| {
490 IotaError::IotaSystemStateRead(format!(
491 "Failed to load validator from table: {err:?}"
492 ))
493 })?;
494 validators.push(validator);
495 }
496 Ok(validators)
497}
498
499#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)]
500pub struct PoolTokenExchangeRate {
501 iota_amount: u64,
502 pool_token_amount: u64,
503}
504
505impl PoolTokenExchangeRate {
506 pub fn rate(&self) -> f64 {
508 if self.iota_amount == 0 {
509 1_f64
510 } else {
511 self.pool_token_amount as f64 / self.iota_amount as f64
512 }
513 }
514
515 pub fn new_for_testing(iota_amount: u64, pool_token_amount: u64) -> Self {
516 Self {
517 iota_amount,
518 pool_token_amount,
519 }
520 }
521}
522
523#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
524pub struct Validator {
525 pub inner: Versioned,
526}
527
528#[derive(Debug)]
529pub struct AdvanceEpochParams {
530 pub epoch: u64,
531 pub next_protocol_version: ProtocolVersion,
532 pub validator_subsidy: u64,
533 pub storage_charge: u64,
534 pub computation_charge: u64,
535 pub computation_charge_burned: u64,
536 pub storage_rebate: u64,
537 pub non_refundable_storage_fee: u64,
538 pub reward_slashing_rate: u64,
539 pub epoch_start_timestamp_ms: u64,
540 pub max_committee_members_count: u64,
541 pub eligible_active_validators: Vec<u64>,
542 pub scores: Vec<u64>,
543 pub adjust_rewards_by_score: bool,
544}
545
546#[cfg(msim)]
547pub mod advance_epoch_result_injection {
548 use std::cell::RefCell;
549
550 use crate::{
551 committee::EpochId,
552 error::{ExecutionError, ExecutionErrorKind},
553 execution::ResultWithTimings,
554 };
555
556 thread_local! {
557 static OVERRIDE: RefCell<Option<(EpochId, EpochId)>> = const { RefCell::new(None) };
559 }
560
561 pub fn set_override(value: Option<(EpochId, EpochId)>) {
564 OVERRIDE.with(|o| *o.borrow_mut() = value);
565 }
566
567 pub fn maybe_modify_result(
571 result: ResultWithTimings<(), ExecutionError>,
572 current_epoch: EpochId,
573 ) -> ResultWithTimings<(), ExecutionError> {
574 if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
575 if current_epoch >= start && current_epoch < end {
576 return Err((
577 ExecutionError::new(ExecutionErrorKind::FunctionNotFound, None),
578 vec![],
579 ));
580 }
581 }
582 result
583 }
584}