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 bytes,
76 objects,
77 withdrawals,
78 pure,
79 receiving,
80 commands,
81 } = ast;
82 let mut context = Context::new(
83 env,
84 metrics,
85 tx_context,
86 gas_charger,
87 bytes,
88 objects,
89 withdrawals,
90 pure,
91 receiving,
92 )?;
93
94 trace_utils::trace_ptb_summary(&mut context, trace_builder_opt, &commands)?;
95
96 let mut mode_results = Mode::empty_results();
97 for sp!(idx, c) in commands {
98 let start = Instant::now();
99 if let Err(err) =
100 execute_command::<Mode>(&mut context, &mut mode_results, c, trace_builder_opt)
101 {
102 let object_runtime = context.object_runtime()?;
103 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
105 drop(context);
107 env.state_view
110 .save_loaded_runtime_objects(loaded_runtime_objects);
111 timings.push(ExecutionTiming::Abort(start.elapsed()));
112 return Err(err.with_command_index(idx as usize));
113 };
114 timings.push(ExecutionTiming::Success(start.elapsed()));
115 }
116 let object_runtime = context.object_runtime()?;
118 let loaded_runtime_objects = object_runtime.loaded_runtime_objects();
122 let wrapped_object_containers = object_runtime.wrapped_object_containers();
125 let generated_object_ids = object_runtime.generated_object_ids();
127
128 let finished = context.finish::<Mode>();
130 env.state_view
132 .save_loaded_runtime_objects(loaded_runtime_objects);
133 env.state_view
134 .save_wrapped_object_containers(wrapped_object_containers);
135 env.state_view.record_execution_results(finished?)?;
136 env.state_view
137 .record_generated_object_ids(generated_object_ids);
138 Ok(mode_results)
139}
140
141#[instrument(level = "trace", skip_all)]
143fn execute_command<Mode: ExecutionMode>(
144 context: &mut Context,
145 mode_results: &mut Mode::ExecutionResults,
146 c: T::Command_,
147 trace_builder_opt: &mut Option<MoveTraceBuilder>,
148) -> Result<(), ExecutionError> {
149 let T::Command_ {
150 command,
151 result_type,
152 drop_values,
153 consumed_shared_objects: _,
154 } = c;
155 assert_invariant!(
156 context.gas_charger.move_gas_status().stack_height_current() == 0,
157 "stack height did not start at 0"
158 );
159 let is_move_call = matches!(command, T::Command__::MoveCall(_));
160 let num_args = command.arguments_len();
161 let mut args_to_update = vec![];
162 let result = match command {
163 T::Command__::MoveCall(move_call) => {
164 trace_utils::trace_move_call_start(trace_builder_opt);
165 let T::MoveCall {
166 function,
167 arguments,
168 } = *move_call;
169 if Mode::TRACK_EXECUTION {
170 args_to_update.extend(
171 arguments
172 .iter()
173 .filter(|arg| matches!(&arg.value.1, T::Type::Reference(true, _)))
174 .cloned(),
175 )
176 }
177 let arguments = context.arguments(arguments)?;
178 let res = context.vm_move_call(function, arguments, trace_builder_opt);
179 trace_utils::trace_move_call_end(trace_builder_opt);
180 res?
181 }
182 T::Command__::TransferObjects(objects, recipient) => {
183 let object_tys = objects
184 .iter()
185 .map(|sp!(_, (_, ty))| ty.clone())
186 .collect::<Vec<_>>();
187 let object_values: Vec<CtxValue> = context.arguments(objects)?;
188 let recipient: AccountAddress = context.argument(recipient)?;
189 assert_invariant!(
190 object_values.len() == object_tys.len(),
191 "object values and types mismatch"
192 );
193 trace_utils::trace_transfer(context, trace_builder_opt, &object_values, &object_tys)?;
194 for (object_value, ty) in object_values.into_iter().zip(object_tys) {
195 let recipient = Owner::AddressOwner(recipient.into());
197 context.transfer_object(recipient, ty, object_value)?;
198 }
199 vec![]
200 }
201 T::Command__::SplitCoins(ty, coin, amounts) => {
202 let mut trace_values = vec![];
203 if Mode::TRACK_EXECUTION {
205 args_to_update.push(coin.clone());
206 }
207 let coin_ref: CtxValue = context.argument(coin)?;
208 let amount_values: Vec<u64> = context.arguments(amounts)?;
209 let mut total: u64 = 0;
210 for amount in &amount_values {
211 let Some(new_total) = total.checked_add(*amount) else {
212 return Err(ExecutionError::from_kind(
213 ExecutionErrorKind::CoinBalanceOverflow,
214 ));
215 };
216 total = new_total;
217 }
218 trace_utils::add_move_value_info_from_ctx_value(
219 context,
220 trace_builder_opt,
221 &mut trace_values,
222 &ty,
223 &coin_ref,
224 )?;
225 let coin_value = context.copy_value(&coin_ref)?.coin_ref_value()?;
226 fp_ensure!(
227 coin_value >= total,
228 ExecutionError::new_with_source(
229 ExecutionErrorKind::InsufficientCoinBalance,
230 format!("balance: {coin_value} required: {total}")
231 )
232 );
233 coin_ref.coin_ref_subtract_balance(total)?;
234 let amounts = amount_values
235 .into_iter()
236 .map(|a| context.new_coin(a))
237 .collect::<Result<Vec<_>, _>>()?;
238 trace_utils::trace_split_coins(
239 context,
240 trace_builder_opt,
241 &ty,
242 trace_values,
243 &amounts,
244 total,
245 )?;
246
247 amounts
248 }
249 T::Command__::MergeCoins(ty, target, coins) => {
250 let mut trace_values = vec![];
251 if Mode::TRACK_EXECUTION {
253 args_to_update.push(target.clone());
254 }
255 let target_ref: CtxValue = context.argument(target)?;
256 trace_utils::add_move_value_info_from_ctx_value(
257 context,
258 trace_builder_opt,
259 &mut trace_values,
260 &ty,
261 &target_ref,
262 )?;
263 let coins = context.arguments(coins)?;
264 let amounts = coins
265 .into_iter()
266 .map(|coin| {
267 trace_utils::add_move_value_info_from_ctx_value(
268 context,
269 trace_builder_opt,
270 &mut trace_values,
271 &ty,
272 &coin,
273 )?;
274 context.destroy_coin(coin)
275 })
276 .collect::<Result<Vec<_>, _>>()?;
277 let mut additional: u64 = 0;
278 for amount in amounts {
279 let Some(new_additional) = additional.checked_add(amount) else {
280 return Err(ExecutionError::from_kind(
281 ExecutionErrorKind::CoinBalanceOverflow,
282 ));
283 };
284 additional = new_additional;
285 }
286 let target_value = context.copy_value(&target_ref)?.coin_ref_value()?;
287 fp_ensure!(
288 target_value.checked_add(additional).is_some(),
289 ExecutionError::from_kind(ExecutionErrorKind::CoinBalanceOverflow,)
290 );
291 target_ref.coin_ref_add_balance(additional)?;
292 trace_utils::trace_merge_coins(
293 context,
294 trace_builder_opt,
295 &ty,
296 trace_values,
297 additional,
298 )?;
299 vec![]
300 }
301 T::Command__::MakeMoveVec(ty, items) => {
302 let items: Vec<CtxValue> = context.arguments(items)?;
303 trace_utils::trace_make_move_vec(context, trace_builder_opt, &items, &ty)?;
304 vec![CtxValue::vec_pack(ty, items)?]
305 }
306 T::Command__::Publish(module_bytes, dep_ids, linkage) => {
307 trace_utils::trace_publish_event(trace_builder_opt)?;
308 let modules =
309 context.deserialize_modules(&module_bytes, false)?;
310
311 let runtime_id = context.publish_and_init_package::<Mode>(
312 modules,
313 &dep_ids,
314 linkage,
315 trace_builder_opt,
316 )?;
317
318 if <Mode>::packages_are_predefined() {
319 std::vec![]
321 } else {
322 std::vec![context.new_upgrade_cap(runtime_id)?]
323 }
324 }
325 T::Command__::Upgrade(
326 module_bytes,
327 dep_ids,
328 current_package_id,
329 upgrade_ticket,
330 linkage,
331 ) => {
332 trace_utils::trace_upgrade_event(trace_builder_opt)?;
333 let upgrade_ticket = context
334 .argument::<CtxValue>(upgrade_ticket)?
335 .into_upgrade_ticket()?;
336 if current_package_id != upgrade_ticket.package.bytes {
338 return Err(ExecutionError::from_kind(
339 ExecutionErrorKind::PackageUpgradeError {
340 upgrade_error: PackageUpgradeError::PackageIDDoesNotMatch {
341 package_id: current_package_id,
342 ticket_id: upgrade_ticket.package.bytes,
343 },
344 },
345 ));
346 }
347 let modules = context.deserialize_modules(&module_bytes, true)?;
349
350 let computed_digest = MovePackage::compute_digest_for_modules_and_deps(
351 &module_bytes,
352 &dep_ids,
353 true,
354 )
355 .to_vec();
356 if computed_digest != upgrade_ticket.digest {
357 return Err(ExecutionError::from_kind(
358 ExecutionErrorKind::PackageUpgradeError {
359 upgrade_error: PackageUpgradeError::DigestDoesNotMatch {
360 digest: computed_digest,
361 },
362 },
363 ));
364 }
365
366 let upgraded_package_id = context.upgrade(
367 modules,
368 &dep_ids,
369 current_package_id,
370 upgrade_ticket.policy,
371 linkage,
372 )?;
373
374 vec![context.upgrade_receipt(upgrade_ticket, upgraded_package_id)]
375 }
376 };
377 if Mode::TRACK_EXECUTION {
378 let argument_updates = context.argument_updates(args_to_update)?;
379 let command_result = context.tracked_results(&result, &result_type)?;
380 Mode::finish_command_v2(mode_results, argument_updates, command_result)?;
381 }
382 assert_invariant!(
383 result.len() == drop_values.len(),
384 "result values and drop values mismatch"
385 );
386 context.charge_command(is_move_call, num_args, result.len())?;
387 let result = result
388 .into_iter()
389 .zip(drop_values)
390 .map(|(value, drop)| if !drop { Some(value) } else { None })
391 .collect::<Vec<_>>();
392 context.result(result)?;
393 assert_invariant!(
394 context.gas_charger.move_gas_status().stack_height_current() == 0,
395 "stack height did not end at 0"
396 );
397 Ok(())
398}