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