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    static_programmable_transactions::{
7        env::Env,
8        linkage::resolved_linkage::RootedLinkage,
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 sui_types::{
15    base_types::{ObjectID, TxContext},
16    error::ExecutionError,
17    object::Owner,
18    transaction::{self as P, CallArg, FundsWithdrawalArg, ObjectArg, SharedObjectMutability},
19};
20
21pub fn transaction<Mode: ExecutionMode>(
22    meter: &mut TranslationMeter<'_, '_>,
23    env: &Env,
24    tx_context: &TxContext,
25    // which inputs are withdrawals that need to be converted to coins, must
26    // be the same length as the inputs
27    withdrawal_compatibility_inputs: Option<Vec<bool>>,
28    gas_coin: Option<ObjectID>,
29    pt: P::ProgrammableTransaction,
30) -> Result<L::Transaction, ExecutionError> {
31    metering::pre_translation::meter(meter, &pt)?;
32    let P::ProgrammableTransaction { inputs, commands } = pt;
33    // withdrawal_compatibility_inputs specified ==> the protocol config flag is set
34    assert_invariant!(
35        withdrawal_compatibility_inputs.is_none()
36            || env
37                .protocol_config
38                .convert_withdrawal_compatibility_ptb_arguments(),
39        "if withdrawal compatibility must be specified, then the flag is set in the protocol config"
40    );
41    let withdrawal_compatibility_inputs =
42        withdrawal_compatibility_inputs.unwrap_or_else(|| vec![false; inputs.len()]);
43    assert_invariant!(
44        inputs.len() == withdrawal_compatibility_inputs.len(),
45        "withdrawal compatibility inputs must be the same length as the inputs"
46    );
47    let inputs = withdrawal_compatibility_inputs
48        .into_iter()
49        .zip(inputs)
50        .map(|(is_withdrawal_compatibility_input, arg)| {
51            input::<Mode>(env, tx_context, is_withdrawal_compatibility_input, arg)
52        })
53        .collect::<Result<Vec<_>, _>>()?;
54    let commands = commands
55        .into_iter()
56        .enumerate()
57        .map(|(idx, cmd)| command(env, cmd).map_err(|e| e.with_command_index(idx)))
58        .collect::<Result<Vec<_>, _>>()?;
59    let loaded_tx = L::Transaction {
60        gas_coin,
61        inputs,
62        commands,
63    };
64    metering::loading::meter(meter, &loaded_tx)?;
65    Ok(loaded_tx)
66}
67
68fn input<Mode: ExecutionMode>(
69    env: &Env,
70    tx_context: &TxContext,
71    // True iff this is a withdrawal that needs to be converted to a coin
72    is_withdrawal_compatibility_input: bool,
73    arg: CallArg,
74) -> Result<(L::InputArg, L::InputType), ExecutionError> {
75    // is_withdrawal_compatibility_input ==> FundsWithdrawal
76    assert_invariant!(
77        !is_withdrawal_compatibility_input || matches!(arg, CallArg::FundsWithdrawal(_)),
78        "withdrawal compatibility inputs must be FundsWithdrawal"
79    );
80    Ok(match arg {
81        CallArg::Pure(bytes) => (L::InputArg::Pure(bytes), L::InputType::Bytes),
82        CallArg::Object(ObjectArg::Receiving(oref)) => {
83            (L::InputArg::Receiving(oref), L::InputType::Bytes)
84        }
85        CallArg::Object(ObjectArg::ImmOrOwnedObject(oref)) => {
86            let id = &oref.0;
87            let obj = env.read_object(id)?;
88            let Some(ty) = obj.type_() else {
89                invariant_violation!("Object {:?} has does not have a Move type", id);
90            };
91            let tag: StructTag = ty.clone().into();
92            let ty = env.load_type_from_struct(&tag)?;
93            let arg = match obj.owner {
94                Owner::AddressOwner(_) => L::ObjectArg::OwnedObject(oref),
95                Owner::Immutable => L::ObjectArg::ImmObject(oref),
96                Owner::ObjectOwner(_)
97                | Owner::Shared { .. }
98                | Owner::ConsensusAddressOwner { .. } => {
99                    assert_invariant!(
100                        Mode::allow_arbitrary_values(),
101                        "Unexpected owner for ImmOrOwnedObject: {:?}",
102                        obj.owner,
103                    );
104                    L::ObjectArg::OwnedObject(oref)
105                }
106            };
107            (L::InputArg::Object(arg), L::InputType::Fixed(ty))
108        }
109        CallArg::Object(ObjectArg::SharedObject {
110            id,
111            initial_shared_version,
112            mutability,
113        }) => {
114            let obj = env.read_object(&id)?;
115            let Some(ty) = obj.type_() else {
116                invariant_violation!("Object {:?} has does not have a Move type", id);
117            };
118            let tag: StructTag = ty.clone().into();
119            let ty = env.load_type_from_struct(&tag)?;
120            let kind = match obj.owner {
121                Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
122                    assert_invariant!(
123                        Mode::allow_arbitrary_values(),
124                        "Unexpected owner for SharedObject: {:?}",
125                        obj.owner
126                    );
127                    L::SharedObjectKind::Party
128                }
129                Owner::Shared { .. } => L::SharedObjectKind::Legacy,
130                Owner::ConsensusAddressOwner { .. } => L::SharedObjectKind::Party,
131            };
132            (
133                L::InputArg::Object(L::ObjectArg::SharedObject {
134                    id,
135                    initial_shared_version,
136                    mutability: object_mutability(mutability),
137                    kind,
138                }),
139                L::InputType::Fixed(ty),
140            )
141        }
142        CallArg::FundsWithdrawal(f) => {
143            assert_invariant!(
144                env.protocol_config.enable_accumulators(),
145                "Withdrawals should be rejected at signing if accumulators are not enabled"
146            );
147            let FundsWithdrawalArg {
148                reservation,
149                type_arg,
150                withdraw_from,
151            } = f;
152            let amount = match reservation {
153                P::Reservation::MaxAmountU64(u) => U256::from(u),
154                // TODO when types other than u64 are supported, we must check that this is a
155                // valid amount for the type
156            };
157            let funds_ty = match type_arg {
158                P::WithdrawalTypeArg::Balance(inner) => {
159                    let inner = env.load_type_tag(0, &inner)?;
160                    env.balance_type(inner)?
161                }
162            };
163            let ty = env.withdrawal_type(funds_ty.clone())?;
164            let owner: AccountAddress = match withdraw_from {
165                P::WithdrawFrom::Sender => tx_context.sender().into(),
166                P::WithdrawFrom::Sponsor => tx_context
167                    .sponsor()
168                    .ok_or_else(|| {
169                        make_invariant_violation!(
170                            "A sponsor withdrawal requires a sponsor and should have been \
171                            checked at signing"
172                        )
173                    })?
174                    .into(),
175            };
176            (
177                L::InputArg::FundsWithdrawal(L::FundsWithdrawalArg {
178                    from_compatibility_object: is_withdrawal_compatibility_input,
179                    amount,
180                    ty: ty.clone(),
181                    owner,
182                }),
183                L::InputType::Fixed(ty),
184            )
185        }
186    })
187}
188
189fn object_mutability(mutability: SharedObjectMutability) -> L::ObjectMutability {
190    match mutability {
191        SharedObjectMutability::Mutable => L::ObjectMutability::Mutable,
192        SharedObjectMutability::NonExclusiveWrite => L::ObjectMutability::NonExclusiveWrite,
193        SharedObjectMutability::Immutable => L::ObjectMutability::Immutable,
194    }
195}
196
197fn command(env: &Env, command: P::Command) -> Result<L::Command, ExecutionError> {
198    Ok(match command {
199        P::Command::MoveCall(pmc) => {
200            let resolved_linkage = env
201                .linkage_analysis
202                .compute_call_linkage(&pmc, env.linkable_store)?;
203            let P::ProgrammableMoveCall {
204                package,
205                module,
206                function: name,
207                type_arguments: ptype_arguments,
208                arguments,
209            } = *pmc;
210            let linkage = RootedLinkage::new(*package, resolved_linkage);
211            let type_arguments = ptype_arguments
212                .into_iter()
213                .enumerate()
214                .map(|(idx, ty)| env.load_type_input(idx, ty))
215                .collect::<Result<Vec<_>, _>>()?;
216            let function = env.load_function(package, module, name, type_arguments, linkage)?;
217            L::Command::MoveCall(Box::new(L::MoveCall {
218                function,
219                arguments,
220            }))
221        }
222        P::Command::MakeMoveVec(ptype_argument, arguments) => {
223            let type_argument = ptype_argument
224                .map(|ty| env.load_type_input(0, ty))
225                .transpose()?;
226            L::Command::MakeMoveVec(type_argument, arguments)
227        }
228        P::Command::TransferObjects(objects, address) => {
229            L::Command::TransferObjects(objects, address)
230        }
231        P::Command::SplitCoins(coin, amounts) => L::Command::SplitCoins(coin, amounts),
232        P::Command::MergeCoins(target, coins) => L::Command::MergeCoins(target, coins),
233        P::Command::Publish(items, object_ids) => {
234            let resolved_linkage = env
235                .linkage_analysis
236                .compute_publication_linkage(&object_ids, env.linkable_store)?;
237            L::Command::Publish(items, object_ids, resolved_linkage)
238        }
239        P::Command::Upgrade(items, object_ids, object_id, argument) => {
240            let resolved_linkage = env
241                .linkage_analysis
242                .compute_publication_linkage(&object_ids, env.linkable_store)?;
243            L::Command::Upgrade(items, object_ids, object_id, argument, resolved_linkage)
244        }
245    })
246}