pub use checked::*;
#[sui_macros::with_checked_arithmetic]
mod checked {
use crate::execution_mode::{self, ExecutionMode};
use move_binary_format::CompiledModule;
use move_vm_runtime::move_vm::MoveVM;
use std::{collections::HashSet, sync::Arc};
use sui_types::balance::{
BALANCE_CREATE_REWARDS_FUNCTION_NAME, BALANCE_DESTROY_REBATES_FUNCTION_NAME,
BALANCE_MODULE_NAME,
};
use sui_types::base_types::SequenceNumber;
use sui_types::gas_coin::GAS;
use sui_types::messages_checkpoint::CheckpointTimestamp;
use sui_types::metrics::LimitsMetrics;
use sui_types::object::OBJECT_START_VERSION;
use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
use tracing::{info, instrument, trace, warn};
use crate::programmable_transactions;
use crate::type_layout_resolver::TypeLayoutResolver;
use crate::{gas_charger::GasCharger, temporary_store::TemporaryStore};
use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed, ProtocolConfig};
use sui_types::authenticator_state::{
AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME, AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME,
AUTHENTICATOR_STATE_MODULE_NAME, AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME,
};
use sui_types::clock::{CLOCK_MODULE_NAME, CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME};
use sui_types::committee::EpochId;
use sui_types::effects::TransactionEffects;
use sui_types::error::{ExecutionError, ExecutionErrorKind};
use sui_types::execution::is_certificate_denied;
use sui_types::execution_config_utils::to_binary_config;
use sui_types::execution_status::{CongestedObjects, ExecutionStatus};
use sui_types::gas::GasCostSummary;
use sui_types::gas::SuiGasStatus;
use sui_types::inner_temporary_store::InnerTemporaryStore;
use sui_types::storage::BackingStore;
#[cfg(msim)]
use sui_types::sui_system_state::advance_epoch_result_injection::maybe_modify_result_legacy;
use sui_types::sui_system_state::{AdvanceEpochParams, ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME};
use sui_types::transaction::CheckedInputObjects;
use sui_types::transaction::{
Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, CallArg, ChangeEpoch,
Command, EndOfEpochTransactionKind, GenesisTransaction, ObjectArg, ProgrammableTransaction,
TransactionKind,
};
use sui_types::{
base_types::{ObjectID, ObjectRef, SuiAddress, TransactionDigest, TxContext},
object::{Object, ObjectInner},
sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME},
SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, SUI_FRAMEWORK_PACKAGE_ID,
SUI_SYSTEM_PACKAGE_ID,
};
#[instrument(name = "tx_execute_to_effects", level = "debug", skip_all)]
pub fn execute_transaction_to_effects<Mode: ExecutionMode>(
store: &dyn BackingStore,
input_objects: CheckedInputObjects,
gas_coins: Vec<ObjectRef>,
gas_status: SuiGasStatus,
transaction_kind: TransactionKind,
transaction_signer: SuiAddress,
transaction_digest: TransactionDigest,
move_vm: &Arc<MoveVM>,
epoch_id: &EpochId,
epoch_timestamp_ms: u64,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
enable_expensive_checks: bool,
certificate_deny_set: &HashSet<TransactionDigest>,
) -> (
InnerTemporaryStore,
SuiGasStatus,
TransactionEffects,
Result<Mode::ExecutionResults, ExecutionError>,
) {
let input_objects = input_objects.into_inner();
let shared_object_refs = input_objects.filter_shared_objects();
let receiving_objects = transaction_kind.receiving_objects();
let mut transaction_dependencies = input_objects.transaction_dependencies();
let contains_deleted_input = input_objects.contains_consensus_stream_ended_objects();
let cancelled_objects = input_objects.get_cancelled_objects();
let mut temporary_store = TemporaryStore::new(
store,
input_objects,
receiving_objects,
transaction_digest,
protocol_config,
);
let mut gas_charger =
GasCharger::new(transaction_digest, gas_coins, gas_status, protocol_config);
let mut tx_ctx = TxContext::new_from_components(
&transaction_signer,
&transaction_digest,
epoch_id,
epoch_timestamp_ms,
1,
1_000_000,
None,
protocol_config,
);
let is_epoch_change = transaction_kind.is_end_of_epoch_tx();
let deny_cert = is_certificate_denied(&transaction_digest, certificate_deny_set);
let (gas_cost_summary, execution_result) = execute_transaction::<Mode>(
&mut temporary_store,
transaction_kind,
&mut gas_charger,
&mut tx_ctx,
move_vm,
protocol_config,
metrics,
enable_expensive_checks,
deny_cert,
contains_deleted_input,
cancelled_objects,
);
let status = if let Err(error) = &execution_result {
use ExecutionErrorKind as K;
match error.kind() {
K::InvariantViolation | K::VMInvariantViolation => {
#[skip_checked_arithmetic]
tracing::error!(
kind = ?error.kind(),
tx_digest = ?transaction_digest,
"INVARIANT VIOLATION! Source: {:?}",
error.source(),
);
}
K::SuiMoveVerificationError | K::VMVerificationOrDeserializationError => {
#[skip_checked_arithmetic]
tracing::debug!(
kind = ?error.kind(),
tx_digest = ?transaction_digest,
"Verification Error. Source: {:?}",
error.source(),
);
}
K::PublishUpgradeMissingDependency | K::PublishUpgradeDependencyDowngrade => {
#[skip_checked_arithmetic]
tracing::debug!(
kind = ?error.kind(),
tx_digest = ?transaction_digest,
"Publish/Upgrade Error. Source: {:?}",
error.source(),
)
}
_ => (),
};
let (status, command) = error.to_execution_status();
ExecutionStatus::new_failure(status, command)
} else {
ExecutionStatus::Success
};
#[skip_checked_arithmetic]
trace!(
tx_digest = ?transaction_digest,
computation_gas_cost = gas_cost_summary.computation_cost,
storage_gas_cost = gas_cost_summary.storage_cost,
storage_gas_rebate = gas_cost_summary.storage_rebate,
"Finished execution of transaction with status {:?}",
status
);
transaction_dependencies.remove(&TransactionDigest::genesis_marker());
if enable_expensive_checks && !Mode::allow_arbitrary_function_calls() {
temporary_store
.check_ownership_invariants(&transaction_signer, &mut gas_charger, is_epoch_change)
.unwrap()
} let (inner, effects) = temporary_store.into_effects(
shared_object_refs,
&transaction_digest,
transaction_dependencies,
gas_cost_summary,
status,
&mut gas_charger,
*epoch_id,
);
(
inner,
gas_charger.into_gas_status(),
effects,
execution_result,
)
}
pub fn execute_genesis_state_update(
store: &dyn BackingStore,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
move_vm: &Arc<MoveVM>,
tx_context: &mut TxContext,
input_objects: CheckedInputObjects,
pt: ProgrammableTransaction,
) -> Result<InnerTemporaryStore, ExecutionError> {
let input_objects = input_objects.into_inner();
let mut temporary_store = TemporaryStore::new(
store,
input_objects,
vec![],
tx_context.digest(),
protocol_config,
);
let mut gas_charger = GasCharger::new_unmetered(tx_context.digest());
programmable_transactions::execution::execute::<execution_mode::Genesis>(
protocol_config,
metrics,
move_vm,
&mut temporary_store,
tx_context,
&mut gas_charger,
pt,
)?;
temporary_store.update_object_version_and_prev_tx();
Ok(temporary_store.into_inner())
}
#[instrument(name = "tx_execute", level = "debug", skip_all)]
fn execute_transaction<Mode: ExecutionMode>(
temporary_store: &mut TemporaryStore<'_>,
transaction_kind: TransactionKind,
gas_charger: &mut GasCharger,
tx_ctx: &mut TxContext,
move_vm: &Arc<MoveVM>,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
enable_expensive_checks: bool,
deny_cert: bool,
contains_deleted_input: bool,
cancelled_objects: Option<(Vec<ObjectID>, SequenceNumber)>,
) -> (
GasCostSummary,
Result<Mode::ExecutionResults, ExecutionError>,
) {
gas_charger.smash_gas(temporary_store);
debug_assert!(
gas_charger.no_charges(),
"No gas charges must be applied yet"
);
let is_genesis_tx = matches!(transaction_kind, TransactionKind::Genesis(_));
let advance_epoch_gas_summary = transaction_kind.get_advance_epoch_tx_gas_summary();
let result = gas_charger.charge_input_objects(temporary_store);
let mut result = result.and_then(|()| {
let mut execution_result = if deny_cert {
Err(ExecutionError::new(
ExecutionErrorKind::CertificateDenied,
None,
))
} else if contains_deleted_input {
Err(ExecutionError::new(
ExecutionErrorKind::InputObjectDeleted,
None,
))
} else if let Some((cancelled_objects, reason)) = cancelled_objects {
match reason {
SequenceNumber::CONGESTED => Err(ExecutionError::new(
ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion {
congested_objects: CongestedObjects(cancelled_objects),
},
None,
)),
SequenceNumber::RANDOMNESS_UNAVAILABLE => Err(ExecutionError::new(
ExecutionErrorKind::ExecutionCancelledDueToRandomnessUnavailable,
None,
)),
_ => panic!("invalid cancellation reason SequenceNumber: {reason}"),
}
} else {
execution_loop::<Mode>(
temporary_store,
transaction_kind,
tx_ctx,
move_vm,
gas_charger,
protocol_config,
metrics.clone(),
)
};
let meter_check = check_meter_limit(
temporary_store,
gas_charger,
protocol_config,
metrics.clone(),
);
if let Err(e) = meter_check {
execution_result = Err(e);
}
if execution_result.is_ok() {
let gas_check = check_written_objects_limit::<Mode>(
temporary_store,
gas_charger,
protocol_config,
metrics,
);
if let Err(e) = gas_check {
execution_result = Err(e);
}
}
execution_result
});
let cost_summary = gas_charger.charge_gas(temporary_store, &mut result);
temporary_store.conserve_unmetered_storage_rebate(gas_charger.unmetered_storage_rebate());
if let Err(e) = run_conservation_checks::<Mode>(
temporary_store,
gas_charger,
tx_ctx,
move_vm,
protocol_config.simple_conservation_checks(),
enable_expensive_checks,
&cost_summary,
is_genesis_tx,
advance_epoch_gas_summary,
) {
result = Err(e);
}
(cost_summary, result)
}
#[instrument(name = "run_conservation_checks", level = "debug", skip_all)]
fn run_conservation_checks<Mode: ExecutionMode>(
temporary_store: &mut TemporaryStore<'_>,
gas_charger: &mut GasCharger,
tx_ctx: &mut TxContext,
move_vm: &Arc<MoveVM>,
simple_conservation_checks: bool,
enable_expensive_checks: bool,
cost_summary: &GasCostSummary,
is_genesis_tx: bool,
advance_epoch_gas_summary: Option<(u64, u64)>,
) -> Result<(), ExecutionError> {
let mut result: std::result::Result<(), sui_types::error::ExecutionError> = Ok(());
if !is_genesis_tx && !Mode::skip_conservation_checks() {
let conservation_result = {
temporary_store
.check_sui_conserved(simple_conservation_checks, cost_summary)
.and_then(|()| {
if enable_expensive_checks {
let mut layout_resolver =
TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
temporary_store.check_sui_conserved_expensive(
cost_summary,
advance_epoch_gas_summary,
&mut layout_resolver,
)
} else {
Ok(())
}
})
};
if let Err(conservation_err) = conservation_result {
result = Err(conservation_err);
gas_charger.reset(temporary_store);
gas_charger.charge_gas(temporary_store, &mut result);
if let Err(recovery_err) = {
temporary_store
.check_sui_conserved(simple_conservation_checks, cost_summary)
.and_then(|()| {
if enable_expensive_checks {
let mut layout_resolver =
TypeLayoutResolver::new(move_vm, Box::new(&*temporary_store));
temporary_store.check_sui_conserved_expensive(
cost_summary,
advance_epoch_gas_summary,
&mut layout_resolver,
)
} else {
Ok(())
}
})
} {
panic!(
"SUI conservation fail in tx block {}: {}\nGas status is {}\nTx was ",
tx_ctx.digest(),
recovery_err,
gas_charger.summary()
)
}
}
} result
}
#[instrument(name = "check_meter_limit", level = "debug", skip_all)]
fn check_meter_limit(
temporary_store: &mut TemporaryStore<'_>,
gas_charger: &mut GasCharger,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
) -> Result<(), ExecutionError> {
let effects_estimated_size = temporary_store.estimate_effects_size_upperbound();
match check_limit_by_meter!(
!gas_charger.is_unmetered(),
effects_estimated_size,
protocol_config.max_serialized_tx_effects_size_bytes(),
protocol_config.max_serialized_tx_effects_size_bytes_system_tx(),
metrics.excessive_estimated_effects_size
) {
LimitThresholdCrossed::None => Ok(()),
LimitThresholdCrossed::Soft(_, limit) => {
warn!(
effects_estimated_size = effects_estimated_size,
soft_limit = limit,
"Estimated transaction effects size crossed soft limit",
);
Ok(())
}
LimitThresholdCrossed::Hard(_, lim) => Err(ExecutionError::new_with_source(
ExecutionErrorKind::EffectsTooLarge {
current_size: effects_estimated_size as u64,
max_size: lim as u64,
},
"Transaction effects are too large",
)),
}
}
#[instrument(name = "check_written_objects_limit", level = "debug", skip_all)]
fn check_written_objects_limit<Mode: ExecutionMode>(
temporary_store: &mut TemporaryStore<'_>,
gas_charger: &mut GasCharger,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
) -> Result<(), ExecutionError> {
if let (Some(normal_lim), Some(system_lim)) = (
protocol_config.max_size_written_objects_as_option(),
protocol_config.max_size_written_objects_system_tx_as_option(),
) {
let written_objects_size = temporary_store.written_objects_size();
match check_limit_by_meter!(
!gas_charger.is_unmetered(),
written_objects_size,
normal_lim,
system_lim,
metrics.excessive_written_objects_size
) {
LimitThresholdCrossed::None => (),
LimitThresholdCrossed::Soft(_, limit) => {
warn!(
written_objects_size = written_objects_size,
soft_limit = limit,
"Written objects size crossed soft limit",
)
}
LimitThresholdCrossed::Hard(_, lim) => {
return Err(ExecutionError::new_with_source(
ExecutionErrorKind::WrittenObjectsTooLarge {
current_size: written_objects_size as u64,
max_size: lim as u64,
},
"Written objects size crossed hard limit",
))
}
};
}
Ok(())
}
#[instrument(level = "debug", skip_all)]
fn execution_loop<Mode: ExecutionMode>(
temporary_store: &mut TemporaryStore<'_>,
transaction_kind: TransactionKind,
tx_ctx: &mut TxContext,
move_vm: &Arc<MoveVM>,
gas_charger: &mut GasCharger,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
) -> Result<Mode::ExecutionResults, ExecutionError> {
let result = match transaction_kind {
TransactionKind::ChangeEpoch(change_epoch) => {
let builder = ProgrammableTransactionBuilder::new();
advance_epoch(
builder,
change_epoch,
temporary_store,
tx_ctx,
move_vm,
gas_charger,
protocol_config,
metrics,
)?;
Ok(Mode::empty_results())
}
TransactionKind::Genesis(GenesisTransaction { objects }) => {
if tx_ctx.epoch() != 0 {
panic!("BUG: Genesis Transactions can only be executed in epoch 0");
}
for genesis_object in objects {
match genesis_object {
sui_types::transaction::GenesisObject::RawObject { data, owner } => {
let object = ObjectInner {
data,
owner,
previous_transaction: tx_ctx.digest(),
storage_rebate: 0,
};
temporary_store.create_object(object.into());
}
}
}
Ok(Mode::empty_results())
}
TransactionKind::ConsensusCommitPrologue(prologue) => {
setup_consensus_commit(
prologue.commit_timestamp_ms,
temporary_store,
tx_ctx,
move_vm,
gas_charger,
protocol_config,
metrics,
)
.expect("ConsensusCommitPrologue cannot fail");
Ok(Mode::empty_results())
}
TransactionKind::ConsensusCommitPrologueV2(prologue) => {
setup_consensus_commit(
prologue.commit_timestamp_ms,
temporary_store,
tx_ctx,
move_vm,
gas_charger,
protocol_config,
metrics,
)
.expect("ConsensusCommitPrologue cannot fail");
Ok(Mode::empty_results())
}
TransactionKind::ConsensusCommitPrologueV3(prologue) => {
setup_consensus_commit(
prologue.commit_timestamp_ms,
temporary_store,
tx_ctx,
move_vm,
gas_charger,
protocol_config,
metrics,
)
.expect("ConsensusCommitPrologue cannot fail");
Ok(Mode::empty_results())
}
TransactionKind::ConsensusCommitPrologueV4(prologue) => {
setup_consensus_commit(
prologue.commit_timestamp_ms,
temporary_store,
tx_ctx,
move_vm,
gas_charger,
protocol_config,
metrics,
)
.expect("ConsensusCommitPrologue cannot fail");
Ok(Mode::empty_results())
}
TransactionKind::ProgrammableTransaction(pt) => {
programmable_transactions::execution::execute::<Mode>(
protocol_config,
metrics,
move_vm,
temporary_store,
tx_ctx,
gas_charger,
pt,
)
}
TransactionKind::EndOfEpochTransaction(txns) => {
let mut builder = ProgrammableTransactionBuilder::new();
let len = txns.len();
for (i, tx) in txns.into_iter().enumerate() {
match tx {
EndOfEpochTransactionKind::ChangeEpoch(change_epoch) => {
assert_eq!(i, len - 1);
advance_epoch(
builder,
change_epoch,
temporary_store,
tx_ctx,
move_vm,
gas_charger,
protocol_config,
metrics,
)?;
return Ok(Mode::empty_results());
}
EndOfEpochTransactionKind::AuthenticatorStateCreate => {
assert!(protocol_config.enable_jwk_consensus_updates());
builder = setup_authenticator_state_create(builder);
}
EndOfEpochTransactionKind::AuthenticatorStateExpire(expire) => {
assert!(protocol_config.enable_jwk_consensus_updates());
builder = setup_authenticator_state_expire(builder, expire);
}
EndOfEpochTransactionKind::RandomnessStateCreate => {
panic!("EndOfEpochTransactionKind::RandomnessStateCreate should not exist in v1");
}
EndOfEpochTransactionKind::DenyListStateCreate => {
panic!("EndOfEpochTransactionKind::CoinDenyListStateCreate should not exist in v1");
}
EndOfEpochTransactionKind::BridgeStateCreate(_) => {
panic!(
"EndOfEpochTransactionKind::BridgeStateCreate should not exist in v1"
);
}
EndOfEpochTransactionKind::BridgeCommitteeInit(_) => {
panic!("EndOfEpochTransactionKind::BridgeCommitteeInit should not exist in v1");
}
EndOfEpochTransactionKind::StoreExecutionTimeObservations(_) => {
panic!("EndOfEpochTransactionKind::StoreExecutionTimeEstimates should not exist in v1");
}
}
}
unreachable!("EndOfEpochTransactionKind::ChangeEpoch should be the last transaction in the list")
}
TransactionKind::AuthenticatorStateUpdate(auth_state_update) => {
setup_authenticator_state_update(
auth_state_update,
temporary_store,
tx_ctx,
move_vm,
gas_charger,
protocol_config,
metrics,
)?;
Ok(Mode::empty_results())
}
TransactionKind::RandomnessStateUpdate(_) => {
panic!("RandomnessStateUpdate should not exist in v1");
}
}?;
temporary_store.check_execution_results_consistency()?;
Ok(result)
}
fn mint_epoch_rewards_in_pt(
builder: &mut ProgrammableTransactionBuilder,
params: &AdvanceEpochParams,
) -> (Argument, Argument) {
let storage_charge_arg = builder
.input(CallArg::Pure(
bcs::to_bytes(¶ms.storage_charge).unwrap(),
))
.unwrap();
let storage_rewards = builder.programmable_move_call(
SUI_FRAMEWORK_PACKAGE_ID,
BALANCE_MODULE_NAME.to_owned(),
BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
vec![GAS::type_tag()],
vec![storage_charge_arg],
);
let computation_charge_arg = builder
.input(CallArg::Pure(
bcs::to_bytes(¶ms.computation_charge).unwrap(),
))
.unwrap();
let computation_rewards = builder.programmable_move_call(
SUI_FRAMEWORK_PACKAGE_ID,
BALANCE_MODULE_NAME.to_owned(),
BALANCE_CREATE_REWARDS_FUNCTION_NAME.to_owned(),
vec![GAS::type_tag()],
vec![computation_charge_arg],
);
(storage_rewards, computation_rewards)
}
pub fn construct_advance_epoch_pt(
mut builder: ProgrammableTransactionBuilder,
params: &AdvanceEpochParams,
) -> Result<ProgrammableTransaction, ExecutionError> {
let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
let mut arguments = vec![storage_rewards, computation_rewards];
let call_arg_arguments = vec![
CallArg::SUI_SYSTEM_MUT,
CallArg::Pure(bcs::to_bytes(¶ms.epoch).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.next_protocol_version.as_u64()).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.storage_rebate).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.non_refundable_storage_fee).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.storage_fund_reinvest_rate).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.reward_slashing_rate).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.epoch_start_timestamp_ms).unwrap()),
]
.into_iter()
.map(|a| builder.input(a))
.collect::<Result<_, _>>();
assert_invariant!(
call_arg_arguments.is_ok(),
"Unable to generate args for advance_epoch transaction!"
);
arguments.append(&mut call_arg_arguments.unwrap());
info!("Call arguments to advance_epoch transaction: {:?}", params);
let storage_rebates = builder.programmable_move_call(
SUI_SYSTEM_PACKAGE_ID,
SUI_SYSTEM_MODULE_NAME.to_owned(),
ADVANCE_EPOCH_FUNCTION_NAME.to_owned(),
vec![],
arguments,
);
builder.programmable_move_call(
SUI_FRAMEWORK_PACKAGE_ID,
BALANCE_MODULE_NAME.to_owned(),
BALANCE_DESTROY_REBATES_FUNCTION_NAME.to_owned(),
vec![GAS::type_tag()],
vec![storage_rebates],
);
Ok(builder.finish())
}
pub fn construct_advance_epoch_safe_mode_pt(
params: &AdvanceEpochParams,
protocol_config: &ProtocolConfig,
) -> Result<ProgrammableTransaction, ExecutionError> {
let mut builder = ProgrammableTransactionBuilder::new();
let (storage_rewards, computation_rewards) = mint_epoch_rewards_in_pt(&mut builder, params);
let mut arguments = vec![storage_rewards, computation_rewards];
let mut args = vec![
CallArg::SUI_SYSTEM_MUT,
CallArg::Pure(bcs::to_bytes(¶ms.epoch).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.next_protocol_version.as_u64()).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.storage_rebate).unwrap()),
CallArg::Pure(bcs::to_bytes(¶ms.non_refundable_storage_fee).unwrap()),
];
if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
args.push(CallArg::Pure(
bcs::to_bytes(¶ms.epoch_start_timestamp_ms).unwrap(),
));
}
let call_arg_arguments = args
.into_iter()
.map(|a| builder.input(a))
.collect::<Result<_, _>>();
assert_invariant!(
call_arg_arguments.is_ok(),
"Unable to generate args for advance_epoch transaction!"
);
arguments.append(&mut call_arg_arguments.unwrap());
info!("Call arguments to advance_epoch transaction: {:?}", params);
builder.programmable_move_call(
SUI_SYSTEM_PACKAGE_ID,
SUI_SYSTEM_MODULE_NAME.to_owned(),
ADVANCE_EPOCH_SAFE_MODE_FUNCTION_NAME.to_owned(),
vec![],
arguments,
);
Ok(builder.finish())
}
fn advance_epoch(
builder: ProgrammableTransactionBuilder,
change_epoch: ChangeEpoch,
temporary_store: &mut TemporaryStore<'_>,
tx_ctx: &mut TxContext,
move_vm: &Arc<MoveVM>,
gas_charger: &mut GasCharger,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
) -> Result<(), ExecutionError> {
let params = AdvanceEpochParams {
epoch: change_epoch.epoch,
next_protocol_version: change_epoch.protocol_version,
storage_charge: change_epoch.storage_charge,
computation_charge: change_epoch.computation_charge,
storage_rebate: change_epoch.storage_rebate,
non_refundable_storage_fee: change_epoch.non_refundable_storage_fee,
storage_fund_reinvest_rate: protocol_config.storage_fund_reinvest_rate(),
reward_slashing_rate: protocol_config.reward_slashing_rate(),
epoch_start_timestamp_ms: change_epoch.epoch_start_timestamp_ms,
};
let advance_epoch_pt = construct_advance_epoch_pt(builder, ¶ms)?;
let result = programmable_transactions::execution::execute::<execution_mode::System>(
protocol_config,
metrics.clone(),
move_vm,
temporary_store,
tx_ctx,
gas_charger,
advance_epoch_pt,
);
#[cfg(msim)]
let result = maybe_modify_result_legacy(result, change_epoch.epoch);
if result.is_err() {
tracing::error!(
"Failed to execute advance epoch transaction. Switching to safe mode. Error: {:?}. Input objects: {:?}. Tx data: {:?}",
result.as_ref().err(),
temporary_store.objects(),
change_epoch,
);
temporary_store.drop_writes();
gas_charger.reset_storage_cost_and_rebate();
if protocol_config.get_advance_epoch_start_time_in_safe_mode() {
temporary_store.advance_epoch_safe_mode(¶ms, protocol_config);
} else {
let advance_epoch_safe_mode_pt =
construct_advance_epoch_safe_mode_pt(¶ms, protocol_config)?;
programmable_transactions::execution::execute::<execution_mode::System>(
protocol_config,
metrics.clone(),
move_vm,
temporary_store,
tx_ctx,
gas_charger,
advance_epoch_safe_mode_pt,
)
.expect("Advance epoch with safe mode must succeed");
}
}
let binary_config = to_binary_config(protocol_config);
for (version, modules, dependencies) in change_epoch.system_packages.into_iter() {
let deserialized_modules: Vec<_> = modules
.iter()
.map(|m| CompiledModule::deserialize_with_config(m, &binary_config).unwrap())
.collect();
if version == OBJECT_START_VERSION {
let package_id = deserialized_modules.first().unwrap().address();
info!("adding new system package {package_id}");
let publish_pt = {
let mut b = ProgrammableTransactionBuilder::new();
b.command(Command::Publish(modules, dependencies));
b.finish()
};
programmable_transactions::execution::execute::<execution_mode::System>(
protocol_config,
metrics.clone(),
move_vm,
temporary_store,
tx_ctx,
gas_charger,
publish_pt,
)
.expect("System Package Publish must succeed");
} else {
let mut new_package = Object::new_system_package(
&deserialized_modules,
version,
dependencies,
tx_ctx.digest(),
);
info!(
"upgraded system package {:?}",
new_package.compute_object_reference()
);
new_package
.data
.try_as_package_mut()
.unwrap()
.decrement_version();
temporary_store.upgrade_system_package(new_package);
}
}
Ok(())
}
fn setup_consensus_commit(
consensus_commit_timestamp_ms: CheckpointTimestamp,
temporary_store: &mut TemporaryStore<'_>,
tx_ctx: &mut TxContext,
move_vm: &Arc<MoveVM>,
gas_charger: &mut GasCharger,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
) -> Result<(), ExecutionError> {
let pt = {
let mut builder = ProgrammableTransactionBuilder::new();
let res = builder.move_call(
SUI_FRAMEWORK_ADDRESS.into(),
CLOCK_MODULE_NAME.to_owned(),
CONSENSUS_COMMIT_PROLOGUE_FUNCTION_NAME.to_owned(),
vec![],
vec![
CallArg::CLOCK_MUT,
CallArg::Pure(bcs::to_bytes(&consensus_commit_timestamp_ms).unwrap()),
],
);
assert_invariant!(
res.is_ok(),
"Unable to generate consensus_commit_prologue transaction!"
);
builder.finish()
};
programmable_transactions::execution::execute::<execution_mode::System>(
protocol_config,
metrics,
move_vm,
temporary_store,
tx_ctx,
gas_charger,
pt,
)
}
fn setup_authenticator_state_create(
mut builder: ProgrammableTransactionBuilder,
) -> ProgrammableTransactionBuilder {
builder
.move_call(
SUI_FRAMEWORK_ADDRESS.into(),
AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME.to_owned(),
vec![],
vec![],
)
.expect("Unable to generate authenticator_state_create transaction!");
builder
}
fn setup_authenticator_state_update(
update: AuthenticatorStateUpdate,
temporary_store: &mut TemporaryStore<'_>,
tx_ctx: &mut TxContext,
move_vm: &Arc<MoveVM>,
gas_charger: &mut GasCharger,
protocol_config: &ProtocolConfig,
metrics: Arc<LimitsMetrics>,
) -> Result<(), ExecutionError> {
let pt = {
let mut builder = ProgrammableTransactionBuilder::new();
let res = builder.move_call(
SUI_FRAMEWORK_ADDRESS.into(),
AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME.to_owned(),
vec![],
vec![
CallArg::Object(ObjectArg::SharedObject {
id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
initial_shared_version: update.authenticator_obj_initial_shared_version,
mutable: true,
}),
CallArg::Pure(bcs::to_bytes(&update.new_active_jwks).unwrap()),
],
);
assert_invariant!(
res.is_ok(),
"Unable to generate authenticator_state_update transaction!"
);
builder.finish()
};
programmable_transactions::execution::execute::<execution_mode::System>(
protocol_config,
metrics,
move_vm,
temporary_store,
tx_ctx,
gas_charger,
pt,
)
}
fn setup_authenticator_state_expire(
mut builder: ProgrammableTransactionBuilder,
expire: AuthenticatorStateExpire,
) -> ProgrammableTransactionBuilder {
builder
.move_call(
SUI_FRAMEWORK_ADDRESS.into(),
AUTHENTICATOR_STATE_MODULE_NAME.to_owned(),
AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME.to_owned(),
vec![],
vec![
CallArg::Object(ObjectArg::SharedObject {
id: SUI_AUTHENTICATOR_STATE_OBJECT_ID,
initial_shared_version: expire.authenticator_obj_initial_shared_version,
mutable: true,
}),
CallArg::Pure(bcs::to_bytes(&expire.min_epoch).unwrap()),
],
)
.expect("Unable to generate authenticator_state_expire transaction!");
builder
}
}