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