1use self::sui_system_state_inner_v1::{SuiSystemStateInnerV1, ValidatorV1};
5use self::sui_system_state_summary::{SuiSystemStateSummary, SuiValidatorSummary};
6use crate::base_types::ObjectID;
7use crate::committee::CommitteeWithNetworkMetadata;
8use crate::dynamic_field::{
9 Field, get_dynamic_field_from_store, get_dynamic_field_object_from_store,
10};
11use crate::error::{SuiError, SuiErrorKind};
12use crate::gas::GasCostSummary;
13use crate::object::{MoveObject, Object};
14use crate::storage::ObjectStore;
15use crate::sui_system_state::epoch_start_sui_system_state::EpochStartSystemState;
16use crate::sui_system_state::sui_system_state_inner_v2::SuiSystemStateInnerV2;
17use crate::versioned::Versioned;
18use crate::{MoveTypeTagTrait, SUI_SYSTEM_ADDRESS, SUI_SYSTEM_STATE_OBJECT_ID, id::UID};
19use anyhow::Result;
20use enum_dispatch::enum_dispatch;
21use move_core_types::{ident_str, identifier::IdentStr, language_storage::StructTag};
22use serde::de::DeserializeOwned;
23use serde::{Deserialize, Serialize};
24use std::fmt;
25use sui_protocol_config::{ProtocolConfig, ProtocolVersion};
26
27pub mod epoch_start_sui_system_state;
28pub mod mock;
29pub mod sui_system_state_inner_v1;
30pub mod sui_system_state_inner_v2;
31pub mod sui_system_state_summary;
32
33#[cfg(msim)]
34mod simtest_sui_system_state_inner;
35#[cfg(msim)]
36use self::simtest_sui_system_state_inner::{
37 SimTestSuiSystemStateInnerDeepV2, SimTestSuiSystemStateInnerShallowV2,
38 SimTestSuiSystemStateInnerV1, SimTestValidatorDeepV2, SimTestValidatorV1,
39};
40
41const SUI_SYSTEM_STATE_WRAPPER_STRUCT_NAME: &IdentStr = ident_str!("SuiSystemState");
42
43pub const SUI_SYSTEM_MODULE_NAME: &IdentStr = ident_str!("sui_system");
44pub const ADVANCE_EPOCH_FUNCTION_NAME: &IdentStr = ident_str!("advance_epoch");
45pub const ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME: &IdentStr = ident_str!("advance_epoch_safe_mode");
46
47#[cfg(msim)]
48pub const SUI_SYSTEM_STATE_SIM_TEST_V1: u64 = 18446744073709551605; #[cfg(msim)]
50pub const SUI_SYSTEM_STATE_SIM_TEST_SHALLOW_V2: u64 = 18446744073709551606; #[cfg(msim)]
52pub const SUI_SYSTEM_STATE_SIM_TEST_DEEP_V2: u64 = 18446744073709551607; #[derive(Debug, Serialize, Deserialize, Clone)]
62pub struct SuiSystemStateWrapper {
63 pub id: UID,
64 pub version: u64,
65}
66
67impl SuiSystemStateWrapper {
68 pub fn type_() -> StructTag {
69 StructTag {
70 address: SUI_SYSTEM_ADDRESS,
71 name: SUI_SYSTEM_STATE_WRAPPER_STRUCT_NAME.to_owned(),
72 module: SUI_SYSTEM_MODULE_NAME.to_owned(),
73 type_params: vec![],
74 }
75 }
76
77 pub fn advance_epoch_safe_mode(
81 &self,
82 params: &AdvanceEpochParams,
83 object_store: &dyn ObjectStore,
84 protocol_config: &ProtocolConfig,
85 ) -> (Object, Object) {
86 let id = self.id.id.bytes;
87 let old_field_object = get_dynamic_field_object_from_store(object_store, id, &self.version)
88 .expect("Dynamic field object of wrapper should always be present in the object store");
89 let mut new_field_object = old_field_object.clone();
90 let move_object = new_field_object
91 .data
92 .try_as_move_mut()
93 .expect("Dynamic field object must be a Move object");
94 match self.version {
95 1 => {
96 Self::advance_epoch_safe_mode_impl::<SuiSystemStateInnerV1>(
97 move_object,
98 params,
99 protocol_config,
100 );
101 }
102 2 => {
103 Self::advance_epoch_safe_mode_impl::<SuiSystemStateInnerV2>(
104 move_object,
105 params,
106 protocol_config,
107 );
108 }
109 #[cfg(msim)]
110 SUI_SYSTEM_STATE_SIM_TEST_V1 => {
111 Self::advance_epoch_safe_mode_impl::<SimTestSuiSystemStateInnerV1>(
112 move_object,
113 params,
114 protocol_config,
115 );
116 }
117 #[cfg(msim)]
118 SUI_SYSTEM_STATE_SIM_TEST_SHALLOW_V2 => {
119 Self::advance_epoch_safe_mode_impl::<SimTestSuiSystemStateInnerShallowV2>(
120 move_object,
121 params,
122 protocol_config,
123 );
124 }
125 #[cfg(msim)]
126 SUI_SYSTEM_STATE_SIM_TEST_DEEP_V2 => {
127 Self::advance_epoch_safe_mode_impl::<SimTestSuiSystemStateInnerDeepV2>(
128 move_object,
129 params,
130 protocol_config,
131 );
132 }
133 _ => unreachable!(),
134 }
135 (old_field_object, new_field_object)
136 }
137
138 fn advance_epoch_safe_mode_impl<T>(
139 move_object: &mut MoveObject,
140 params: &AdvanceEpochParams,
141 protocol_config: &ProtocolConfig,
142 ) where
143 T: Serialize + DeserializeOwned + SuiSystemStateTrait,
144 {
145 let mut field: Field<u64, T> =
146 bcs::from_bytes(move_object.contents()).expect("bcs deserialization should never fail");
147 tracing::info!(
148 "Advance epoch safe mode: current epoch: {}, protocol_version: {}, system_state_version: {}",
149 field.value.epoch(),
150 field.value.protocol_version(),
151 field.value.system_state_version()
152 );
153 field.value.advance_epoch_safe_mode(params);
154 tracing::info!(
155 "Safe mode activated. New epoch: {}, protocol_version: {}, system_state_version: {}",
156 field.value.epoch(),
157 field.value.protocol_version(),
158 field.value.system_state_version()
159 );
160 let new_contents = bcs::to_bytes(&field).expect("bcs serialization should never fail");
161 move_object
162 .update_contents_advance_epoch_safe_mode(new_contents, protocol_config)
163 .expect("Update sui system object content cannot fail since it should be small or unbounded");
164 }
165}
166
167#[enum_dispatch]
169pub trait SuiSystemStateTrait {
170 fn epoch(&self) -> u64;
171 fn reference_gas_price(&self) -> u64;
172 fn protocol_version(&self) -> u64;
173 fn system_state_version(&self) -> u64;
174 fn epoch_start_timestamp_ms(&self) -> u64;
175 fn epoch_duration_ms(&self) -> u64;
176 fn safe_mode(&self) -> bool;
177 fn safe_mode_gas_cost_summary(&self) -> GasCostSummary;
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<SuiValidatorSummary>, SuiError>;
184 fn into_epoch_start_state(self) -> EpochStartSystemState;
185 fn into_sui_system_state_summary(self) -> SuiSystemStateSummary;
186}
187
188#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
193#[enum_dispatch(SuiSystemStateTrait)]
194pub enum SuiSystemState {
195 V1(SuiSystemStateInnerV1),
196 V2(SuiSystemStateInnerV2),
197 #[cfg(msim)]
198 SimTestV1(SimTestSuiSystemStateInnerV1),
199 #[cfg(msim)]
200 SimTestShallowV2(SimTestSuiSystemStateInnerShallowV2),
201 #[cfg(msim)]
202 SimTestDeepV2(SimTestSuiSystemStateInnerDeepV2),
203}
204
205pub type SuiSystemStateInnerGenesis = SuiSystemStateInnerV1;
207pub type SuiValidatorGenesis = ValidatorV1;
208
209impl SuiSystemState {
210 pub fn into_genesis_version_for_tooling(self) -> SuiSystemStateInnerGenesis {
215 match self {
216 SuiSystemState::V1(inner) => inner,
217 _ => unreachable!(),
218 }
219 }
220
221 pub fn version(&self) -> u64 {
222 self.system_state_version()
223 }
224}
225
226pub fn get_sui_system_state_wrapper(
227 object_store: &dyn ObjectStore,
228) -> Result<SuiSystemStateWrapper, SuiError> {
229 let wrapper = object_store
230 .get_object(&SUI_SYSTEM_STATE_OBJECT_ID)
231 .ok_or_else(|| {
233 SuiErrorKind::SuiSystemStateReadError(
234 "SuiSystemStateWrapper object not found".to_owned(),
235 )
236 })?;
237 let move_object = wrapper.data.try_as_move().ok_or_else(|| {
238 SuiErrorKind::SuiSystemStateReadError(
239 "SuiSystemStateWrapper object must be a Move object".to_owned(),
240 )
241 })?;
242 let result = bcs::from_bytes::<SuiSystemStateWrapper>(move_object.contents())
243 .map_err(|err| SuiErrorKind::SuiSystemStateReadError(err.to_string()))?;
244 Ok(result)
245}
246
247pub fn get_sui_system_state(object_store: &dyn ObjectStore) -> Result<SuiSystemState, SuiError> {
248 let wrapper = get_sui_system_state_wrapper(object_store)?;
249 let id = wrapper.id.id.bytes;
250 match wrapper.version {
251 1 => {
252 let result: SuiSystemStateInnerV1 =
253 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
254 |err| {
255 SuiErrorKind::DynamicFieldReadError(format!(
256 "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
257 id, wrapper.version, err
258 ))
259 },
260 )?;
261 Ok(SuiSystemState::V1(result))
262 }
263 2 => {
264 let result: SuiSystemStateInnerV2 =
265 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
266 |err| {
267 SuiErrorKind::DynamicFieldReadError(format!(
268 "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
269 id, wrapper.version, err
270 ))
271 },
272 )?;
273 Ok(SuiSystemState::V2(result))
274 }
275 #[cfg(msim)]
276 SUI_SYSTEM_STATE_SIM_TEST_V1 => {
277 let result: SimTestSuiSystemStateInnerV1 =
278 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
279 |err| {
280 SuiErrorKind::DynamicFieldReadError(format!(
281 "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
282 id, wrapper.version, err
283 ))
284 },
285 )?;
286 Ok(SuiSystemState::SimTestV1(result))
287 }
288 #[cfg(msim)]
289 SUI_SYSTEM_STATE_SIM_TEST_SHALLOW_V2 => {
290 let result: SimTestSuiSystemStateInnerShallowV2 =
291 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
292 |err| {
293 SuiErrorKind::DynamicFieldReadError(format!(
294 "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
295 id, wrapper.version, err
296 ))
297 },
298 )?;
299 Ok(SuiSystemState::SimTestShallowV2(result))
300 }
301 #[cfg(msim)]
302 SUI_SYSTEM_STATE_SIM_TEST_DEEP_V2 => {
303 let result: SimTestSuiSystemStateInnerDeepV2 =
304 get_dynamic_field_from_store(object_store, id, &wrapper.version).map_err(
305 |err| {
306 SuiErrorKind::DynamicFieldReadError(format!(
307 "Failed to load sui system state inner object with ID {:?} and version {:?}: {:?}",
308 id, wrapper.version, err
309 ))
310 },
311 )?;
312 Ok(SuiSystemState::SimTestDeepV2(result))
313 }
314 _ => Err(SuiErrorKind::SuiSystemStateReadError(format!(
315 "Unsupported SuiSystemState version: {}",
316 wrapper.version
317 ))
318 .into()),
319 }
320}
321
322pub fn get_validator_from_table<K>(
327 object_store: &dyn ObjectStore,
328 table_id: ObjectID,
329 key: &K,
330) -> Result<SuiValidatorSummary, SuiError>
331where
332 K: Clone + MoveTypeTagTrait + Serialize + DeserializeOwned + fmt::Debug,
333{
334 let field: ValidatorWrapper = get_dynamic_field_from_store(object_store, table_id, key)
335 .map_err(|err| {
336 SuiErrorKind::SuiSystemStateReadError(format!(
337 "Failed to load validator wrapper from table: {:?}",
338 err
339 ))
340 })?;
341 let versioned = field.inner;
342 let version = versioned.version;
343 match version {
344 1 => {
345 let validator: ValidatorV1 =
346 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
347 .map_err(|err| {
348 SuiErrorKind::SuiSystemStateReadError(format!(
349 "Failed to load inner validator from the wrapper: {:?}",
350 err
351 ))
352 })?;
353 Ok(validator.into_sui_validator_summary())
354 }
355 #[cfg(msim)]
356 SUI_SYSTEM_STATE_SIM_TEST_V1 => {
357 let validator: SimTestValidatorV1 =
358 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
359 .map_err(|err| {
360 SuiErrorKind::SuiSystemStateReadError(format!(
361 "Failed to load inner validator from the wrapper: {:?}",
362 err
363 ))
364 })?;
365 Ok(validator.into_sui_validator_summary())
366 }
367 #[cfg(msim)]
368 SUI_SYSTEM_STATE_SIM_TEST_DEEP_V2 => {
369 let validator: SimTestValidatorDeepV2 =
370 get_dynamic_field_from_store(object_store, versioned.id.id.bytes, &version)
371 .map_err(|err| {
372 SuiErrorKind::SuiSystemStateReadError(format!(
373 "Failed to load inner validator from the wrapper: {:?}",
374 err
375 ))
376 })?;
377 Ok(validator.into_sui_validator_summary())
378 }
379 _ => Err(SuiErrorKind::SuiSystemStateReadError(format!(
380 "Unsupported Validator version: {}",
381 version
382 ))
383 .into()),
384 }
385}
386
387pub fn get_validators_from_table_vec<S, ValidatorType>(
388 object_store: &S,
389 table_id: ObjectID,
390 table_size: u64,
391) -> Result<Vec<ValidatorType>, SuiError>
392where
393 S: ObjectStore + ?Sized,
394 ValidatorType: Serialize + DeserializeOwned,
395{
396 let mut validators = vec![];
397 for i in 0..table_size {
398 let validator: ValidatorType = get_dynamic_field_from_store(&object_store, table_id, &i)
399 .map_err(|err| {
400 SuiErrorKind::SuiSystemStateReadError(format!(
401 "Failed to load validator from table: {:?}",
402 err
403 ))
404 })?;
405 validators.push(validator);
406 }
407 Ok(validators)
408}
409
410#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)]
411pub struct PoolTokenExchangeRate {
412 sui_amount: u64,
413 pool_token_amount: u64,
414}
415
416impl PoolTokenExchangeRate {
417 pub fn rate(&self) -> f64 {
419 if self.sui_amount == 0 {
420 1_f64
421 } else {
422 self.pool_token_amount as f64 / self.sui_amount as f64
423 }
424 }
425
426 pub fn new(sui_amount: u64, pool_token_amount: u64) -> Self {
427 Self {
428 sui_amount,
429 pool_token_amount,
430 }
431 }
432}
433
434#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
435pub struct ValidatorWrapper {
436 pub inner: Versioned,
437}
438
439#[derive(Debug)]
440pub struct AdvanceEpochParams {
441 pub epoch: u64,
442 pub next_protocol_version: ProtocolVersion,
443 pub storage_charge: u64,
444 pub computation_charge: u64,
445 pub storage_rebate: u64,
446 pub non_refundable_storage_fee: u64,
447 pub storage_fund_reinvest_rate: u64,
448 pub reward_slashing_rate: u64,
449 pub epoch_start_timestamp_ms: u64,
450}
451
452#[cfg(msim)]
453pub mod advance_epoch_result_injection {
454 use crate::{
455 committee::EpochId,
456 error::{ExecutionError, ExecutionErrorKind},
457 execution::ResultWithTimings,
458 };
459 use std::cell::RefCell;
460
461 thread_local! {
462 static OVERRIDE: RefCell<Option<(EpochId, EpochId)>> = RefCell::new(None);
464 }
465
466 pub fn set_override(value: Option<(EpochId, EpochId)>) {
468 OVERRIDE.with(|o| *o.borrow_mut() = value);
469 }
470
471 pub fn maybe_modify_result(
474 result: ResultWithTimings<(), ExecutionError>,
475 current_epoch: EpochId,
476 ) -> ResultWithTimings<(), ExecutionError> {
477 if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
478 if current_epoch >= start && current_epoch < end {
479 return Err((
480 ExecutionError::new(ExecutionErrorKind::FunctionNotFound, None),
481 vec![],
482 ));
483 }
484 }
485 result
486 }
487
488 pub fn maybe_modify_result_legacy(
490 result: Result<(), ExecutionError>,
491 current_epoch: EpochId,
492 ) -> Result<(), ExecutionError> {
493 if let Some((start, end)) = OVERRIDE.with(|o| *o.borrow()) {
494 if current_epoch >= start && current_epoch < end {
495 return Err(ExecutionError::new(
496 ExecutionErrorKind::FunctionNotFound,
497 None,
498 ));
499 }
500 }
501 result
502 }
503}