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