sui_adapter_latest/static_programmable_transactions/execution/
interpreter.rsuse crate::{
execution_mode::ExecutionMode,
gas_charger::GasCharger,
sp,
static_programmable_transactions::{
env::Env,
execution::context::{Context, CtxValue},
typing::ast as T,
},
};
use move_core_types::account_address::AccountAddress;
use move_trace_format::format::MoveTraceBuilder;
use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant};
use sui_types::{
base_types::TxContext,
error::{ExecutionError, ExecutionErrorKind},
execution::{ExecutionTiming, ResultWithTimings},
execution_status::PackageUpgradeError,
metrics::LimitsMetrics,
move_package::MovePackage,
object::Owner,
};
use tracing::instrument;
pub fn execute<'env, 'pc, 'vm, 'state, 'linkage, Mode: ExecutionMode>(
env: &'env mut Env<'pc, 'vm, 'state, 'linkage>,
metrics: Arc<LimitsMetrics>,
tx_context: Rc<RefCell<TxContext>>,
gas_charger: &mut GasCharger,
ast: T::Transaction,
trace_builder_opt: &mut Option<MoveTraceBuilder>,
) -> ResultWithTimings<Mode::ExecutionResults, ExecutionError>
where
'pc: 'state,
'env: 'state,
{
let mut timings = vec![];
let result = execute_inner::<Mode>(
&mut timings,
env,
metrics,
tx_context,
gas_charger,
ast,
trace_builder_opt,
);
match result {
Ok(result) => Ok((result, timings)),
Err(e) => Err((e, timings)),
}
}
pub fn execute_inner<'env, 'pc, 'vm, 'state, 'linkage, Mode: ExecutionMode>(
timings: &mut Vec<ExecutionTiming>,
env: &'env mut Env<'pc, 'vm, 'state, 'linkage>,
metrics: Arc<LimitsMetrics>,
tx_context: Rc<RefCell<TxContext>>,
gas_charger: &mut GasCharger,
ast: T::Transaction,
trace_builder_opt: &mut Option<MoveTraceBuilder>,
) -> Result<Mode::ExecutionResults, ExecutionError>
where
'pc: 'state,
{
let T::Transaction { inputs, commands } = ast;
let mut context = Context::new(env, metrics, tx_context, gas_charger, inputs)?;
let mut mode_results = Mode::empty_results();
for (sp!(idx, command), tys) in commands {
let start = Instant::now();
if let Err(err) = execute_command::<Mode>(
&mut context,
&mut mode_results,
command,
tys,
trace_builder_opt.as_mut(),
) {
let object_runtime = context.object_runtime()?;
let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
drop(context);
env.state_view
.save_loaded_runtime_objects(loaded_runtime_objects);
timings.push(ExecutionTiming::Abort(start.elapsed()));
return Err(err.with_command_index(idx as usize));
};
timings.push(ExecutionTiming::Success(start.elapsed()));
}
let object_runtime = context.object_runtime()?;
let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
let wrapped_object_containers = object_runtime.wrapped_object_containers();
let finished = context.finish::<Mode>();
env.state_view
.save_loaded_runtime_objects(loaded_runtime_objects);
env.state_view
.save_wrapped_object_containers(wrapped_object_containers);
env.state_view.record_execution_results(finished?);
Ok(mode_results)
}
#[instrument(level = "trace", skip_all)]
fn execute_command<Mode: ExecutionMode>(
context: &mut Context,
mode_results: &mut Mode::ExecutionResults,
command: T::Command_,
result_tys: T::ResultType,
trace_builder_opt: Option<&mut MoveTraceBuilder>,
) -> Result<(), ExecutionError> {
let mut args_to_update = vec![];
let result = match command {
T::Command_::MoveCall(move_call) => {
let T::MoveCall {
function,
arguments,
} = *move_call;
if Mode::TRACK_EXECUTION {
args_to_update.extend(
arguments
.iter()
.filter(|arg| matches!(&arg.value.1, T::Type::Reference(true, _)))
.map(|sp!(_, (arg, ty))| (arg.location(), ty.clone())),
)
}
let arguments = context.arguments(arguments)?;
context.vm_move_call(function, arguments, trace_builder_opt)?
}
T::Command_::TransferObjects(objects, recipient) => {
let object_tys = objects
.iter()
.map(|sp!(_, (_, ty))| ty.clone())
.collect::<Vec<_>>();
let object_values: Vec<CtxValue> = context.arguments(objects)?;
let recipient: AccountAddress = context.argument(recipient)?;
assert_invariant!(
object_values.len() == object_tys.len(),
"object values and types mismatch"
);
for (object_value, ty) in object_values.into_iter().zip(object_tys) {
let recipient = Owner::AddressOwner(recipient.into());
context.transfer_object(recipient, ty, object_value)?;
}
vec![]
}
T::Command_::SplitCoins(_, coin, amounts) => {
if Mode::TRACK_EXECUTION {
args_to_update.push((coin.value.0.location(), coin.value.1.clone()));
}
let coin_ref: CtxValue = context.argument(coin)?;
let amount_values: Vec<u64> = context.arguments(amounts)?;
let mut total: u64 = 0;
for amount in &amount_values {
let Some(new_total) = total.checked_add(*amount) else {
return Err(ExecutionError::from_kind(
ExecutionErrorKind::CoinBalanceOverflow,
));
};
total = new_total;
}
let coin_value = context.copy_value(&coin_ref)?.coin_ref_value()?;
fp_ensure!(
coin_value >= total,
ExecutionError::new_with_source(
ExecutionErrorKind::InsufficientCoinBalance,
format!("balance: {coin_value} required: {total}")
)
);
coin_ref.coin_ref_subtract_balance(total)?;
amount_values
.into_iter()
.map(|a| context.new_coin(a))
.collect::<Result<_, _>>()?
}
T::Command_::MergeCoins(_, target, coins) => {
if Mode::TRACK_EXECUTION {
args_to_update.push((target.value.0.location(), target.value.1.clone()));
}
let target_ref: CtxValue = context.argument(target)?;
let coins = context.arguments(coins)?;
let amounts = coins
.into_iter()
.map(|coin| context.destroy_coin(coin))
.collect::<Result<Vec<_>, _>>()?;
let mut additional: u64 = 0;
for amount in amounts {
let Some(new_additional) = additional.checked_add(amount) else {
return Err(ExecutionError::from_kind(
ExecutionErrorKind::CoinBalanceOverflow,
));
};
additional = new_additional;
}
let target_value = context.copy_value(&target_ref)?.coin_ref_value()?;
fp_ensure!(
target_value.checked_add(additional).is_some(),
ExecutionError::from_kind(ExecutionErrorKind::CoinBalanceOverflow,)
);
target_ref.coin_ref_add_balance(additional)?;
vec![]
}
T::Command_::MakeMoveVec(ty, items) => {
let items: Vec<CtxValue> = context.arguments(items)?;
vec![CtxValue::vec_pack(ty, items)?]
}
T::Command_::Publish(module_bytes, dep_ids, linkage) => {
let modules =
context.deserialize_modules(&module_bytes, false)?;
let runtime_id = context.publish_and_init_package::<Mode>(
modules,
&dep_ids,
linkage,
trace_builder_opt,
)?;
if <Mode>::packages_are_predefined() {
std::vec![]
} else {
std::vec![context.new_upgrade_cap(runtime_id)?]
}
}
T::Command_::Upgrade(
module_bytes,
dep_ids,
current_package_id,
upgrade_ticket,
linkage,
) => {
let upgrade_ticket = context
.argument::<CtxValue>(upgrade_ticket)?
.into_upgrade_ticket()?;
if current_package_id != upgrade_ticket.package.bytes {
return Err(ExecutionError::from_kind(
ExecutionErrorKind::PackageUpgradeError {
upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
package_id: current_package_id,
ticket_id: upgrade_ticket.package.bytes,
},
},
));
}
let modules = context.deserialize_modules(&module_bytes, true)?;
let computed_digest = MovePackage::compute_digest_for_modules_and_deps(
&module_bytes,
&dep_ids,
true,
)
.to_vec();
if computed_digest != upgrade_ticket.digest {
return Err(ExecutionError::from_kind(
ExecutionErrorKind::PackageUpgradeError {
upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
digest: computed_digest,
},
},
));
}
let upgraded_package_id = context.upgrade(
modules,
&dep_ids,
current_package_id,
upgrade_ticket.policy,
linkage,
)?;
vec![context.upgrade_receipt(upgrade_ticket, upgraded_package_id)]
}
};
if Mode::TRACK_EXECUTION {
let argument_updates = context.location_updates(args_to_update)?;
let command_result = context.tracked_results(&result, &result_tys)?;
Mode::finish_command_v2(mode_results, argument_updates, command_result)?;
}
context.result(result)?;
Ok(())
}