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        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    // which inputs are withdrawals that need to be converted to coins, must
27    // be the same length as the inputs
28    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    // withdrawal_compatibility_inputs specified ==> the protocol config flag is set
35    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    // True iff this is a withdrawal that needs to be converted to a coin
75    is_withdrawal_compatibility_input: bool,
76    arg: CallArg,
77) -> Result<(L::InputArg, L::InputType), Mode::Error> {
78    // is_withdrawal_compatibility_input ==> FundsWithdrawal
79    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                // TODO when types other than u64 are supported, we must check that this is a
186                // valid amount for the type
187            };
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}