1use super::{ast as T, env::Env};
5use crate::{
6 execution_mode::ExecutionMode,
7 gas_charger::GasPayment,
8 static_programmable_transactions::{
9 execution::context::EitherError,
10 linkage::resolved_linkage::ExecutableLinkage,
11 loading::ast::{self as L, Type},
12 spanned::sp,
13 typing::ast::BytesConstraint,
14 },
15};
16use indexmap::{IndexMap, IndexSet};
17use move_binary_format::file_format::{Ability, AbilitySet};
18use move_core_types::account_address::AccountAddress;
19use std::rc::Rc;
20use sui_types::{
21 balance::RESOLVED_BALANCE_STRUCT,
22 base_types::{ObjectRef, TxContextKind},
23 coin::{COIN_MODULE_NAME, REDEEM_FUNDS_FUNC_NAME, RESOLVED_COIN_STRUCT},
24 error::{ExecutionError, ExecutionErrorTrait, SafeIndex, command_argument_error},
25 execution_status::{CommandArgumentError, ExecutionErrorKind},
26 funds_accumulator::RESOLVED_WITHDRAWAL_STRUCT,
27};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30enum SplatLocation {
31 GasCoin,
32 Input(T::InputIndex),
33 Result(u16, u16),
34}
35
36#[derive(Debug, Clone, Copy)]
37enum InputKind {
38 Object,
39 Withdrawal,
40 Pure,
41 Receiving,
42}
43
44struct Context {
45 current_command: u16,
46 gas_payment: Option<GasPayment>,
47 input_resolution: Vec<InputKind>,
49 bytes: IndexSet<Vec<u8>>,
50 bytes_idx_remapping: IndexMap<T::InputIndex, T::ByteIndex>,
52 receiving_refs: IndexMap<T::InputIndex, ObjectRef>,
53 objects: IndexMap<T::InputIndex, T::ObjectInput>,
54 withdrawals: IndexMap<T::InputIndex, T::WithdrawalInput>,
55 pure: IndexMap<(T::InputIndex, Type), T::PureInput>,
56 receiving: IndexMap<(T::InputIndex, Type), T::ReceivingInput>,
57 withdrawal_compatibility_conversions:
58 IndexMap<T::Location, T::WithdrawalCompatibilityConversion>,
59 original_command_len: usize,
60 commands: Vec<T::Command>,
61 unified_linkage: Option<ExecutableLinkage>,
62}
63
64impl Context {
65 fn new(
66 gas_payment: Option<GasPayment>,
67 original_command_len: usize,
68 linputs: L::Inputs,
69 unified_linkage: Option<ExecutableLinkage>,
70 ) -> Result<Self, ExecutionError> {
71 let mut context = Context {
72 current_command: 0,
73 gas_payment,
74 input_resolution: vec![],
75 original_command_len,
76 bytes: IndexSet::new(),
77 bytes_idx_remapping: IndexMap::new(),
78 receiving_refs: IndexMap::new(),
79 objects: IndexMap::new(),
80 withdrawals: IndexMap::new(),
81 pure: IndexMap::new(),
82 withdrawal_compatibility_conversions: IndexMap::new(),
83 receiving: IndexMap::new(),
84 commands: vec![],
85 unified_linkage,
86 };
87 #[cfg(debug_assertions)]
89 let cloned_inputs = linputs
90 .iter()
91 .map(|(arg, _)| arg.clone())
92 .collect::<Vec<_>>();
93 for (i, (arg, ty)) in linputs.into_iter().enumerate() {
96 let idx = T::InputIndex(checked_as!(i, u16)?);
97 let kind = match (arg, ty) {
98 (L::InputArg::Pure(bytes), L::InputType::Bytes) => {
99 let (byte_index, _) = context.bytes.insert_full(bytes);
100 context.bytes_idx_remapping.insert(idx, byte_index);
101 InputKind::Pure
102 }
103 (L::InputArg::Receiving(oref), L::InputType::Bytes) => {
104 context.receiving_refs.insert(idx, oref);
105 InputKind::Receiving
106 }
107 (L::InputArg::Object(arg), L::InputType::Fixed(ty)) => {
108 let o = T::ObjectInput {
109 original_input_index: idx,
110 arg,
111 ty,
112 };
113 context.objects.insert(idx, o);
114 InputKind::Object
115 }
116 (L::InputArg::FundsWithdrawal(withdrawal), L::InputType::Fixed(input_ty)) => {
117 let L::FundsWithdrawalArg {
118 from_compatibility_object: _,
119 ty,
120 source,
121 amount,
122 } = withdrawal;
123 debug_assert!(ty == input_ty);
124 let withdrawal = T::WithdrawalInput {
125 original_input_index: idx,
126 ty,
127 source,
128 amount,
129 };
130 context.withdrawals.insert(idx, withdrawal);
131 InputKind::Withdrawal
132 }
133 (arg, ty) => invariant_violation!(
134 "Input arg, type mismatch. Unexpected {arg:?} with type {ty:?}"
135 ),
136 };
137 context.input_resolution.push(kind);
138 }
139 #[cfg(debug_assertions)]
140 {
141 for (i, arg) in cloned_inputs.iter().enumerate() {
143 if let L::InputArg::Pure(bytes) = &arg {
144 let idx = T::InputIndex(checked_as!(i, u16)?);
145 let Some(byte_index) = context.bytes_idx_remapping.get(&idx) else {
146 invariant_violation!("Unbound pure input {}", idx.0);
147 };
148 let Some(interned_bytes) = context.bytes.get_index(*byte_index) else {
149 invariant_violation!("Interned bytes not found for index {}", byte_index);
150 };
151 if interned_bytes != bytes {
152 assert_invariant!(
153 interned_bytes == bytes,
154 "Interned bytes mismatch for input {i}",
155 );
156 }
157 }
158 }
159 }
160 Ok(context)
161 }
162
163 fn finish(self) -> T::Transaction {
164 let Self {
165 gas_payment,
166 bytes,
167 objects,
168 withdrawals,
169 pure,
170 receiving,
171 withdrawal_compatibility_conversions,
172 original_command_len,
173 commands,
174 unified_linkage,
175 ..
176 } = self;
177 let objects = objects.into_iter().map(|(_, o)| o).collect();
178 let withdrawals = withdrawals.into_iter().map(|(_, w)| w).collect();
179 let pure = pure.into_iter().map(|(_, p)| p).collect();
180 let receiving = receiving.into_iter().map(|(_, r)| r).collect();
181 T::Transaction {
182 gas_payment,
183 bytes,
184 objects,
185 withdrawals,
186 pure,
187 receiving,
188 withdrawal_compatibility_conversions,
189 original_command_len,
190 commands,
191 unified_linkage,
192 }
193 }
194
195 fn push_result(&mut self, command: T::Command_) -> Result<(), ExecutionError> {
196 self.commands.push(sp(self.current_command, command));
197 Ok(())
198 }
199
200 fn result_type(&self, i: u16) -> Option<&T::ResultType> {
201 self.commands.get(i as usize).map(|c| &c.value.result_type)
202 }
203
204 fn fixed_location_type<Mode: ExecutionMode>(
205 &mut self,
206 env: &Env<Mode>,
207 location: T::Location,
208 ) -> Result<Option<Type>, Mode::Error> {
209 Ok(Some(match location {
210 T::Location::TxContext => env.tx_context_type()?,
211 T::Location::GasCoin => env.gas_coin_type()?,
212 T::Location::Result(i, j) => {
213 let Some(tys) = self.result_type(i) else {
214 invariant_violation!("Result index {i} is out of bounds")
215 };
216 tys.safe_get(j as usize)?.clone()
217 }
218 T::Location::ObjectInput(i) => {
219 let Some((_, object_input)) = self.objects.get_index(i as usize) else {
220 invariant_violation!("Unbound object input {}", i)
221 };
222 object_input.ty.clone()
223 }
224 T::Location::WithdrawalInput(i) => {
225 let Some((_, withdrawal_input)) = self.withdrawals.get_index(i as usize) else {
226 invariant_violation!("Unbound withdrawal input {}", i)
227 };
228 withdrawal_input.ty.clone()
229 }
230 T::Location::PureInput(_) | T::Location::ReceivingInput(_) => return Ok(None),
231 }))
232 }
233
234 fn fixed_type<Mode: ExecutionMode>(
236 &mut self,
237 env: &Env<Mode>,
238 splat_location: SplatLocation,
239 ) -> Result<Option<(T::Location, Type)>, Mode::Error> {
240 let location = match splat_location {
241 SplatLocation::GasCoin => T::Location::GasCoin,
242 SplatLocation::Result(i, j) => T::Location::Result(i, j),
243 SplatLocation::Input(i) => match self.input_resolution.safe_get(i.0 as usize)? {
244 InputKind::Object => {
245 let Some(index) = self.objects.get_index_of(&i) else {
246 invariant_violation!("Unbound object input {}", i.0)
247 };
248 T::Location::ObjectInput(checked_as!(index, u16)?)
249 }
250 InputKind::Withdrawal => {
251 let Some(withdrawal_index) = self.withdrawals.get_index_of(&i) else {
252 invariant_violation!("Unbound withdrawal input {}", i.0)
253 };
254 T::Location::WithdrawalInput(checked_as!(withdrawal_index, u16)?)
255 }
256 InputKind::Pure | InputKind::Receiving => return Ok(None),
257 },
258 };
259 let Some(ty) = self.fixed_location_type(env, location)? else {
260 invariant_violation!("Location {location:?} does not have a fixed type")
261 };
262 Ok(Some((location, ty)))
263 }
264
265 fn resolve_location<Mode: ExecutionMode>(
266 &mut self,
267 env: &Env<Mode>,
268 splat_location: SplatLocation,
269 expected_ty: &Type,
270 bytes_constraint: BytesConstraint,
271 ) -> Result<(T::Location, Type), Mode::Error> {
272 let location = match splat_location {
273 SplatLocation::GasCoin => T::Location::GasCoin,
274 SplatLocation::Result(i, j) => T::Location::Result(i, j),
275 SplatLocation::Input(i) => match self.input_resolution.safe_get(i.0 as usize)? {
276 InputKind::Object => {
277 let Some(index) = self.objects.get_index_of(&i) else {
278 invariant_violation!("Unbound object input {}", i.0)
279 };
280 T::Location::ObjectInput(checked_as!(index, u16)?)
281 }
282 InputKind::Withdrawal => {
283 let Some(index) = self.withdrawals.get_index_of(&i) else {
284 invariant_violation!("Unbound withdrawal input {}", i.0)
285 };
286 T::Location::WithdrawalInput(checked_as!(index, u16)?)
287 }
288 InputKind::Pure => {
289 let ty = match expected_ty {
290 Type::Reference(_, inner) => (**inner).clone(),
291 ty => ty.clone(),
292 };
293 let k = (i, ty.clone());
294 if !self.pure.contains_key(&k) {
295 let Some(byte_index) = self.bytes_idx_remapping.get(&i).copied() else {
296 invariant_violation!("Unbound pure input {}", i.0);
297 };
298 let pure = T::PureInput {
299 original_input_index: i,
300 byte_index,
301 ty: ty.clone(),
302 constraint: bytes_constraint,
303 };
304 self.pure.insert(k.clone(), pure);
305 }
306 let byte_index = self.pure.get_index_of(&k).unwrap();
307 return Ok((T::Location::PureInput(checked_as!(byte_index, u16)?), ty));
308 }
309 InputKind::Receiving => {
310 let ty = match expected_ty {
311 Type::Reference(_, inner) => (**inner).clone(),
312 ty => ty.clone(),
313 };
314 let k = (i, ty.clone());
315 if !self.receiving.contains_key(&k) {
316 let Some(object_ref) = self.receiving_refs.get(&i).copied() else {
317 invariant_violation!("Unbound receiving input {}", i.0);
318 };
319 let receiving = T::ReceivingInput {
320 original_input_index: i,
321 object_ref,
322 ty: ty.clone(),
323 constraint: bytes_constraint,
324 };
325 self.receiving.insert(k.clone(), receiving);
326 }
327 let byte_index = self.receiving.get_index_of(&k).unwrap();
328 return Ok((
329 T::Location::ReceivingInput(checked_as!(byte_index, u16)?),
330 ty,
331 ));
332 }
333 },
334 };
335 let Some(ty) = self.fixed_location_type(env, location)? else {
336 invariant_violation!("Location {location:?} does not have a fixed type")
337 };
338 Ok((location, ty))
339 }
340}
341
342pub fn transaction<Mode: ExecutionMode>(
343 env: &Env<Mode>,
344 lt: L::Transaction,
345) -> Result<T::Transaction, Mode::Error> {
346 let L::Transaction {
347 gas_payment,
348 mut inputs,
349 original_command_len,
350 mut commands,
351 unified_linkage,
352 } = lt;
353 let withdrawal_compatability_inputs =
354 determine_withdrawal_compatibility_inputs(env, &mut inputs)?;
355 let mut context = Context::new(gas_payment, original_command_len, inputs, unified_linkage)?;
356 withdrawal_compatibility_conversion(
357 env,
358 &mut context,
359 withdrawal_compatability_inputs,
360 &mut commands,
361 )?;
362 for (i, c) in commands.into_iter().enumerate() {
363 let idx = checked_as!(i, u16)?;
364 context.current_command = idx;
365 let (c_, tys) =
366 command::<Mode>(env, &mut context, c).map_err(|e| e.with_command_index(i))?;
367 let c = T::Command_ {
368 command: c_,
369 result_type: tys,
370 drop_values: vec![],
372 incurs_post_execution_checks: false,
374 };
375 context.push_result(c)?
376 }
377 let mut ast = context.finish();
378 scope_references::transaction(env.protocol_config, &mut ast);
380 unused_results::transaction(&mut ast)?;
382 post_execution_checks::transaction(env.protocol_config, &mut ast)?;
384 Ok(ast)
385}
386
387fn command<Mode: ExecutionMode>(
388 env: &Env<Mode>,
389 context: &mut Context,
390 command: L::Command,
391) -> Result<(T::Command__, T::ResultType), Mode::Error> {
392 Ok(match command {
393 L::Command::MoveCall(lmc) => {
394 let L::MoveCall {
395 function,
396 arguments: largs,
397 } = *lmc;
398 let arg_locs = locations(context, 0, largs)?;
399 let args = move_call_arguments(env, context, &function, arg_locs)?;
400 let result = function.signature.return_.clone();
401 (
402 T::Command__::MoveCall(Box::new(T::MoveCall {
403 function,
404 arguments: args,
405 })),
406 result,
407 )
408 }
409 L::Command::TransferObjects(lobjects, laddress) => {
410 const TRANSFER_OBJECTS_CONSTRAINT: AbilitySet =
411 AbilitySet::singleton(Ability::Store).union(AbilitySet::singleton(Ability::Key));
412 let object_locs = locations(context, 0, lobjects)?;
413 let address_loc = one_location(context, object_locs.len(), laddress)?;
414 let objects = constrained_arguments(
415 env,
416 context,
417 0,
418 object_locs,
419 TRANSFER_OBJECTS_CONSTRAINT,
420 CommandArgumentError::InvalidTransferObject,
421 )?;
422 let address = argument(env, context, objects.len(), address_loc, Type::Address)?;
423 (T::Command__::TransferObjects(objects, address), vec![])
424 }
425 L::Command::SplitCoins(lcoin, lamounts) => {
426 let coin_loc = one_location(context, 0, lcoin)?;
427 let amount_locs = locations(context, 1, lamounts)?;
428 let coin = coin_mut_ref_argument(env, context, 0, coin_loc)?;
429 let coin_type = match &coin.value.1 {
430 Type::Reference(true, ty) => (**ty).clone(),
431 ty => invariant_violation!("coin must be a mutable reference. Found: {ty:?}"),
432 };
433 let amounts = arguments(
434 env,
435 context,
436 1,
437 amount_locs,
438 std::iter::repeat_with(|| Type::U64),
439 )?;
440 let result = vec![coin_type.clone(); amounts.len()];
441 (T::Command__::SplitCoins(coin_type, coin, amounts), result)
442 }
443 L::Command::MergeCoins(ltarget, lcoins) => {
444 let target_loc = one_location(context, 0, ltarget)?;
445 let coin_locs = locations(context, 1, lcoins)?;
446 let target = coin_mut_ref_argument(env, context, 0, target_loc)?;
447 let coin_type = match &target.value.1 {
448 Type::Reference(true, ty) => (**ty).clone(),
449 ty => invariant_violation!("target must be a mutable reference. Found: {ty:?}"),
450 };
451 let coins = arguments(
452 env,
453 context,
454 1,
455 coin_locs,
456 std::iter::repeat_with(|| coin_type.clone()),
457 )?;
458 (T::Command__::MergeCoins(coin_type, target, coins), vec![])
459 }
460 L::Command::MakeMoveVec(Some(ty), lelems) => {
461 let elem_locs = locations(context, 0, lelems)?;
462 let elems = arguments(
463 env,
464 context,
465 0,
466 elem_locs,
467 std::iter::repeat_with(|| ty.clone()),
468 )?;
469 (
470 T::Command__::MakeMoveVec(ty.clone(), elems),
471 vec![env.vector_type(ty)?],
472 )
473 }
474 L::Command::MakeMoveVec(None, lelems) => {
475 const MAKE_MOVE_VEC_OBJECT_CONSTRAINT: AbilitySet = AbilitySet::singleton(Ability::Key);
476 let mut lelems = lelems.into_iter();
477 let Some(lfirst) = lelems.next() else {
478 invariant_violation!(
480 "input checker ensures if args are empty, there is a type specified"
481 );
482 };
483 let first_loc = one_location(context, 0, lfirst)?;
484 let first_arg = constrained_argument(
485 env,
486 context,
487 0,
488 first_loc,
489 MAKE_MOVE_VEC_OBJECT_CONSTRAINT,
490 CommandArgumentError::InvalidMakeMoveVecNonObjectArgument,
491 )?;
492 let first_ty = first_arg.value.1.clone();
493 let elems_loc = locations(context, 1, lelems)?;
494 let mut elems = arguments(
495 env,
496 context,
497 1,
498 elems_loc,
499 std::iter::repeat_with(|| first_ty.clone()),
500 )?;
501 elems.insert(0, first_arg);
502 (
503 T::Command__::MakeMoveVec(first_ty.clone(), elems),
504 vec![env.vector_type(first_ty)?],
505 )
506 }
507 L::Command::Publish(items, object_ids, linkage) => {
508 let result = if Mode::packages_are_predefined() {
509 vec![]
511 } else {
512 vec![env.upgrade_cap_type()?.clone()]
513 };
514 (T::Command__::Publish(items, object_ids, linkage), result)
515 }
516 L::Command::Upgrade(items, object_ids, object_id, la, linkage) => {
517 let location = one_location(context, 0, la)?;
518 let expected_ty = env.upgrade_ticket_type()?;
519 let a = argument(env, context, 0, location, expected_ty)?;
520 let res = env.upgrade_receipt_type()?;
521 (
522 T::Command__::Upgrade(items, object_ids, object_id, a, linkage),
523 vec![res.clone()],
524 )
525 }
526 })
527}
528
529fn move_call_parameters<'a, Mode: ExecutionMode>(
530 _env: &Env<Mode>,
531 function: &'a L::LoadedFunction,
532) -> Vec<(&'a Type, TxContextKind)> {
533 function
534 .signature
535 .parameters
536 .iter()
537 .map(|ty| (ty, ty.is_tx_context()))
538 .collect()
539}
540
541fn move_call_arguments<Mode: ExecutionMode>(
542 env: &Env<Mode>,
543 context: &mut Context,
544 function: &L::LoadedFunction,
545 args: Vec<SplatLocation>,
546) -> Result<Vec<T::Argument>, Mode::Error> {
547 let params = move_call_parameters(env, function);
548 assert_invariant!(
549 params.len() == function.signature.parameters.len(),
550 "Generated parameter types does not match the function signature"
551 );
552 let num_tx_contexts = params
554 .iter()
555 .filter(|(_, k)| matches!(k, TxContextKind::Mutable | TxContextKind::Immutable))
556 .count();
557 let num_user_args = args.len();
558 let Some(num_args) = num_user_args.checked_add(num_tx_contexts) else {
559 invariant_violation!("usize overflow when calculating number of arguments");
560 };
561 let num_parameters = params.len();
562 if num_args != num_parameters {
563 return Err(Mode::Error::new_with_source(
564 ExecutionErrorKind::ArityMismatch,
565 format!(
566 "Expected {} argument{} calling function '{}::{}', but found {}",
567 num_parameters,
568 if num_parameters == 1 { "" } else { "s" },
569 function.version_mid,
570 function.name,
571 num_args,
572 ),
573 ));
574 }
575 let mut args = args.into_iter().enumerate();
577 let res = params
578 .into_iter()
579 .enumerate()
580 .map(|(param_idx, (expected_ty, tx_context_kind))| {
581 Ok(match tx_context_kind {
582 TxContextKind::None => {
583 let Some((arg_idx, location)) = args.next() else {
584 invariant_violation!("arguments are empty but arity was already checked");
585 };
586 argument(env, context, arg_idx, location, expected_ty.clone())?
587 }
588 TxContextKind::Mutable | TxContextKind::Immutable => {
589 let is_mut = match tx_context_kind {
590 TxContextKind::Mutable => true,
591 TxContextKind::Immutable => false,
592 TxContextKind::None => unreachable!(),
593 };
594 let idx = checked_as!(param_idx, u16)?;
597 let arg__ = T::Argument__::Borrow(is_mut, T::Location::TxContext);
598 let ty = Type::Reference(is_mut, Rc::new(env.tx_context_type()?));
599 sp(idx, (arg__, ty))
600 }
601 })
602 })
603 .collect::<Result<Vec<_>, Mode::Error>>()?;
604
605 assert_invariant!(
606 args.next().is_none(),
607 "some arguments went unused but arity was already checked"
608 );
609 Ok(res)
610}
611
612fn one_location<E: ExecutionErrorTrait>(
613 context: &mut Context,
614 command_arg_idx: usize,
615 arg: L::Argument,
616) -> Result<SplatLocation, E> {
617 let locs = locations(context, command_arg_idx, vec![arg])?;
618 let Ok([loc]): Result<[SplatLocation; 1], _> = locs.try_into() else {
619 return Err(command_argument_error(
620 CommandArgumentError::InvalidArgumentArity,
621 command_arg_idx,
622 )
623 .into());
624 };
625 Ok(loc)
626}
627
628fn locations<E: ExecutionErrorTrait, Items: IntoIterator<Item = L::Argument>>(
629 context: &mut Context,
630 start_idx: usize,
631 args: Items,
632) -> Result<Vec<SplatLocation>, E>
633where
634 Items::IntoIter: ExactSizeIterator,
635{
636 fn splat_arg<E: ExecutionErrorTrait>(
637 context: &mut Context,
638 res: &mut Vec<SplatLocation>,
639 arg: L::Argument,
640 ) -> Result<(), EitherError<E>> {
641 match arg {
642 L::Argument::GasCoin => res.push(SplatLocation::GasCoin),
643 L::Argument::Input(i) => {
644 if i as usize >= context.input_resolution.len() {
645 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
646 }
647 res.push(SplatLocation::Input(T::InputIndex(i)))
648 }
649 L::Argument::NestedResult(i, j) => {
650 let Some(command_result) = context.result_type(i) else {
651 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
652 };
653 if j as usize >= command_result.len() {
654 return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
655 result_idx: i,
656 secondary_idx: j,
657 }
658 .into());
659 };
660 res.push(SplatLocation::Result(i, j))
661 }
662 L::Argument::Result(i) => {
663 let Some(result) = context.result_type(i) else {
664 return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
665 };
666 let Ok(len): Result<u16, _> = result.len().try_into() else {
667 invariant_violation!("Result of length greater than u16::MAX");
668 };
669 if len != 1 {
670 return Err(CommandArgumentError::InvalidResultArity { result_idx: i }.into());
672 }
673 res.extend((0..len).map(|j| SplatLocation::Result(i, j)))
674 }
675 }
676 Ok(())
677 }
678
679 let args = args.into_iter();
680 let _args_len = args.len();
681 let mut res = vec![];
682 for (arg_idx, arg) in args.enumerate() {
683 splat_arg::<E>(context, &mut res, arg).map_err(|e| {
684 let Some(idx) = start_idx.checked_add(arg_idx) else {
685 return make_invariant_violation!("usize overflow when calculating argument index")
686 .into();
687 };
688 e.into_execution_error(idx)
689 })?
690 }
691 debug_assert_eq!(res.len(), _args_len);
692 Ok(res)
693}
694
695fn arguments<Mode: ExecutionMode>(
696 env: &Env<Mode>,
697 context: &mut Context,
698 start_idx: usize,
699 locations: Vec<SplatLocation>,
700 expected_tys: impl IntoIterator<Item = Type>,
701) -> Result<Vec<T::Argument>, Mode::Error> {
702 #[allow(clippy::disallowed_methods)]
703 locations
704 .into_iter()
705 .zip(expected_tys)
708 .enumerate()
709 .map(|(i, (location, expected_ty))| {
710 let Some(idx) = start_idx.checked_add(i) else {
711 invariant_violation!("usize overflow when calculating argument index");
712 };
713 argument(env, context, idx, location, expected_ty)
714 })
715 .collect()
716}
717
718fn argument<Mode: ExecutionMode>(
719 env: &Env<Mode>,
720 context: &mut Context,
721 command_arg_idx: usize,
722 location: SplatLocation,
723 expected_ty: Type,
724) -> Result<T::Argument, Mode::Error> {
725 let arg__ = argument_(env, context, command_arg_idx, location, &expected_ty)
726 .map_err(|e| e.into_execution_error(command_arg_idx))?;
727 let arg_ = (arg__, expected_ty);
728 Ok(sp(checked_as!(command_arg_idx, u16)?, arg_))
729}
730
731fn argument_<Mode: ExecutionMode>(
732 env: &Env<Mode>,
733 context: &mut Context,
734 command_arg_idx: usize,
735 location: SplatLocation,
736 expected_ty: &Type,
737) -> Result<T::Argument__, EitherError<Mode::Error>> {
738 let current_command = context.current_command;
739 let bytes_constraint = BytesConstraint {
740 command: current_command,
741 argument: checked_as!(command_arg_idx, u16)?,
742 };
743 let (location, actual_ty) = context
744 .resolve_location(env, location, expected_ty, bytes_constraint)
745 .map_err(EitherError::Execution)?;
746 Ok(match (actual_ty, expected_ty) {
747 (Type::Reference(a_is_mut, a), Type::Reference(b_is_mut, b)) => {
749 let needs_freeze = match (a_is_mut, b_is_mut) {
750 (true, true) | (false, false) => false,
752 (true, false) => true,
754 (false, true) => return Err(CommandArgumentError::TypeMismatch.into()),
756 };
757 debug_assert!(expected_ty.abilities().has_copy());
758 check_type(&a, b)?;
760 if needs_freeze {
761 T::Argument__::Freeze(T::Usage::new_copy(location))
762 } else {
763 T::Argument__::new_copy(location)
764 }
765 }
766 (Type::Reference(_, a), b) => {
767 check_type(&a, b)?;
768 if !b.abilities().has_copy() {
769 return Err(CommandArgumentError::TypeMismatch.into());
771 }
772 T::Argument__::Read(T::Usage::new_copy(location))
773 }
774
775 (actual_ty, Type::Reference(is_mut, inner)) => {
777 check_type(&actual_ty, inner)?;
778 T::Argument__::Borrow(*is_mut, location)
779 }
780 (actual_ty, _) => {
781 check_type(&actual_ty, expected_ty)?;
782 T::Argument__::Use(if expected_ty.abilities().has_copy() {
783 T::Usage::new_copy(location)
784 } else {
785 T::Usage::new_move(location)
786 })
787 }
788 })
789}
790
791fn check_type(actual_ty: &Type, expected_ty: &Type) -> Result<(), CommandArgumentError> {
792 if actual_ty == expected_ty {
793 Ok(())
794 } else {
795 Err(CommandArgumentError::TypeMismatch)
796 }
797}
798
799fn constrained_arguments<Mode: ExecutionMode>(
800 env: &Env<Mode>,
801 context: &mut Context,
802 start_idx: usize,
803 locations: Vec<SplatLocation>,
804 constraint: AbilitySet,
805 err_case: CommandArgumentError,
806) -> Result<Vec<T::Argument>, Mode::Error> {
807 locations
808 .into_iter()
809 .enumerate()
810 .map(|(i, location)| {
811 let Some(idx) = start_idx.checked_add(i) else {
812 invariant_violation!("usize overflow when calculating argument index");
813 };
814 constrained_argument(env, context, idx, location, constraint, err_case)
815 })
816 .collect()
817}
818
819fn constrained_argument<Mode: ExecutionMode>(
820 env: &Env<Mode>,
821 context: &mut Context,
822 command_arg_idx: usize,
823 location: SplatLocation,
824 constraint: AbilitySet,
825 err_case: CommandArgumentError,
826) -> Result<T::Argument, Mode::Error> {
827 let arg_ = constrained_argument_(
828 env,
829 context,
830 command_arg_idx,
831 location,
832 constraint,
833 err_case,
834 )
835 .map_err(|e| e.into_execution_error(command_arg_idx))?;
836 Ok(sp(checked_as!(command_arg_idx, u16)?, arg_))
837}
838
839fn constrained_argument_<Mode: ExecutionMode>(
840 env: &Env<Mode>,
841 context: &mut Context,
842 command_arg_idx: usize,
843 location: SplatLocation,
844 constraint: AbilitySet,
845 err_case: CommandArgumentError,
846) -> Result<T::Argument_, EitherError<Mode::Error>> {
847 if let Some((location, ty)) =
848 constrained_type(env, context, command_arg_idx, location, constraint)
849 .map_err(EitherError::Execution)?
850 {
851 if ty.abilities().has_copy() {
852 Ok((T::Argument__::new_copy(location), ty))
853 } else {
854 Ok((T::Argument__::new_move(location), ty))
855 }
856 } else {
857 Err(err_case.into())
858 }
859}
860
861fn constrained_type<'a, Mode: ExecutionMode>(
862 env: &'a Env<Mode>,
863 context: &'a mut Context,
864 _command_arg_idx: usize,
865 location: SplatLocation,
866 constraint: AbilitySet,
867) -> Result<Option<(T::Location, Type)>, Mode::Error> {
868 let Some((location, ty)) = context.fixed_type(env, location)? else {
869 return Ok(None);
870 };
871 Ok(if constraint.is_subset(ty.abilities()) {
872 Some((location, ty))
873 } else {
874 None
875 })
876}
877
878fn coin_mut_ref_argument<Mode: ExecutionMode>(
879 env: &Env<Mode>,
880 context: &mut Context,
881 command_arg_idx: usize,
882 location: SplatLocation,
883) -> Result<T::Argument, Mode::Error> {
884 let arg_ = coin_mut_ref_argument_(env, context, command_arg_idx, location)
885 .map_err(|e| e.into_execution_error(command_arg_idx))?;
886 Ok(sp(checked_as!(command_arg_idx, u16)?, arg_))
887}
888
889fn coin_mut_ref_argument_<Mode: ExecutionMode>(
890 env: &Env<Mode>,
891 context: &mut Context,
892 _command_arg_idx: usize,
893 location: SplatLocation,
894) -> Result<T::Argument_, EitherError<Mode::Error>> {
895 let Some((location, actual_ty)) = context
896 .fixed_type(env, location)
897 .map_err(EitherError::Execution)?
898 else {
899 return Err(CommandArgumentError::TypeMismatch.into());
902 };
903 Ok(match &actual_ty {
904 Type::Reference(is_mut, ty) if *is_mut => {
905 check_coin_type(ty)?;
906 (
907 T::Argument__::new_copy(location),
908 Type::Reference(*is_mut, ty.clone()),
909 )
910 }
911 ty => {
912 check_coin_type(ty)?;
913 (
914 T::Argument__::Borrow(true, location),
915 Type::Reference(true, Rc::new(ty.clone())),
916 )
917 }
918 })
919}
920
921fn check_coin_type<E: ExecutionErrorTrait>(ty: &Type) -> Result<(), EitherError<E>> {
922 if coin_inner_type(ty).is_some() {
923 Ok(())
924 } else {
925 Err(CommandArgumentError::TypeMismatch.into())
926 }
927}
928
929fn determine_withdrawal_compatibility_inputs<Mode: ExecutionMode>(
936 _env: &Env<Mode>,
937 inputs: &mut L::Inputs,
938) -> Result<IndexMap<u16, u16>, Mode::Error> {
939 let withdrawal_compatibility_owners: IndexMap<u16, AccountAddress> = inputs
940 .iter()
941 .enumerate()
942 .filter_map(|(i, (input_arg, _))| {
943 if let L::InputArg::FundsWithdrawal(withdrawal) = input_arg
944 && withdrawal.from_compatibility_object
945 {
946 Some((i, withdrawal.source.source_account()))
947 } else {
948 None
949 }
950 })
951 .map(|(i, owner)| Ok((checked_as!(i, u16)?, owner)))
952 .collect::<Result<_, Mode::Error>>()?;
953 withdrawal_compatibility_owners
954 .into_iter()
955 .map(|(i, owner)| {
956 let owner_idx = checked_as!(inputs.len(), u16)?;
957 let bytes: Vec<u8> = bcs::to_bytes(&owner).map_err(|_| {
958 make_invariant_violation!(
959 "Failed to serialize owner address for withdrawal compatibility input",
960 )
961 })?;
962 inputs.push((L::InputArg::Pure(bytes), L::InputType::Bytes));
963 Ok((i, owner_idx))
964 })
965 .collect()
966}
967
968struct WithdrawalCompatibilityRemap {
969 remap: IndexMap<u16, u16>,
971 lift: u16,
973}
974
975fn withdrawal_compatibility_conversion<Mode: ExecutionMode>(
979 env: &Env<Mode>,
980 context: &mut Context,
981 withdrawal_compatability_inputs: IndexMap<
982 u16,
983 u16,
984 >,
985 commands: &mut [L::Command],
986) -> Result<(), Mode::Error> {
987 let mut compatibility_remap = WithdrawalCompatibilityRemap {
988 remap: IndexMap::new(),
989 lift: 0,
990 };
991 for (input, owner_idx) in withdrawal_compatability_inputs {
992 let result_idx = convert_withdrawal_to_coin(env, context, input, owner_idx)?;
993 compatibility_remap.remap.insert(input, result_idx);
994 }
995 compatibility_remap.lift = checked_as!(context.commands.len(), u16)?;
996 lift_result_indices(&compatibility_remap, commands)?;
997 Ok(())
998}
999
1000fn convert_withdrawal_to_coin<Mode: ExecutionMode>(
1001 env: &Env<Mode>,
1002 context: &mut Context,
1003 withdrawal_input: u16,
1004 owner_input: u16,
1005) -> Result<u16, Mode::Error> {
1006 assert_invariant!(
1007 env.protocol_config
1008 .convert_withdrawal_compatibility_ptb_arguments(),
1009 "convert_withdrawal_to_coin called when conversion is disabled"
1010 );
1011 let (owner_location, _owner_ty) = context.resolve_location(
1013 env,
1014 SplatLocation::Input(T::InputIndex(owner_input)),
1015 &Type::Address,
1016 BytesConstraint {
1017 command: 0,
1018 argument: 0,
1019 },
1020 )?;
1021 let Some((location, withdrawal_ty)) =
1022 context.fixed_type(env, SplatLocation::Input(T::InputIndex(withdrawal_input)))?
1023 else {
1024 invariant_violation!(
1025 "Expected fixed type for withdrawal compatibility input {}",
1026 withdrawal_input
1027 )
1028 };
1029 let Some(inner_ty) = withdrawal_inner_type(&withdrawal_ty)
1030 .and_then(balance_inner_type)
1031 .cloned()
1032 else {
1033 invariant_violation!("convert_withdrawal_to_coin called with non-withdrawal type");
1034 };
1035 let idx = 0u16;
1036 let withdrawal_arg_ = T::Argument__::new_move(location);
1038 let withdrawal_arg = sp(idx, (withdrawal_arg_, withdrawal_ty));
1039 let ctx_arg_ = T::Argument__::Borrow(true, T::Location::TxContext);
1040 let ctx_ty = Type::Reference(true, Rc::new(env.tx_context_type()?));
1041 let ctx_arg = sp(idx, (ctx_arg_, ctx_ty));
1042 let conversion_command__ = T::Command__::MoveCall(Box::new(T::MoveCall {
1043 function: env.load_framework_function(
1044 COIN_MODULE_NAME,
1045 REDEEM_FUNDS_FUNC_NAME,
1046 vec![inner_ty.clone()],
1047 context.unified_linkage.as_ref(),
1048 )?,
1049 arguments: vec![withdrawal_arg, ctx_arg],
1050 }));
1051 let conversion_command_ = T::Command_ {
1052 command: conversion_command__,
1053 result_type: vec![env.coin_type(inner_ty.clone())?],
1054 drop_values: vec![],
1055 incurs_post_execution_checks: false,
1056 };
1057 let conversion_idx = checked_as!(context.commands.len(), u16)?;
1058 context.push_result(conversion_command_)?;
1059 context.withdrawal_compatibility_conversions.insert(
1061 location,
1062 T::WithdrawalCompatibilityConversion {
1063 owner: owner_location,
1064 conversion_result: conversion_idx,
1065 },
1066 );
1067 Ok(conversion_idx)
1069}
1070
1071fn lift_result_indices(
1074 remap: &WithdrawalCompatibilityRemap,
1075 commands: &mut [L::Command],
1076) -> Result<(), ExecutionError> {
1077 for command in commands {
1078 for arg in command.arguments_mut() {
1079 match arg {
1080 L::Argument::NestedResult(result, _) | L::Argument::Result(result) => {
1081 *result = remap.lift.checked_add(*result).ok_or_else(|| {
1082 make_invariant_violation!(
1083 "u16 overflow when lifting result index during withdrawal compatibility",
1084 )
1085 })?;
1086 }
1087 L::Argument::Input(i) => {
1088 if let Some(converted_withdrawal) = remap.remap.get(i).copied() {
1089 *arg = L::Argument::NestedResult(converted_withdrawal, 0);
1090 }
1091 }
1092 L::Argument::GasCoin => (),
1093 }
1094 }
1095 }
1096 Ok(())
1097}
1098
1099pub(crate) fn coin_inner_type(ty: &Type) -> Option<&Type> {
1101 if let Type::Datatype(dt) = ty
1102 && dt.type_arguments.len() == 1
1103 && dt.qualified_ident() == RESOLVED_COIN_STRUCT
1104 {
1105 Some(dt.type_arguments.first().unwrap())
1106 } else {
1107 None
1108 }
1109}
1110
1111pub(crate) fn balance_inner_type(ty: &Type) -> Option<&Type> {
1113 if let Type::Datatype(dt) = ty
1114 && dt.type_arguments.len() == 1
1115 && dt.qualified_ident() == RESOLVED_BALANCE_STRUCT
1116 {
1117 Some(dt.type_arguments.first().unwrap())
1118 } else {
1119 None
1120 }
1121}
1122
1123pub(crate) fn withdrawal_inner_type(ty: &Type) -> Option<&Type> {
1125 if let Type::Datatype(dt) = ty
1126 && dt.type_arguments.len() == 1
1127 && dt.qualified_ident() == RESOLVED_WITHDRAWAL_STRUCT
1128 {
1129 Some(dt.type_arguments.first().unwrap())
1130 } else {
1131 None
1132 }
1133}
1134
1135mod scope_references {
1140 use crate::{
1141 sp,
1142 static_programmable_transactions::typing::ast::{self as T, Type},
1143 };
1144 use std::collections::BTreeSet;
1145 use sui_protocol_config::ProtocolConfig;
1146
1147 struct Context<'pc> {
1148 protocol_config: &'pc ProtocolConfig,
1149 used: BTreeSet<(u16, u16)>,
1150 }
1151
1152 pub fn transaction(protocol_config: &ProtocolConfig, ast: &mut T::Transaction) {
1155 let mut context = Context {
1156 protocol_config,
1157 used: BTreeSet::new(),
1158 };
1159 for c in ast.commands.iter_mut().rev() {
1160 command(&mut context, c);
1161 }
1162 }
1163
1164 fn command(context: &mut Context, sp!(_, c): &mut T::Command) {
1165 match &mut c.command {
1166 T::Command__::MoveCall(mc) => arguments(context, &mut mc.arguments),
1167 T::Command__::TransferObjects(objects, recipient) => {
1168 argument(context, recipient);
1169 arguments(context, objects);
1170 }
1171 T::Command__::SplitCoins(_, coin, amounts) => {
1172 arguments(context, amounts);
1173 argument(context, coin);
1174 }
1175 T::Command__::MergeCoins(_, target, coins) => {
1176 arguments(context, coins);
1177 argument(context, target);
1178 }
1179 T::Command__::MakeMoveVec(_, xs) => arguments(context, xs),
1180 T::Command__::Publish(_, _, _) => (),
1181 T::Command__::Upgrade(_, _, _, x, _) => argument(context, x),
1182 }
1183 }
1184
1185 fn arguments(context: &mut Context, args: &mut [T::Argument]) {
1186 for arg in args.iter_mut().rev() {
1187 argument(context, arg)
1188 }
1189 }
1190
1191 fn argument(context: &mut Context, arg: &mut T::Argument) {
1192 if context.protocol_config.fix_ptb_generated_reads() {
1193 argument_v2(context, arg)
1194 } else {
1195 argument_v1(context, arg)
1196 }
1197 }
1198
1199 fn argument_v2(context: &mut Context, sp!(_, (arg_, ty)): &mut T::Argument) {
1200 use T::Argument__ as TArg;
1201 let usage = match arg_ {
1202 TArg::Read(u) => u,
1204 TArg::Use(_) | TArg::Freeze(_) if !ty.is_reference() => return,
1205 TArg::Use(u) | TArg::Freeze(u) => u,
1206 TArg::Borrow(_, _) => return,
1208 };
1209 match usage {
1210 T::Usage::Move(T::Location::Result(i, j)) => {
1211 debug_assert!(false, "No reference should be moved at this point");
1212 context.used.insert((*i, *j));
1213 }
1214 T::Usage::Copy {
1215 location: T::Location::Result(i, j),
1216 ..
1217 } => {
1218 let last_usage = context.used.insert((*i, *j));
1220 if last_usage {
1221 let loc = T::Location::Result(*i, *j);
1223 *usage = T::Usage::Move(loc);
1224 }
1225 }
1226 _ => (),
1227 }
1228 }
1229
1230 fn argument_v1(context: &mut Context, sp!(_, (arg_, ty)): &mut T::Argument) {
1231 let usage = match arg_ {
1232 T::Argument__::Use(u) | T::Argument__::Read(u) | T::Argument__::Freeze(u) => u,
1233 T::Argument__::Borrow(_, _) => return,
1234 };
1235 match (&usage, ty) {
1236 (T::Usage::Move(T::Location::Result(i, j)), Type::Reference(_, _)) => {
1237 debug_assert!(false, "No reference should be moved at this point");
1238 context.used.insert((*i, *j));
1239 }
1240 (
1241 T::Usage::Copy {
1242 location: T::Location::Result(i, j),
1243 ..
1244 },
1245 Type::Reference(_, _),
1246 ) => {
1247 let last_usage = context.used.insert((*i, *j));
1249 if last_usage {
1250 let loc = T::Location::Result(*i, *j);
1252 *usage = T::Usage::Move(loc);
1253 }
1254 }
1255 _ => (),
1256 }
1257 }
1258}
1259
1260mod unused_results {
1265 use indexmap::IndexSet;
1266 use sui_types::error::ExecutionError;
1267
1268 use crate::{sp, static_programmable_transactions::typing::ast as T};
1269
1270 pub fn transaction(ast: &mut T::Transaction) -> Result<(), ExecutionError> {
1274 let mut used: IndexSet<(u16, u16)> = IndexSet::new();
1276 for c in &ast.commands {
1277 command(&mut used, c);
1278 }
1279
1280 for (i, sp!(_, c)) in ast.commands.iter_mut().enumerate() {
1282 debug_assert!(c.drop_values.is_empty());
1283 let i = checked_as!(i, u16)?;
1284 c.drop_values = c
1285 .result_type
1286 .iter()
1287 .enumerate()
1288 .map(|(j, ty)| {
1289 Ok(ty.abilities().has_drop() && !used.contains(&(i, checked_as!(j, u16)?)))
1290 })
1291 .collect::<Result<_, ExecutionError>>()?;
1292 }
1293 Ok(())
1294 }
1295
1296 fn command(used: &mut IndexSet<(u16, u16)>, sp!(_, c): &T::Command) {
1297 match &c.command {
1298 T::Command__::MoveCall(mc) => arguments(used, &mc.arguments),
1299 T::Command__::TransferObjects(objects, recipient) => {
1300 argument(used, recipient);
1301 arguments(used, objects);
1302 }
1303 T::Command__::SplitCoins(_, coin, amounts) => {
1304 arguments(used, amounts);
1305 argument(used, coin);
1306 }
1307 T::Command__::MergeCoins(_, target, coins) => {
1308 arguments(used, coins);
1309 argument(used, target);
1310 }
1311 T::Command__::MakeMoveVec(_, elements) => arguments(used, elements),
1312 T::Command__::Publish(_, _, _) => (),
1313 T::Command__::Upgrade(_, _, _, x, _) => argument(used, x),
1314 }
1315 }
1316
1317 fn arguments(used: &mut IndexSet<(u16, u16)>, args: &[T::Argument]) {
1318 for arg in args {
1319 argument(used, arg)
1320 }
1321 }
1322
1323 fn argument(used: &mut IndexSet<(u16, u16)>, sp!(_, (arg_, _)): &T::Argument) {
1324 if let T::Location::Result(i, j) = arg_.location() {
1325 used.insert((i, j));
1326 }
1327 }
1328}
1329
1330mod post_execution_checks {
1335
1336 use crate::{sp, static_programmable_transactions::typing::ast as T};
1337 use sui_protocol_config::ProtocolConfig;
1338 use sui_types::{
1339 error::{ExecutionError, SafeIndex},
1340 object::ObjectPermissions,
1341 };
1342
1343 struct Context {
1345 inputs: Vec<bool>,
1347 results: Vec<bool>,
1349 propagate_through_mut_borrow: bool,
1350 }
1351
1352 impl Context {
1353 pub fn new(protocol_config: &ProtocolConfig, ast: &T::Transaction) -> Self {
1354 let T::Transaction {
1355 gas_payment: _,
1356 bytes: _,
1357 objects,
1358 withdrawals: _,
1359 pure: _,
1360 receiving: _,
1361 withdrawal_compatibility_conversions: _,
1362 original_command_len: _,
1363 commands: _,
1364 unified_linkage: _,
1365 } = ast;
1366 let inputs = objects
1368 .iter()
1369 .map(|o| {
1370 o.arg.refined_permissions.can_use_mutably()
1371 && o.arg.refined_permissions != ObjectPermissions::ALL
1372 })
1373 .collect::<Vec<_>>();
1374 Self {
1375 inputs,
1376 results: vec![],
1377 propagate_through_mut_borrow: protocol_config.granular_post_execution_checks(),
1378 }
1379 }
1380 }
1381
1382 pub fn transaction(
1387 protocol_config: &ProtocolConfig,
1388 ast: &mut T::Transaction,
1389 ) -> Result<(), ExecutionError> {
1390 let mut context = Context::new(protocol_config, ast);
1391
1392 for c in &mut ast.commands {
1395 debug_assert!(!c.value.incurs_post_execution_checks);
1396 command(&mut context, c)?;
1397 }
1398 Ok(())
1399 }
1400
1401 fn command(context: &mut Context, sp!(_, c): &mut T::Command) -> Result<(), ExecutionError> {
1402 let arg_requires_post_execution_checks = arguments(context, c.command.arguments())?;
1403 let (incurs_checks, tainted_result) = match &c.command {
1404 T::Command__::MakeMoveVec(_, _) => {
1407 assert_invariant!(
1408 c.result_type.len() == 1,
1409 "MakeMoveVec must return a single value"
1410 );
1411 (false, arg_requires_post_execution_checks)
1412 }
1413 T::Command__::MoveCall(_)
1415 | T::Command__::TransferObjects(_, _)
1416 | T::Command__::SplitCoins(_, _, _)
1417 | T::Command__::MergeCoins(_, _, _)
1418 | T::Command__::Publish(_, _, _)
1419 | T::Command__::Upgrade(_, _, _, _, _) => (arg_requires_post_execution_checks, false),
1420 };
1421 c.incurs_post_execution_checks |= incurs_checks;
1422 context.results.push(tainted_result);
1423 Ok(())
1424 }
1425
1426 fn arguments<'a>(
1427 context: &mut Context,
1428 args: impl IntoIterator<Item = &'a T::Argument>,
1429 ) -> Result<bool, ExecutionError> {
1430 for arg in args {
1431 if argument(context, arg)? {
1432 return Ok(true);
1433 }
1434 }
1435 Ok(false)
1436 }
1437
1438 fn argument(
1439 context: &mut Context,
1440 sp!(_, (arg_, _)): &T::Argument,
1441 ) -> Result<bool, ExecutionError> {
1442 Ok(match arg_.location() {
1443 T::Location::TxContext
1445 | T::Location::GasCoin
1446 | T::Location::WithdrawalInput(_)
1447 | T::Location::PureInput(_)
1448 | T::Location::ReceivingInput(_) => false,
1449 T::Location::ObjectInput(i) => match arg_ {
1450 T::Argument__::Use(T::Usage::Move(_)) => {
1451 let arg_requires_post_execution_checks = context.inputs.safe_get(i as usize)?;
1452 *arg_requires_post_execution_checks
1453 }
1454 T::Argument__::Use(T::Usage::Copy { .. })
1455 | T::Argument__::Borrow(_, _)
1456 | T::Argument__::Read(_)
1457 | T::Argument__::Freeze(_) => {
1458 false
1460 }
1461 },
1462
1463 T::Location::Result(i, _) => {
1464 match arg_ {
1465 T::Argument__::Use(T::Usage::Move(_)) => {
1466 let tainted = context.results.safe_get(i as usize)?;
1467 *tainted
1468 }
1469 T::Argument__::Borrow(true, _) => {
1470 if context.propagate_through_mut_borrow {
1471 let tainted = context.results.safe_get(i as usize)?;
1472 *tainted
1473 } else {
1474 false
1475 }
1476 }
1477 T::Argument__::Use(T::Usage::Copy { .. })
1478 | T::Argument__::Borrow(false, _)
1479 | T::Argument__::Read(_)
1480 | T::Argument__::Freeze(_) => {
1481 false
1483 }
1484 }
1485 }
1486 })
1487 }
1488}