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::ExecutionError,
18    object::Owner,
19    transaction::{self as P, CallArg, FundsWithdrawalArg, ObjectArg, SharedObjectMutability},
20};
21
22pub fn transaction<Mode: ExecutionMode>(
23    meter: &mut TranslationMeter<'_, '_>,
24    env: &Env,
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, ExecutionError> {
32    metering::pre_translation::meter(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(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(meter, &loaded_tx)?;
68    Ok(loaded_tx)
69}
70
71fn input<Mode: ExecutionMode>(
72    env: &Env,
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), ExecutionError> {
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::OwnedObject(oref),
98                Owner::Immutable => L::ObjectArg::ImmObject(oref),
99                Owner::ObjectOwner(_)
100                | Owner::Shared { .. }
101                | Owner::ConsensusAddressOwner { .. } => {
102                    assert_invariant!(
103                        Mode::allow_arbitrary_values(),
104                        "Unexpected owner for ImmOrOwnedObject: {:?}",
105                        obj.owner,
106                    );
107                    L::ObjectArg::OwnedObject(oref)
108                }
109            };
110            (L::InputArg::Object(arg), L::InputType::Fixed(ty))
111        }
112        CallArg::Object(ObjectArg::SharedObject {
113            id,
114            initial_shared_version,
115            mutability,
116        }) => {
117            let obj = env.read_object(&id)?;
118            let Some(ty) = obj.type_() else {
119                invariant_violation!("Object {:?} does not have a Move type", id);
120            };
121            let tag: StructTag = ty.clone().into();
122            let ty = env.load_type_from_struct(&tag)?;
123            let kind = match obj.owner {
124                Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
125                    assert_invariant!(
126                        Mode::allow_arbitrary_values(),
127                        "Unexpected owner for SharedObject: {:?}",
128                        obj.owner
129                    );
130                    L::SharedObjectKind::Party
131                }
132                Owner::Shared { .. } => L::SharedObjectKind::Legacy,
133                Owner::ConsensusAddressOwner { .. } => L::SharedObjectKind::Party,
134            };
135            (
136                L::InputArg::Object(L::ObjectArg::SharedObject {
137                    id,
138                    initial_shared_version,
139                    mutability: object_mutability(mutability),
140                    kind,
141                }),
142                L::InputType::Fixed(ty),
143            )
144        }
145        CallArg::FundsWithdrawal(f) => {
146            assert_invariant!(
147                env.protocol_config.enable_accumulators(),
148                "Withdrawals should be rejected at signing if accumulators are not enabled"
149            );
150            let FundsWithdrawalArg {
151                reservation,
152                type_arg,
153                withdraw_from,
154            } = f;
155            let amount = match reservation {
156                P::Reservation::MaxAmountU64(u) => U256::from(u),
157                // TODO when types other than u64 are supported, we must check that this is a
158                // valid amount for the type
159            };
160            let funds_ty = match type_arg {
161                P::WithdrawalTypeArg::Balance(inner) => {
162                    let inner = env.load_type_tag(0, &inner)?;
163                    env.balance_type(inner)?
164                }
165            };
166            let ty = env.withdrawal_type(funds_ty.clone())?;
167            let owner: AccountAddress = match withdraw_from {
168                P::WithdrawFrom::Sender => tx_context.sender().into(),
169                P::WithdrawFrom::Sponsor => tx_context
170                    .sponsor()
171                    .ok_or_else(|| {
172                        make_invariant_violation!(
173                            "A sponsor withdrawal requires a sponsor and should have been \
174                            checked at signing"
175                        )
176                    })?
177                    .into(),
178            };
179            (
180                L::InputArg::FundsWithdrawal(L::FundsWithdrawalArg {
181                    from_compatibility_object: is_withdrawal_compatibility_input,
182                    amount,
183                    ty: ty.clone(),
184                    owner,
185                }),
186                L::InputType::Fixed(ty),
187            )
188        }
189    })
190}
191
192fn object_mutability(mutability: SharedObjectMutability) -> L::ObjectMutability {
193    match mutability {
194        SharedObjectMutability::Mutable => L::ObjectMutability::Mutable,
195        SharedObjectMutability::NonExclusiveWrite => L::ObjectMutability::NonExclusiveWrite,
196        SharedObjectMutability::Immutable => L::ObjectMutability::Immutable,
197    }
198}
199
200fn command(env: &Env, command: P::Command) -> Result<L::Command, ExecutionError> {
201    Ok(match command {
202        P::Command::MoveCall(pmc) => {
203            let P::ProgrammableMoveCall {
204                package,
205                module,
206                function: name,
207                type_arguments: ptype_arguments,
208                arguments,
209            } = *pmc;
210            let type_arguments = ptype_arguments
211                .into_iter()
212                .enumerate()
213                .map(|(idx, ty)| env.load_type_input(idx, ty))
214                .collect::<Result<Vec<_>, _>>()?;
215            let function = env.load_function(package, module, name, type_arguments)?;
216            L::Command::MoveCall(Box::new(L::MoveCall {
217                function,
218                arguments,
219            }))
220        }
221        P::Command::MakeMoveVec(ptype_argument, arguments) => {
222            let type_argument = ptype_argument
223                .map(|ty| env.load_type_input(0, ty))
224                .transpose()?;
225            L::Command::MakeMoveVec(type_argument, arguments)
226        }
227        P::Command::TransferObjects(objects, address) => {
228            L::Command::TransferObjects(objects, address)
229        }
230        P::Command::SplitCoins(coin, amounts) => L::Command::SplitCoins(coin, amounts),
231        P::Command::MergeCoins(target, coins) => L::Command::MergeCoins(target, coins),
232        P::Command::Publish(items, object_ids) => {
233            let resolved_linkage = env
234                .linkage_analysis
235                .compute_publication_linkage(&object_ids, env.linkable_store)?;
236            L::Command::Publish(items, object_ids, resolved_linkage)
237        }
238        P::Command::Upgrade(items, object_ids, object_id, argument) => {
239            let resolved_linkage = env
240                .linkage_analysis
241                .compute_publication_linkage(&object_ids, env.linkable_store)?;
242            L::Command::Upgrade(items, object_ids, object_id, argument, resolved_linkage)
243        }
244    })
245}