sui_adapter_latest/static_programmable_transactions/loading/
translate.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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    // which inputs are withdrawals that need to be converted to coins, must
28    // be the same length as the inputs
29    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    // withdrawal_compatibility_inputs specified ==> the protocol config flag is set
36    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    // True iff this is a withdrawal that needs to be converted to a coin
81    is_withdrawal_compatibility_input: bool,
82    arg: CallArg,
83) -> Result<(L::InputArg, L::InputType), Mode::Error> {
84    // is_withdrawal_compatibility_input ==> FundsWithdrawal
85    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                // TODO when types other than u64 are supported, we must check that this is a
192                // valid amount for the type
193            };
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}