sui_adapter_v3/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 Owner::Party { .. } => {
107 unimplemented!("Party does not exist for this execution version")
108 }
109 };
110 (L::InputArg::Object(arg), L::InputType::Fixed(ty))
111 }
112 CallArg::Object(ObjectArg::SharedObject {
113 id,
114 initial_shared_version,
115 mutability,
116 }) => {
117 let obj = env.read_object(&id)?;
118 let Some(ty) = obj.type_() else {
119 invariant_violation!("Object {:?} has does not have a Move type", id);
120 };
121 let tag: StructTag = ty.clone().into();
122 let ty = env.load_type_from_struct(&tag)?;
123 let kind = match obj.owner {
124 Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
125 assert_invariant!(
126 Mode::allow_arbitrary_values(),
127 "Unexpected owner for SharedObject: {:?}",
128 obj.owner
129 );
130 L::SharedObjectKind::Party
131 }
132 Owner::Shared { .. } => L::SharedObjectKind::Legacy,
133 Owner::ConsensusAddressOwner { .. } => L::SharedObjectKind::Party,
134 Owner::Party { .. } => {
135 unimplemented!("Party does not exist for this execution version")
136 }
137 };
138 (
139 L::InputArg::Object(L::ObjectArg::SharedObject {
140 id,
141 initial_shared_version,
142 mutability: object_mutability(mutability),
143 kind,
144 }),
145 L::InputType::Fixed(ty),
146 )
147 }
148 CallArg::FundsWithdrawal(f) => {
149 assert_invariant!(
150 env.protocol_config.enable_accumulators(),
151 "Withdrawals should be rejected at signing if accumulators are not enabled"
152 );
153 let FundsWithdrawalArg {
154 reservation,
155 type_arg,
156 withdraw_from,
157 } = f;
158 let amount = match reservation {
159 P::Reservation::MaxAmountU64(u) => U256::from(u),
160 };
163 let funds_ty = match type_arg {
164 P::WithdrawalTypeArg::Balance(inner) => {
165 let inner = env.load_type_tag(0, &inner)?;
166 env.balance_type(inner)?
167 }
168 };
169 let ty = env.withdrawal_type(funds_ty.clone())?;
170 let owner: AccountAddress = match withdraw_from {
171 P::WithdrawFrom::Sender => tx_context.sender().into(),
172 P::WithdrawFrom::Sponsor => tx_context
173 .sponsor()
174 .ok_or_else(|| {
175 make_invariant_violation!(
176 "A sponsor withdrawal requires a sponsor and should have been \
177 checked at signing"
178 )
179 })?
180 .into(),
181 };
182 (
183 L::InputArg::FundsWithdrawal(L::FundsWithdrawalArg {
184 from_compatibility_object: is_withdrawal_compatibility_input,
185 amount,
186 ty: ty.clone(),
187 owner,
188 }),
189 L::InputType::Fixed(ty),
190 )
191 }
192 })
193}
194
195fn object_mutability(mutability: SharedObjectMutability) -> L::ObjectMutability {
196 match mutability {
197 SharedObjectMutability::Mutable => L::ObjectMutability::Mutable,
198 SharedObjectMutability::NonExclusiveWrite => L::ObjectMutability::NonExclusiveWrite,
199 SharedObjectMutability::Immutable => L::ObjectMutability::Immutable,
200 }
201}
202
203fn command(env: &Env, command: P::Command) -> Result<L::Command, ExecutionError> {
204 Ok(match command {
205 P::Command::MoveCall(pmc) => {
206 let resolved_linkage = env
207 .linkage_analysis
208 .compute_call_linkage(&pmc, env.linkable_store)?;
209 let P::ProgrammableMoveCall {
210 package,
211 module,
212 function: name,
213 type_arguments: ptype_arguments,
214 arguments,
215 } = *pmc;
216 let linkage = RootedLinkage::new(*package, resolved_linkage);
217 let type_arguments = ptype_arguments
218 .into_iter()
219 .enumerate()
220 .map(|(idx, ty)| env.load_type_input(idx, ty))
221 .collect::<Result<Vec<_>, _>>()?;
222 let function = env.load_function(package, module, name, type_arguments, linkage)?;
223 L::Command::MoveCall(Box::new(L::MoveCall {
224 function,
225 arguments,
226 }))
227 }
228 P::Command::MakeMoveVec(ptype_argument, arguments) => {
229 let type_argument = ptype_argument
230 .map(|ty| env.load_type_input(0, ty))
231 .transpose()?;
232 L::Command::MakeMoveVec(type_argument, arguments)
233 }
234 P::Command::TransferObjects(objects, address) => {
235 L::Command::TransferObjects(objects, address)
236 }
237 P::Command::SplitCoins(coin, amounts) => L::Command::SplitCoins(coin, amounts),
238 P::Command::MergeCoins(target, coins) => L::Command::MergeCoins(target, coins),
239 P::Command::Publish(items, object_ids) => {
240 let resolved_linkage = env
241 .linkage_analysis
242 .compute_publication_linkage(&object_ids, env.linkable_store)?;
243 L::Command::Publish(items, object_ids, resolved_linkage)
244 }
245 P::Command::Upgrade(items, object_ids, object_id, argument) => {
246 let resolved_linkage = env
247 .linkage_analysis
248 .compute_publication_linkage(&object_ids, env.linkable_store)?;
249 L::Command::Upgrade(items, object_ids, object_id, argument, resolved_linkage)
250 }
251 })
252}