1use crate::{
5 execution_mode::ExecutionMode,
6 gas_charger::GasCharger,
7 sp,
8 static_programmable_transactions::{
9 env::Env,
10 execution::context::{Context, CtxValue},
11 execution::trace_utils,
12 typing::ast as T,
13 },
14};
15use move_core_types::account_address::AccountAddress;
16use move_trace_format::format::MoveTraceBuilder;
17use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant};
18use sui_types::{
19 base_types::TxContext,
20 error::{ExecutionError, ExecutionErrorKind},
21 execution::{ExecutionTiming, ResultWithTimings},
22 execution_status::PackageUpgradeError,
23 metrics::LimitsMetrics,
24 move_package::MovePackage,
25 object::Owner,
26};
27use tracing::instrument;
28
29pub fn execute<'env, 'pc, 'vm, 'state, 'linkage, Mode: ExecutionMode>(
30 env: &'env mut Env<'pc, 'vm, 'state, 'linkage>,
31 metrics: Arc<LimitsMetrics>,
32 tx_context: Rc<RefCell<TxContext>>,
33 gas_charger: &mut GasCharger,
34 ast: T::Transaction,
35 trace_builder_opt: &mut Option<MoveTraceBuilder>,
36) -> ResultWithTimings<Mode::ExecutionResults, ExecutionError>
37where
38 'pc: 'state,
39 'env: 'state,
40{
41 let mut timings = vec![];
42 let result = execute_inner::<Mode>(
43 &mut timings,
44 env,
45 metrics,
46 tx_context,
47 gas_charger,
48 ast,
49 trace_builder_opt,
50 );
51
52 match result {
53 Ok(result) => Ok((result, timings)),
54 Err(e) => {
55 trace_utils::trace_execution_error(trace_builder_opt, e.to_string());
56 Err((e, timings))
57 }
58 }
59}
60
61pub fn execute_inner<'env, 'pc, 'vm, 'state, 'linkage, Mode: ExecutionMode>(
62 timings: &mut Vec<ExecutionTiming>,
63 env: &'env mut Env<'pc, 'vm, 'state, 'linkage>,
64 metrics: Arc<LimitsMetrics>,
65 tx_context: Rc<RefCell<TxContext>>,
66 gas_charger: &mut GasCharger,
67 ast: T::Transaction,
68 trace_builder_opt: &mut Option<MoveTraceBuilder>,
69) -> Result<Mode::ExecutionResults, ExecutionError>
70where
71 'pc: 'state,
72{
73 debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
74 let T::Transaction {
75 gas_coin,
76 bytes,
77 objects,
78 withdrawals,
79 pure,
80 receiving,
81 withdrawal_compatibility_conversions: _,
82 commands,
83 } = ast;
84 let mut context = Context::new(
85 env,
86 metrics,
87 tx_context,
88 gas_charger,
89 gas_coin,
90 bytes,
91 objects,
92 withdrawals,
93 pure,
94 receiving,
95 )?;
96
97 trace_utils::trace_ptb_summary(&mut context, trace_builder_opt, &commands)?;
98
99 let mut mode_results = Mode::empty_results();
100 for sp!(idx, c) in commands {
101 let start = Instant::now();
102 if let Err(err) =
103 execute_command::<Mode>(&mut context, &mut mode_results, c, trace_builder_opt)
104 {
105 let object_runtime = context.object_runtime()?;
106 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
108 drop(context);
110 env.state_view
113 .save_loaded_runtime_objects(loaded_runtime_objects);
114 timings.push(ExecutionTiming::Abort(start.elapsed()));
115 return Err(err.with_command_index(idx as usize));
116 };
117 timings.push(ExecutionTiming::Success(start.elapsed()));
118 }
119 let object_runtime = context.object_runtime()?;
121 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
125 let wrapped_object_containers = object_runtime.wrapped_object_containers();
128 let generated_object_ids = object_runtime.generated_object_ids();
130
131 let finished = context.finish::<Mode>();
133 env.state_view
135 .save_loaded_runtime_objects(loaded_runtime_objects);
136 env.state_view
137 .save_wrapped_object_containers(wrapped_object_containers);
138 env.state_view.record_execution_results(finished?)?;
139 env.state_view
140 .record_generated_object_ids(generated_object_ids);
141 Ok(mode_results)
142}
143
144#[instrument(level = "trace", skip_all)]
146fn execute_command<Mode: ExecutionMode>(
147 context: &mut Context,
148 mode_results: &mut Mode::ExecutionResults,
149 c: T::Command_,
150 trace_builder_opt: &mut Option<MoveTraceBuilder>,
151) -> Result<(), ExecutionError> {
152 let T::Command_ {
153 command,
154 result_type,
155 drop_values,
156 consumed_shared_objects: _,
157 } = c;
158 assert_invariant!(
159 context.gas_charger.move_gas_status().stack_height_current() == 0,
160 "stack height did not start at 0"
161 );
162 let is_move_call = matches!(command, T::Command__::MoveCall(_));
163 let num_args = command.arguments_len();
164 let mut args_to_update = vec![];
165 let result = match command {
166 T::Command__::MoveCall(move_call) => {
167 trace_utils::trace_move_call_start(trace_builder_opt);
168 let T::MoveCall {
169 function,
170 arguments,
171 } = *move_call;
172 if Mode::TRACK_EXECUTION {
173 args_to_update.extend(
174 arguments
175 .iter()
176 .filter(|arg| matches!(&arg.value.1, T::Type::Reference(true, _)))
177 .cloned(),
178 )
179 }
180 let arguments = context.arguments(arguments)?;
181 let res = context.vm_move_call(function, arguments, trace_builder_opt);
182 trace_utils::trace_move_call_end(trace_builder_opt);
183 res?
184 }
185 T::Command__::TransferObjects(objects, recipient) => {
186 let object_tys = objects
187 .iter()
188 .map(|sp!(_, (_, ty))| ty.clone())
189 .collect::<Vec<_>>();
190 let object_values: Vec<CtxValue> = context.arguments(objects)?;
191 let recipient: AccountAddress = context.argument(recipient)?;
192 assert_invariant!(
193 object_values.len() == object_tys.len(),
194 "object values and types mismatch"
195 );
196 trace_utils::trace_transfer(context, trace_builder_opt, &object_values, &object_tys)?;
197 for (object_value, ty) in object_values.into_iter().zip(object_tys) {
198 let recipient = Owner::AddressOwner(recipient.into());
200 context.transfer_object(recipient, ty, object_value)?;
201 }
202 vec![]
203 }
204 T::Command__::SplitCoins(ty, coin, amounts) => {
205 let mut trace_values = vec![];
206 if Mode::TRACK_EXECUTION {
208 args_to_update.push(coin.clone());
209 }
210 let coin_ref: CtxValue = context.argument(coin)?;
211 let amount_values: Vec<u64> = context.arguments(amounts)?;
212 let mut total: u64 = 0;
213 for amount in &amount_values {
214 let Some(new_total) = total.checked_add(*amount) else {
215 return Err(ExecutionError::from_kind(
216 ExecutionErrorKind::CoinBalanceOverflow,
217 ));
218 };
219 total = new_total;
220 }
221 trace_utils::add_move_value_info_from_ctx_value(
222 context,
223 trace_builder_opt,
224 &mut trace_values,
225 &ty,
226 &coin_ref,
227 )?;
228 let coin_value = context.copy_value(&coin_ref)?.coin_ref_value()?;
229 fp_ensure!(
230 coin_value >= total,
231 ExecutionError::new_with_source(
232 ExecutionErrorKind::InsufficientCoinBalance,
233 format!("balance: {coin_value} required: {total}")
234 )
235 );
236 coin_ref.coin_ref_subtract_balance(total)?;
237 let amounts = amount_values
238 .into_iter()
239 .map(|a| context.new_coin(a))
240 .collect::<Result<Vec<_>, _>>()?;
241 trace_utils::trace_split_coins(
242 context,
243 trace_builder_opt,
244 &ty,
245 trace_values,
246 &amounts,
247 total,
248 )?;
249
250 amounts
251 }
252 T::Command__::MergeCoins(ty, target, coins) => {
253 let mut trace_values = vec![];
254 if Mode::TRACK_EXECUTION {
256 args_to_update.push(target.clone());
257 }
258 let target_ref: CtxValue = context.argument(target)?;
259 trace_utils::add_move_value_info_from_ctx_value(
260 context,
261 trace_builder_opt,
262 &mut trace_values,
263 &ty,
264 &target_ref,
265 )?;
266 let coins = context.arguments(coins)?;
267 let amounts = coins
268 .into_iter()
269 .map(|coin| {
270 trace_utils::add_move_value_info_from_ctx_value(
271 context,
272 trace_builder_opt,
273 &mut trace_values,
274 &ty,
275 &coin,
276 )?;
277 context.destroy_coin(coin)
278 })
279 .collect::<Result<Vec<_>, _>>()?;
280 let mut additional: u64 = 0;
281 for amount in amounts {
282 let Some(new_additional) = additional.checked_add(amount) else {
283 return Err(ExecutionError::from_kind(
284 ExecutionErrorKind::CoinBalanceOverflow,
285 ));
286 };
287 additional = new_additional;
288 }
289 let target_value = context.copy_value(&target_ref)?.coin_ref_value()?;
290 fp_ensure!(
291 target_value.checked_add(additional).is_some(),
292 ExecutionError::from_kind(ExecutionErrorKind::CoinBalanceOverflow,)
293 );
294 target_ref.coin_ref_add_balance(additional)?;
295 trace_utils::trace_merge_coins(
296 context,
297 trace_builder_opt,
298 &ty,
299 trace_values,
300 additional,
301 )?;
302 vec![]
303 }
304 T::Command__::MakeMoveVec(ty, items) => {
305 let items: Vec<CtxValue> = context.arguments(items)?;
306 trace_utils::trace_make_move_vec(context, trace_builder_opt, &items, &ty)?;
307 vec![CtxValue::vec_pack(ty, items)?]
308 }
309 T::Command__::Publish(module_bytes, dep_ids, linkage) => {
310 trace_utils::trace_publish_event(trace_builder_opt)?;
311 let modules =
312 context.deserialize_modules(&module_bytes, false)?;
313
314 let runtime_id = context.publish_and_init_package::<Mode>(
315 modules,
316 &dep_ids,
317 linkage,
318 trace_builder_opt,
319 )?;
320
321 if <Mode>::packages_are_predefined() {
322 std::vec![]
324 } else {
325 std::vec![context.new_upgrade_cap(runtime_id)?]
326 }
327 }
328 T::Command__::Upgrade(
329 module_bytes,
330 dep_ids,
331 current_package_id,
332 upgrade_ticket,
333 linkage,
334 ) => {
335 trace_utils::trace_upgrade_event(trace_builder_opt)?;
336 let upgrade_ticket = context
337 .argument::<CtxValue>(upgrade_ticket)?
338 .into_upgrade_ticket()?;
339 if current_package_id != upgrade_ticket.package.bytes {
341 return Err(ExecutionError::from_kind(
342 ExecutionErrorKind::PackageUpgradeError {
343 upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
344 package_id: current_package_id,
345 ticket_id: upgrade_ticket.package.bytes,
346 },
347 },
348 ));
349 }
350 let modules = context.deserialize_modules(&module_bytes, true)?;
352
353 let computed_digest = MovePackage::compute_digest_for_modules_and_deps(
354 &module_bytes,
355 &dep_ids,
356 true,
357 )
358 .to_vec();
359 if computed_digest != upgrade_ticket.digest {
360 return Err(ExecutionError::from_kind(
361 ExecutionErrorKind::PackageUpgradeError {
362 upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
363 digest: computed_digest,
364 },
365 },
366 ));
367 }
368
369 let upgraded_package_id = context.upgrade(
370 modules,
371 &dep_ids,
372 current_package_id,
373 upgrade_ticket.policy,
374 linkage,
375 )?;
376
377 vec![context.upgrade_receipt(upgrade_ticket, upgraded_package_id)]
378 }
379 };
380 if Mode::TRACK_EXECUTION {
381 let argument_updates = context.argument_updates(args_to_update)?;
382 let command_result = context.tracked_results(&result, &result_type)?;
383 Mode::finish_command_v2(mode_results, argument_updates, command_result)?;
384 }
385 assert_invariant!(
386 result.len() == drop_values.len(),
387 "result values and drop values mismatch"
388 );
389 context.charge_command(is_move_call, num_args, result.len())?;
390 let result = result
391 .into_iter()
392 .zip(drop_values)
393 .map(|(value, drop)| if !drop { Some(value) } else { None })
394 .collect::<Vec<_>>();
395 context.result(result)?;
396 assert_invariant!(
397 context.gas_charger.move_gas_status().stack_height_current() == 0,
398 "stack height did not end at 0"
399 );
400 Ok(())
401}