Skip to main content

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