1use std::fmt;
6
7use anyhow::Result;
8use enum_dispatch::enum_dispatch;
9use iota_protocol_config::{ProtocolConfig, ProtocolVersion};
10use iota_sdk_types::Identifier;
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 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, MoveObjectExt, 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
44pub const ADVANCE_EPOCH_FUNCTION_NAME: Identifier = Identifier::from_static("advance_epoch");
45pub const ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME: Identifier =
46 Identifier::from_static("advance_epoch_safe_mode");
47
48#[cfg(msim)]
49pub const IOTA_SYSTEM_STATE_SIM_TEST_V1: u64 = 18446744073709551605; #[cfg(msim)]
51pub const IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1: u64 = 18446744073709551606; #[cfg(msim)]
53pub const IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1: u64 = 18446744073709551607; #[derive(Debug, Serialize, Deserialize, Clone)]
64pub struct IotaSystemStateWrapper {
65 pub id: UID,
66 pub version: u64,
67}
68
69impl IotaSystemStateWrapper {
70 pub fn advance_epoch_safe_mode(
75 &self,
76 params: &AdvanceEpochParams,
77 object_store: &dyn ObjectStore,
78 protocol_config: &ProtocolConfig,
79 ) -> (Object, Object) {
80 let id = self.id.id.bytes;
81 let old_field_object = get_dynamic_field_object_from_store(object_store, id, &self.version)
82 .expect("Dynamic field object of wrapper should always be present in the object store");
83 let mut new_field_object = old_field_object.clone();
84 let move_object = new_field_object
85 .data
86 .as_struct_mut_opt()
87 .expect("Dynamic field object must be a Move object");
88 match self.version {
89 1 => {
90 Self::advance_epoch_safe_mode_impl::<IotaSystemStateV1>(
91 move_object,
92 params,
93 protocol_config,
94 );
95 }
96 2 => {
97 Self::advance_epoch_safe_mode_impl::<IotaSystemStateV2>(
98 move_object,
99 params,
100 protocol_config,
101 );
102 }
103 #[cfg(msim)]
104 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
105 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateV1>(
106 move_object,
107 params,
108 protocol_config,
109 );
110 }
111 #[cfg(msim)]
112 IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1 => {
113 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateShallowV1>(
114 move_object,
115 params,
116 protocol_config,
117 );
118 }
119 #[cfg(msim)]
120 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
121 Self::advance_epoch_safe_mode_impl::<SimTestIotaSystemStateDeepV1>(
122 move_object,
123 params,
124 protocol_config,
125 );
126 }
127 _ => unreachable!(),
128 }
129 (old_field_object, new_field_object)
130 }
131
132 fn advance_epoch_safe_mode_impl<T>(
133 move_object: &mut MoveObject,
134 params: &AdvanceEpochParams,
135 protocol_config: &ProtocolConfig,
136 ) where
137 T: Serialize + DeserializeOwned + IotaSystemStateTrait,
138 {
139 let mut field: Field<u64, T> =
140 bcs::from_bytes(move_object.contents()).expect("bcs deserialization should never fail");
141 tracing::info!(
142 "Advance epoch safe mode: current epoch: {}, protocol_version: {}, system_state_version: {}",
143 field.value.epoch(),
144 field.value.protocol_version(),
145 field.value.system_state_version()
146 );
147 field.value.advance_epoch_safe_mode(params);
148 tracing::info!(
149 "Safe mode activated. New epoch: {}, protocol_version: {}, system_state_version: {}",
150 field.value.epoch(),
151 field.value.protocol_version(),
152 field.value.system_state_version()
153 );
154 let new_contents = bcs::to_bytes(&field).expect("bcs serialization should never fail");
155 move_object
156 .update_contents(new_contents, protocol_config)
157 .expect("Update iota system object content cannot fail since it should be small");
158 }
159}
160
161#[enum_dispatch]
164pub trait IotaSystemStateTrait {
165 fn epoch(&self) -> u64;
166 fn reference_gas_price(&self) -> u64;
167 fn protocol_version(&self) -> u64;
168 fn system_state_version(&self) -> u64;
169 fn epoch_start_timestamp_ms(&self) -> u64;
170 fn epoch_duration_ms(&self) -> u64;
171 fn safe_mode(&self) -> bool;
172 fn advance_epoch_safe_mode(&mut self, params: &AdvanceEpochParams);
173 fn get_current_epoch_committee(&self) -> CommitteeWithNetworkMetadata;
174 fn get_pending_active_validators<S: ObjectStore + ?Sized>(
175 &self,
176 object_store: &S,
177 ) -> Result<Vec<IotaValidatorSummary>, IotaError>;
178 fn into_epoch_start_state(self) -> EpochStartSystemState;
179 fn into_iota_system_state_summary(self) -> IotaSystemStateSummary;
180}
181
182#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
188#[enum_dispatch(IotaSystemStateTrait)]
189pub enum IotaSystemState {
190 V1(IotaSystemStateV1),
191 V2(IotaSystemStateV2),
192 #[cfg(msim)]
193 SimTestV1(SimTestIotaSystemStateV1),
194 #[cfg(msim)]
195 SimTestShallowV1(SimTestIotaSystemStateShallowV1),
196 #[cfg(msim)]
197 SimTestDeepV1(SimTestIotaSystemStateDeepV1),
198}
199
200pub type IotaSystemStateInnerGenesis = IotaSystemStateV1;
202pub type IotaValidatorGenesis = ValidatorV1;
203
204impl IotaSystemState {
205 pub fn into_genesis_version_for_tooling(self) -> IotaSystemStateInnerGenesis {
211 match self {
212 IotaSystemState::V1(inner) => inner,
213 _ => unreachable!(),
215 }
216 }
217
218 pub fn version(&self) -> u64 {
219 self.system_state_version()
220 }
221}
222
223pub fn get_iota_system_state_wrapper(
224 object_store: &dyn ObjectStore,
225) -> Result<IotaSystemStateWrapper, IotaError> {
226 let wrapper = object_store
227 .try_get_object(&ObjectID::SYSTEM_STATE)?
228 .ok_or_else(|| {
230 IotaError::IotaSystemStateRead("IotaSystemStateWrapper object not found".to_owned())
231 })?;
232 let move_object = wrapper.data.as_struct_opt().ok_or_else(|| {
233 IotaError::IotaSystemStateRead(
234 "IotaSystemStateWrapper object must be a Move object".to_owned(),
235 )
236 })?;
237 let result = bcs::from_bytes::<IotaSystemStateWrapper>(move_object.contents())
238 .map_err(|err| IotaError::IotaSystemStateRead(err.to_string()))?;
239 Ok(result)
240}
241
242pub fn get_iota_system_state(object_store: &dyn ObjectStore) -> Result<IotaSystemState, IotaError> {
243 let wrapper = get_iota_system_state_wrapper(object_store)?;
244 let id = wrapper.id.id.bytes;
245 match wrapper.version {
246 1 => {
247 let result: IotaSystemStateV1 =
248 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
249 |err| {
250 IotaError::DynamicFieldRead(format!(
251 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
252 id, wrapper.version, err
253 ))
254 },
255 )?;
256 Ok(IotaSystemState::V1(result))
257 }
258 2 => {
259 let result: IotaSystemStateV2 =
260 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
261 |err| {
262 IotaError::DynamicFieldRead(format!(
263 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
264 id, wrapper.version, err
265 ))
266 },
267 )?;
268 Ok(IotaSystemState::V2(result))
269 }
270 #[cfg(msim)]
271 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
272 let result: SimTestIotaSystemStateV1 =
273 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
274 |err| {
275 IotaError::DynamicFieldRead(format!(
276 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
277 id, wrapper.version, err
278 ))
279 },
280 )?;
281 Ok(IotaSystemState::SimTestV1(result))
282 }
283 #[cfg(msim)]
284 IOTA_SYSTEM_STATE_SIM_TEST_SHALLOW_V1 => {
285 let result: SimTestIotaSystemStateShallowV1 =
286 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
287 |err| {
288 IotaError::DynamicFieldRead(format!(
289 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
290 id, wrapper.version, err
291 ))
292 },
293 )?;
294 Ok(IotaSystemState::SimTestShallowV1(result))
295 }
296 #[cfg(msim)]
297 IOTA_SYSTEM_STATE_SIM_TEST_DEEP_V1 => {
298 let result: SimTestIotaSystemStateDeepV1 =
299 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
300 |err| {
301 IotaError::DynamicFieldRead(format!(
302 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
303 id, wrapper.version, err
304 ))
305 },
306 )?;
307 Ok(IotaSystemState::SimTestDeepV1(result))
308 }
309 _ => Err(IotaError::IotaSystemStateRead(format!(
310 "Unsupported IotaSystemState version: {}",
311 wrapper.version
312 ))),
313 }
314}
315
316pub fn get_validator_from_table<K>(
321 object_store: &dyn ObjectStore,
322 table_id: ObjectID,
323 key: &K,
324 protocol_version: Option<u64>,
325) -> Result<IotaValidatorSummary, IotaError>
326where
327 K: MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug,
328{
329 let field: Validator =
330 get_dynamic_field_from_store(object_store, table_id, key).map_err(|err| {
331 IotaError::IotaSystemStateRead(format!(
332 "Failed to load validator wrapper from table: {err:?}"
333 ))
334 })?;
335 let versioned = field.inner;
336 let version = versioned.version;
337 match version {
338 1 => {
339 let validator: ValidatorV1 =
340 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
341 .map_err(|err| {
342 IotaError::IotaSystemStateRead(format!(
343 "Failed to load inner validator from the wrapper: {err:?}"
344 ))
345 })?;
346 Ok(validator.into_iota_validator_summary(protocol_version))
347 }
348 #[cfg(msim)]
349 IOTA_SYSTEM_STATE_SIM_TEST_V1 => {
350 let validator: SimTestValidatorV1 =
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_DEEP_V1 => {
362 let validator: SimTestValidatorDeepV1 =
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 _ => Err(IotaError::IotaSystemStateRead(format!(
373 "Unsupported Validator version: {version}"
374 ))),
375 }
376}
377
378pub fn get_validators_from_table_vec<S, ValidatorType>(
379 object_store: &S,
380 table_id: ObjectID,
381 table_size: u64,
382) -> Result<Vec<ValidatorType>, IotaError>
383where
384 S: ObjectStore + ?Sized,
385 ValidatorType: Serialize + DeserializeOwned,
386{
387 let mut validators = vec![];
388 for i in 0..table_size {
389 let validator: ValidatorType = get_dynamic_field_from_store(&object_store, table_id, &i)
390 .map_err(|err| {
391 IotaError::IotaSystemStateRead(format!(
392 "Failed to load validator from table: {err:?}"
393 ))
394 })?;
395 validators.push(validator);
396 }
397 Ok(validators)
398}
399
400#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)]
401pub struct PoolTokenExchangeRate {
402 iota_amount: u64,
403 pool_token_amount: u64,
404}
405
406impl PoolTokenExchangeRate {
407 pub fn rate(&self) -> f64 {
409 if self.iota_amount == 0 {
410 1_f64
411 } else {
412 self.pool_token_amount as f64 / self.iota_amount as f64
413 }
414 }
415
416 pub fn new_for_testing(iota_amount: u64, pool_token_amount: u64) -> Self {
417 Self {
418 iota_amount,
419 pool_token_amount,
420 }
421 }
422}
423
424#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
425pub struct Validator {
426 pub inner: Versioned,
427}
428
429#[derive(Debug)]
430pub struct AdvanceEpochParams {
431 pub epoch: u64,
432 pub next_protocol_version: ProtocolVersion,
433 pub validator_subsidy: u64,
434 pub storage_charge: u64,
435 pub computation_charge: u64,
436 pub computation_charge_burned: u64,
437 pub storage_rebate: u64,
438 pub non_refundable_storage_fee: u64,
439 pub reward_slashing_rate: u64,
440 pub epoch_start_timestamp_ms: u64,
441 pub max_committee_members_count: u64,
442 pub eligible_active_validators: Vec<u64>,
443 pub scores: Vec<u64>,
444 pub adjust_rewards_by_score: bool,
445}
446
447#[cfg(msim)]
448pub mod advance_epoch_result_injection {
449 use std::cell::RefCell;
450
451 use crate::{
452 committee::EpochId,
453 error::{ExecutionError, ExecutionErrorKind},
454 };
455
456 thread_local! {
457 static OVERRIDE: RefCell<Option<(EpochId, EpochId)>> = const { RefCell::new(None) };
459 }
460
461 pub fn set_override(value: Option<(EpochId, EpochId)>) {
464 OVERRIDE.with(|o| *o.borrow_mut() = value);
465 }
466
467 pub fn maybe_modify_result(
471 result: Result<(), ExecutionError>,
472 current_epoch: EpochId,
473 ) -> Result<(), ExecutionError> {
474 if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
475 if current_epoch >= start && current_epoch < end {
476 return Err::<(), ExecutionError>(ExecutionError::new(
477 ExecutionErrorKind::FunctionNotFound,
478 None,
479 ));
480 }
481 }
482 result
483 }
484}