1use crate::{
5 gas_charger::GasPayment,
6 static_programmable_transactions::linkage::resolved_linkage::{
7 ExecutableLinkage, ResolvedLinkage,
8 },
9};
10use indexmap::IndexSet;
11use move_binary_format::{
12 CompiledModule,
13 file_format::{AbilitySet, CodeOffset, FunctionDefinitionIndex, Visibility},
14};
15use move_core_types::{
16 account_address::AccountAddress,
17 identifier::IdentStr,
18 language_storage::{ModuleId, StructTag},
19 u256::U256,
20};
21use std::{collections::BTreeSet, rc::Rc};
22use sui_types::{
23 Identifier, TypeTag,
24 base_types::{ObjectID, ObjectRef, RESOLVED_TX_CONTEXT, SequenceNumber, TxContextKind},
25 object::ObjectPermissions,
26};
27use sui_verifier::INIT_FN_NAME;
28
29#[derive(Debug)]
34pub struct Transaction {
35 pub gas_payment: Option<GasPayment>,
36 pub inputs: Inputs,
37 pub original_command_len: usize,
40 pub commands: Commands,
41 pub unified_linkage: Option<ExecutableLinkage>,
42}
43
44pub type Inputs = Vec<(InputArg, InputType)>;
45
46pub type Commands = Vec<Command>;
47
48#[derive(Debug)]
49#[cfg_attr(debug_assertions, derive(Clone))]
50pub enum InputArg {
51 Pure(Vec<u8>),
52 Receiving(ObjectRef),
53 Object(ObjectArg),
54 FundsWithdrawal(FundsWithdrawalArg),
55}
56
57#[derive(Debug)]
58#[cfg_attr(debug_assertions, derive(Clone))]
59pub enum ObjectArgKind {
60 ImmObject(ObjectRef),
61 OwnedObject(ObjectRef),
62 ConsensusObject {
63 id: ObjectID,
64 initial_shared_version: SequenceNumber,
65 },
66}
67
68#[derive(Debug)]
69#[cfg_attr(debug_assertions, derive(Clone))]
70pub struct ObjectArg {
71 pub kind: ObjectArgKind,
72 pub refined_permissions: ObjectPermissions,
76}
77
78#[derive(Debug)]
79#[cfg_attr(debug_assertions, derive(Clone))]
80pub struct FundsWithdrawalArg {
81 pub from_compatibility_object: bool,
83 pub ty: Type,
87 pub source: WithdrawalSource,
88 pub amount: U256,
90}
91
92#[derive(Debug)]
93#[cfg_attr(debug_assertions, derive(Clone))]
94pub enum WithdrawalSource {
95 Direct { owner: AccountAddress },
97 Allowance {
99 funder: AccountAddress,
100 id: ObjectID,
101 },
102}
103
104impl WithdrawalSource {
105 pub fn source_account(&self) -> AccountAddress {
107 match self {
108 Self::Direct { owner } => *owner,
109 Self::Allowance { funder, .. } => *funder,
110 }
111 }
112}
113
114#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
115pub enum Type {
116 Bool,
117 U8,
118 U16,
119 U32,
120 U64,
121 U128,
122 U256,
123 Address,
124 Signer,
125 Vector(Rc<Vector>),
126 Datatype(Rc<Datatype>),
127 Reference(bool, Rc<Type>),
128}
129
130#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
131pub struct Vector {
132 pub abilities: AbilitySet,
133 pub element_type: Type,
134}
135
136#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
137pub struct Datatype {
138 pub abilities: AbilitySet,
139 pub module: ModuleId,
140 pub name: Identifier,
141 pub type_arguments: Vec<Type>,
142}
143
144#[derive(Debug, Clone)]
145pub enum InputType {
146 Bytes,
147 Fixed(Type),
148}
149
150#[derive(Debug)]
151pub enum Command {
152 MoveCall(Box<MoveCall>),
153 TransferObjects(Vec<Argument>, Argument),
154 SplitCoins(Argument, Vec<Argument>),
155 MergeCoins(Argument, Vec<Argument>),
156 MakeMoveVec(Option<Type>, Vec<Argument>),
157 Publish(PackagePayload, Vec<ObjectID>, ResolvedLinkage),
158 Upgrade(
159 PackagePayload,
160 Vec<ObjectID>,
161 ObjectID,
162 Argument,
163 ResolvedLinkage,
164 ),
165}
166
167#[derive(Debug, Clone)]
168pub enum PackagePayload {
169 Serialized(Vec<Vec<u8>>),
170 Deserialized(DeserializedPackage),
171}
172
173#[derive(Debug, Clone)]
175pub struct DeserializedPackage {
176 pub deserialized_modules: Vec<CompiledModule>,
178 pub total_bytes: usize,
180 pub computed_digest: [u8; 32],
183 pub modules_with_init: BTreeSet<Identifier>,
185}
186
187impl DeserializedPackage {
188 pub fn new(
189 deserialized_modules: Vec<CompiledModule>,
190 total_bytes: usize,
191 computed_digest: [u8; 32],
192 ) -> Self {
193 let modules_with_init = deserialized_modules
194 .iter()
195 .filter(|module| module_has_init(module))
196 .map(|module| module.identifier_at(module.self_handle().name).to_owned())
197 .collect();
198 Self {
199 deserialized_modules,
200 total_bytes,
201 computed_digest,
202 modules_with_init,
203 }
204 }
205
206 pub fn has_potential_init(&self) -> bool {
209 !self.modules_with_init.is_empty()
210 }
211}
212
213pub(crate) fn module_has_init(module: &CompiledModule) -> bool {
219 module.function_defs().iter().any(|func_def| {
220 let handle = module.function_handle_at(func_def.function);
221 module.identifier_at(handle.name) == INIT_FN_NAME
222 })
223}
224
225#[derive(Debug)]
226pub struct LoadedFunctionInstantiation {
227 pub parameters: Vec<Type>,
228 pub return_: Vec<Type>,
229}
230
231#[derive(Debug)]
232pub struct LoadedFunction {
233 pub version_mid: ModuleId,
234 pub original_mid: ModuleId,
235 pub name: Identifier,
236 pub type_arguments: Vec<Type>,
237 pub signature: LoadedFunctionInstantiation,
238 pub linkage: ExecutableLinkage,
239 pub instruction_length: CodeOffset,
240 pub definition_index: FunctionDefinitionIndex,
241 pub visibility: Visibility,
242 pub is_entry: bool,
243 pub is_native: bool,
244}
245
246#[derive(Debug)]
247pub struct MoveCall {
248 pub function: LoadedFunction,
249 pub arguments: Vec<Argument>,
250}
251
252pub use sui_types::transaction::Argument;
253
254impl ObjectArg {
259 pub fn id(&self) -> ObjectID {
260 self.kind.id()
261 }
262}
263
264impl ObjectArgKind {
265 pub fn id(&self) -> ObjectID {
266 match self {
267 Self::ImmObject(oref) | Self::OwnedObject(oref) => oref.0,
268 Self::ConsensusObject { id, .. } => *id,
269 }
270 }
271}
272
273impl Type {
274 pub fn abilities(&self) -> AbilitySet {
275 match self {
276 Type::Bool
277 | Type::U8
278 | Type::U16
279 | Type::U32
280 | Type::U64
281 | Type::U128
282 | Type::U256
283 | Type::Address => AbilitySet::PRIMITIVES,
284 Type::Signer => AbilitySet::SIGNER,
285 Type::Reference(_, _) => AbilitySet::REFERENCES,
286 Type::Vector(v) => v.abilities,
287 Type::Datatype(dt) => dt.abilities,
288 }
289 }
290
291 pub fn is_tx_context(&self) -> TxContextKind {
292 let (is_mut, inner) = match self {
293 Type::Reference(is_mut, inner) => (*is_mut, inner),
294 _ => return TxContextKind::None,
295 };
296 let Type::Datatype(dt) = &**inner else {
297 return TxContextKind::None;
298 };
299 if dt.qualified_ident() == RESOLVED_TX_CONTEXT {
300 if is_mut {
301 TxContextKind::Mutable
302 } else {
303 TxContextKind::Immutable
304 }
305 } else {
306 TxContextKind::None
307 }
308 }
309
310 pub fn is_tx_context_by_value(&self) -> bool {
312 matches!(self, Type::Datatype(dt) if dt.qualified_ident() == RESOLVED_TX_CONTEXT)
313 }
314
315 pub fn all_addresses(&self) -> IndexSet<AccountAddress> {
316 match self {
317 Type::Bool
318 | Type::U8
319 | Type::U16
320 | Type::U32
321 | Type::U64
322 | Type::U128
323 | Type::U256
324 | Type::Address
325 | Type::Signer => IndexSet::new(),
326 Type::Vector(v) => v.element_type.all_addresses(),
327 Type::Reference(_, inner) => inner.all_addresses(),
328 Type::Datatype(dt) => dt.all_addresses(),
329 }
330 }
331
332 pub fn node_count(&self) -> u64 {
333 use Type::*;
334 let mut total = 0u64;
335 let mut stack = vec![self];
336
337 while let Some(ty) = stack.pop() {
338 total = total.saturating_add(1);
339 match ty {
340 Bool | U8 | U16 | U32 | U64 | U128 | U256 | Address | Signer => {}
341 Vector(v) => stack.push(&v.element_type),
342 Reference(_, inner) => stack.push(inner),
343 Datatype(dt) => {
344 stack.extend(&dt.type_arguments);
345 }
346 }
347 }
348
349 total
350 }
351
352 pub fn is_reference(&self) -> bool {
353 match self {
354 Type::Bool
355 | Type::U8
356 | Type::U16
357 | Type::U32
358 | Type::U64
359 | Type::U128
360 | Type::U256
361 | Type::Address
362 | Type::Signer
363 | Type::Vector(_)
364 | Type::Datatype(_) => false,
365 Type::Reference(_, _) => true,
366 }
367 }
368}
369
370impl Datatype {
371 pub fn qualified_ident(&self) -> (&AccountAddress, &IdentStr, &IdentStr) {
372 (
373 self.module.address(),
374 self.module.name(),
375 self.name.as_ident_str(),
376 )
377 }
378
379 pub fn all_addresses(&self) -> IndexSet<AccountAddress> {
380 let mut addresses = IndexSet::new();
381 addresses.insert(*self.module.address());
382 for arg in &self.type_arguments {
383 addresses.extend(arg.all_addresses());
384 }
385 addresses
386 }
387}
388
389impl Command {
390 pub fn arguments_mut(&mut self) -> Box<dyn Iterator<Item = &mut Argument> + '_> {
391 match self {
392 Command::MoveCall(mc) => Box::new(mc.arguments.iter_mut()),
393 Command::TransferObjects(objs, recipient) => {
394 Box::new(objs.iter_mut().chain(std::iter::once(recipient)))
395 }
396 Command::SplitCoins(coin, amounts) => {
397 Box::new(std::iter::once(coin).chain(amounts.iter_mut()))
398 }
399 Command::MergeCoins(coin, coins) => {
400 Box::new(std::iter::once(coin).chain(coins.iter_mut()))
401 }
402 Command::MakeMoveVec(_, elements) => Box::new(elements.iter_mut()),
403 Command::Publish(_, _, _) => Box::new(std::iter::empty()),
404 Command::Upgrade(_, _, _, obj, _) => Box::new(std::iter::once(obj)),
405 }
406 }
407
408 pub fn arguments(&self) -> Box<dyn Iterator<Item = &Argument> + '_> {
409 match self {
410 Command::MoveCall(mc) => Box::new(mc.arguments.iter()),
411 Command::TransferObjects(objs, recipient) => {
412 Box::new(objs.iter().chain(std::iter::once(recipient)))
413 }
414 Command::SplitCoins(coin, amounts) => {
415 Box::new(std::iter::once(coin).chain(amounts.iter()))
416 }
417 Command::MergeCoins(coin, coins) => Box::new(std::iter::once(coin).chain(coins.iter())),
418 Command::MakeMoveVec(_, elements) => Box::new(elements.iter()),
419 Command::Publish(_, _, _) => Box::new(std::iter::empty()),
420 Command::Upgrade(_, _, _, obj, _) => Box::new(std::iter::once(obj)),
421 }
422 }
423}
424
425impl TryFrom<Type> for TypeTag {
430 type Error = &'static str;
431 fn try_from(ty: Type) -> Result<Self, Self::Error> {
432 Ok(match ty {
433 Type::Bool => TypeTag::Bool,
434 Type::U8 => TypeTag::U8,
435 Type::U16 => TypeTag::U16,
436 Type::U32 => TypeTag::U32,
437 Type::U64 => TypeTag::U64,
438 Type::U128 => TypeTag::U128,
439 Type::U256 => TypeTag::U256,
440 Type::Address => TypeTag::Address,
441 Type::Signer => TypeTag::Signer,
442 Type::Vector(inner) => {
443 let Vector { element_type, .. } = &*inner;
444 TypeTag::Vector(Box::new(element_type.clone().try_into()?))
445 }
446 Type::Datatype(dt) => {
447 let dt: &Datatype = &dt;
448 TypeTag::Struct(Box::new(dt.try_into()?))
449 }
450 Type::Reference(_, _) => return Err("unexpected reference type"),
451 })
452 }
453}
454
455impl TryFrom<&Datatype> for StructTag {
456 type Error = &'static str;
457
458 fn try_from(dt: &Datatype) -> Result<Self, Self::Error> {
459 let Datatype {
460 module,
461 name,
462 type_arguments,
463 ..
464 } = dt;
465 Ok(StructTag {
466 address: *module.address(),
467 module: module.name().to_owned(),
468 name: name.to_owned(),
469 type_params: type_arguments
470 .iter()
471 .map(|t| t.clone().try_into())
472 .collect::<Result<Vec<TypeTag>, _>>()?,
473 })
474 }
475}