sui_adapter_latest/static_programmable_transactions/loading/
translate.rs1use 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 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 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 is_withdrawal_compatibility_input: bool,
76 arg: CallArg,
77) -> Result<(L::InputArg, L::InputType), ExecutionError> {
78 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 };
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}