Skip to main content

sui_adapter_latest/static_programmable_transactions/loading/
ast.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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//**************************************************************************************************
30// AST Nodes
31//**************************************************************************************************
32
33#[derive(Debug)]
34pub struct Transaction {
35    pub gas_payment: Option<GasPayment>,
36    pub inputs: Inputs,
37    /// Original number of commands in the transaction. After typing, Spanned indices in the AST
38    /// should be < `original_command_len`
39    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    /// Permissions, potentially refined/limited based on the input argument. For example if a
73    /// shared object is used but marked as read-only, the permissions would be refined to being
74    /// _only_ immutable usage.
75    pub refined_permissions: ObjectPermissions,
76}
77
78#[derive(Debug)]
79#[cfg_attr(debug_assertions, derive(Clone))]
80pub struct FundsWithdrawalArg {
81    // if true, it was from a compatibility object input, not a intentional withdrawal argument
82    pub from_compatibility_object: bool,
83    /// The full type.
84    /// Either `sui::funds_accumulator::Withdrawal<T>` for a direct source, or
85    /// `sui::allowance::AllowanceWithdrawal<T>` for an allowance source
86    pub ty: Type,
87    pub source: WithdrawalSource,
88    /// This amount is verified to be <= the max for the type described by the `T` in `ty`
89    pub amount: U256,
90}
91
92#[derive(Debug)]
93#[cfg_attr(debug_assertions, derive(Clone))]
94pub enum WithdrawalSource {
95    /// A `sui::funds_accumulator::Withdrawal` from the sender/sponsor
96    Direct { owner: AccountAddress },
97    /// An `sui::allowance::AllowanceWithdrawal` permissioned by the `Allowance` object `id`
98    Allowance {
99        funder: AccountAddress,
100        id: ObjectID,
101    },
102}
103
104impl WithdrawalSource {
105    /// The account the withdrawal debits.
106    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(/* is mut */ 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(/* T for vector<T> */ 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// A Deserialized but not yet verified package created as part of loading.
174#[derive(Debug, Clone)]
175pub struct DeserializedPackage {
176    // NB: Modules are deserialized but not yet verified. They _are_ bounds checked though.
177    pub deserialized_modules: Vec<CompiledModule>,
178    // Sum of the sizes of all modules in (serialized) bytes, used for metering
179    pub total_bytes: usize,
180    // The computed digest of the package --
181    // `MovePackage::compute_digest_for_modules_and_deps` with `hash_modules` set to `true`.
182    pub computed_digest: [u8; 32],
183    // Names of the modules in this package that define a function named `init`.
184    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    /// Returns true if this package defines any modules with function that could be a possible
207    /// `init` function.
208    pub fn has_potential_init(&self) -> bool {
209        !self.modules_with_init.is_empty()
210    }
211}
212
213/// Whether `module` defines a function named `init`.
214///
215/// NB: we presuppose that a function named `init` is the module's initializer. If it does not
216/// conform to the `init` signature requirements the entry points verifier rejects the publish
217/// later, failing the transaction as a whole.
218pub(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
254//**************************************************************************************************
255// impl
256//**************************************************************************************************
257
258impl 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    /// Is this the `TxContext` datatype itself, not behind a reference?
311    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
425//**************************************************************************************************
426// Traits
427//**************************************************************************************************
428
429impl 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}