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