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