diff --git a/crates/iota-core/src/authority.rs b/crates/iota-core/src/authority.rs index 9542d7d6f70..9a4f8139cb0 100644 --- a/crates/iota-core/src/authority.rs +++ b/crates/iota-core/src/authority.rs @@ -4643,7 +4643,11 @@ impl AuthorityState { gas_cost_summary: &GasCostSummary, checkpoint: CheckpointSequenceNumber, epoch_start_timestamp_ms: CheckpointTimestamp, - ) -> anyhow::Result<(IotaSystemState, SystemEpochInfoEventV1, TransactionEffects)> { + ) -> anyhow::Result<( + IotaSystemState, + Option, + TransactionEffects, + )> { let mut txns = Vec::new(); if let Some(tx) = self.create_authenticator_state_tx(epoch_store) { @@ -4783,12 +4787,15 @@ impl AuthorityState { .events .data .iter() - .find(|event| event.is_system_epoch_info_event()) - .expect("end of epoch tx must emit system epoch info event"); - let system_epoch_info_event = bcs::from_bytes::( - &system_epoch_info_event.contents, - ) - .expect("deserialization should succeed since we asserted that the event is of this type"); + .find(|event| event.is_system_epoch_info_event()); + let system_epoch_info_event = system_epoch_info_event.map(|event| { + bcs::from_bytes::(&event.contents).expect( + "deserialization should succeed since we asserted that the event is of this type", + ) + }); + // The system epoch info event can be `None` in case if the `advance_epoch` + // Move function call failed and was executed in the safe mode. + assert!(system_epoch_info_event.is_some() || system_obj.safe_mode()); // We must write tx and effects to the state sync tables so that state sync is // able to deliver to the transaction to CheckpointExecutor after it is diff --git a/crates/iota-core/src/checkpoints/mod.rs b/crates/iota-core/src/checkpoints/mod.rs index c7b56a7cd34..e0c197630c1 100644 --- a/crates/iota-core/src/checkpoints/mod.rs +++ b/crates/iota-core/src/checkpoints/mod.rs @@ -1445,11 +1445,16 @@ impl CheckpointBuilder { ) .await?; + // The system epoch info event can be `None` in case if the `advance_epoch` + // Move function call failed and was executed in the safe mode. + // In this case, the tokens supply should be unchanged. + // // SAFETY: The number of minted and burnt tokens easily fit into an i64 and due // to those small numbers, no overflows will occur during conversion or // subtraction. - let epoch_supply_change = system_epoch_info_event.minted_tokens_amount as i64 - - system_epoch_info_event.burnt_tokens_amount as i64; + let epoch_supply_change = system_epoch_info_event.map_or(0, |event| { + event.minted_tokens_amount as i64 - event.burnt_tokens_amount as i64 + }); let committee = system_state_obj .get_current_epoch_committee() @@ -1574,7 +1579,7 @@ impl CheckpointBuilder { checkpoint_effects: &mut Vec, signatures: &mut Vec>, checkpoint: CheckpointSequenceNumber, - ) -> anyhow::Result<(IotaSystemState, SystemEpochInfoEventV1)> { + ) -> anyhow::Result<(IotaSystemState, Option)> { let (system_state, system_epoch_info_event, effects) = self .state .create_and_execute_advance_epoch_tx(