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