sui_adapter_latest/static_programmable_transactions/loading/
translate.rs1use crate::{
5 execution_mode::ExecutionMode,
6 static_programmable_transactions::{
7 env::Env,
8 linkage::resolved_linkage::RootedLinkage,
9 loading::ast as L,
10 metering::{self, translation_meter::TranslationMeter},
11 },
12};
13use move_core_types::{account_address::AccountAddress, language_storage::StructTag, u256::U256};
14use sui_types::{
15 base_types::{ObjectID, TxContext},
16 error::ExecutionError,
17 object::Owner,
18 transaction::{self as P, CallArg, FundsWithdrawalArg, ObjectArg, SharedObjectMutability},
19};
20
21pub fn transaction<Mode: ExecutionMode>(
22 meter: &mut TranslationMeter<'_, '_>,
23 env: &Env,
24 tx_context: &TxContext,
25 withdrawal_compatibility_inputs: Option<Vec<bool>>,
28 gas_coin: Option<ObjectID>,
29 pt: P::ProgrammableTransaction,
30) -> Result<L::Transaction, ExecutionError> {
31 metering::pre_translation::meter(meter, &pt)?;
32 let P::ProgrammableTransaction { inputs, commands } = pt;
33 assert_invariant!(
35 withdrawal_compatibility_inputs.is_none()
36 || env
37 .protocol_config
38 .convert_withdrawal_compatibility_ptb_arguments(),
39 "if withdrawal compatibility must be specified, then the flag is set in the protocol config"
40 );
41 let withdrawal_compatibility_inputs =
42 withdrawal_compatibility_inputs.unwrap_or_else(|| vec![false; inputs.len()]);
43 assert_invariant!(
44 inputs.len() == withdrawal_compatibility_inputs.len(),
45 "withdrawal compatibility inputs must be the same length as the inputs"
46 );
47 let inputs = withdrawal_compatibility_inputs
48 .into_iter()
49 .zip(inputs)
50 .map(|(is_withdrawal_compatibility_input, arg)| {
51 input::<Mode>(env, tx_context, is_withdrawal_compatibility_input, arg)
52 })
53 .collect::<Result<Vec<_>, _>>()?;
54 let commands = commands
55 .into_iter()
56 .enumerate()
57 .map(|(idx, cmd)| command(env, cmd).map_err(|e| e.with_command_index(idx)))
58 .collect::<Result<Vec<_>, _>>()?;
59 let loaded_tx = L::Transaction {
60 gas_coin,
61 inputs,
62 commands,
63 };
64 metering::loading::meter(meter, &loaded_tx)?;
65 Ok(loaded_tx)
66}
67
68fn input<Mode: ExecutionMode>(
69 env: &Env,
70 tx_context: &TxContext,
71 is_withdrawal_compatibility_input: bool,
73 arg: CallArg,
74) -> Result<(L::InputArg, L::InputType), ExecutionError> {
75 assert_invariant!(
77 !is_withdrawal_compatibility_input || matches!(arg, CallArg::FundsWithdrawal(_)),
78 "withdrawal compatibility inputs must be FundsWithdrawal"
79 );
80 Ok(match arg {
81 CallArg::Pure(bytes) => (L::InputArg::Pure(bytes), L::InputType::Bytes),
82 CallArg::Object(ObjectArg::Receiving(oref)) => {
83 (L::InputArg::Receiving(oref), L::InputType::Bytes)
84 }
85 CallArg::Object(ObjectArg::ImmOrOwnedObject(oref)) => {
86 let id = &oref.0;
87 let obj = env.read_object(id)?;
88 let Some(ty) = obj.type_() else {
89 invariant_violation!("Object {:?} has does not have a Move type", id);
90 };
91 let tag: StructTag = ty.clone().into();
92 let ty = env.load_type_from_struct(&tag)?;
93 let arg = match obj.owner {
94 Owner::AddressOwner(_) => L::ObjectArg::OwnedObject(oref),
95 Owner::Immutable => L::ObjectArg::ImmObject(oref),
96 Owner::ObjectOwner(_)
97 | Owner::Shared { .. }
98 | Owner::ConsensusAddressOwner { .. } => {
99 assert_invariant!(
100 Mode::allow_arbitrary_values(),
101 "Unexpected owner for ImmOrOwnedObject: {:?}",
102 obj.owner,
103 );
104 L::ObjectArg::OwnedObject(oref)
105 }
106 };
107 (L::InputArg::Object(arg), L::InputType::Fixed(ty))
108 }
109 CallArg::Object(ObjectArg::SharedObject {
110 id,
111 initial_shared_version,
112 mutability,
113 }) => {
114 let obj = env.read_object(&id)?;
115 let Some(ty) = obj.type_() else {
116 invariant_violation!("Object {:?} has does not have a Move type", id);
117 };
118 let tag: StructTag = ty.clone().into();
119 let ty = env.load_type_from_struct(&tag)?;
120 let kind = match obj.owner {
121 Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
122 assert_invariant!(
123 Mode::allow_arbitrary_values(),
124 "Unexpected owner for SharedObject: {:?}",
125 obj.owner
126 );
127 L::SharedObjectKind::Party
128 }
129 Owner::Shared { .. } => L::SharedObjectKind::Legacy,
130 Owner::ConsensusAddressOwner { .. } => L::SharedObjectKind::Party,
131 };
132 (
133 L::InputArg::Object(L::ObjectArg::SharedObject {
134 id,
135 initial_shared_version,
136 mutability: object_mutability(mutability),
137 kind,
138 }),
139 L::InputType::Fixed(ty),
140 )
141 }
142 CallArg::FundsWithdrawal(f) => {
143 assert_invariant!(
144 env.protocol_config.enable_accumulators(),
145 "Withdrawals should be rejected at signing if accumulators are not enabled"
146 );
147 let FundsWithdrawalArg {
148 reservation,
149 type_arg,
150 withdraw_from,
151 } = f;
152 let amount = match reservation {
153 P::Reservation::MaxAmountU64(u) => U256::from(u),
154 };
157 let funds_ty = match type_arg {
158 P::WithdrawalTypeArg::Balance(inner) => {
159 let inner = env.load_type_tag(0, &inner)?;
160 env.balance_type(inner)?
161 }
162 };
163 let ty = env.withdrawal_type(funds_ty.clone())?;
164 let owner: AccountAddress = match withdraw_from {
165 P::WithdrawFrom::Sender => tx_context.sender().into(),
166 P::WithdrawFrom::Sponsor => tx_context
167 .sponsor()
168 .ok_or_else(|| {
169 make_invariant_violation!(
170 "A sponsor withdrawal requires a sponsor and should have been \
171 checked at signing"
172 )
173 })?
174 .into(),
175 };
176 (
177 L::InputArg::FundsWithdrawal(L::FundsWithdrawalArg {
178 from_compatibility_object: is_withdrawal_compatibility_input,
179 amount,
180 ty: ty.clone(),
181 owner,
182 }),
183 L::InputType::Fixed(ty),
184 )
185 }
186 })
187}
188
189fn object_mutability(mutability: SharedObjectMutability) -> L::ObjectMutability {
190 match mutability {
191 SharedObjectMutability::Mutable => L::ObjectMutability::Mutable,
192 SharedObjectMutability::NonExclusiveWrite => L::ObjectMutability::NonExclusiveWrite,
193 SharedObjectMutability::Immutable => L::ObjectMutability::Immutable,
194 }
195}
196
197fn command(env: &Env, command: P::Command) -> Result<L::Command, ExecutionError> {
198 Ok(match command {
199 P::Command::MoveCall(pmc) => {
200 let resolved_linkage = env
201 .linkage_analysis
202 .compute_call_linkage(&pmc, env.linkable_store)?;
203 let P::ProgrammableMoveCall {
204 package,
205 module,
206 function: name,
207 type_arguments: ptype_arguments,
208 arguments,
209 } = *pmc;
210 let linkage = RootedLinkage::new(*package, resolved_linkage);
211 let type_arguments = ptype_arguments
212 .into_iter()
213 .enumerate()
214 .map(|(idx, ty)| env.load_type_input(idx, ty))
215 .collect::<Result<Vec<_>, _>>()?;
216 let function = env.load_function(package, module, name, type_arguments, linkage)?;
217 L::Command::MoveCall(Box::new(L::MoveCall {
218 function,
219 arguments,
220 }))
221 }
222 P::Command::MakeMoveVec(ptype_argument, arguments) => {
223 let type_argument = ptype_argument
224 .map(|ty| env.load_type_input(0, ty))
225 .transpose()?;
226 L::Command::MakeMoveVec(type_argument, arguments)
227 }
228 P::Command::TransferObjects(objects, address) => {
229 L::Command::TransferObjects(objects, address)
230 }
231 P::Command::SplitCoins(coin, amounts) => L::Command::SplitCoins(coin, amounts),
232 P::Command::MergeCoins(target, coins) => L::Command::MergeCoins(target, coins),
233 P::Command::Publish(items, object_ids) => {
234 let resolved_linkage = env
235 .linkage_analysis
236 .compute_publication_linkage(&object_ids, env.linkable_store)?;
237 L::Command::Publish(items, object_ids, resolved_linkage)
238 }
239 P::Command::Upgrade(items, object_ids, object_id, argument) => {
240 let resolved_linkage = env
241 .linkage_analysis
242 .compute_publication_linkage(&object_ids, env.linkable_store)?;
243 L::Command::Upgrade(items, object_ids, object_id, argument, resolved_linkage)
244 }
245 })
246}