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::rc::Rc;
22use sui_types::{
23    Identifier, TypeTag,
24    base_types::{ObjectID, ObjectRef, RESOLVED_TX_CONTEXT, SequenceNumber, TxContextKind},
25    object::ObjectPermissions,
26};
27
28//**************************************************************************************************
29// AST Nodes
30//**************************************************************************************************
31
32#[derive(Debug)]
33pub struct Transaction {
34    pub gas_payment: Option<GasPayment>,
35    pub inputs: Inputs,
36    /// Original number of commands in the transaction. After typing, Spanned indices in the AST
37    /// should be < `original_command_len`
38    pub original_command_len: usize,
39    pub commands: Commands,
40}
41
42pub type Inputs = Vec<(InputArg, InputType)>;
43
44pub type Commands = Vec<Command>;
45
46#[derive(Debug)]
47#[cfg_attr(debug_assertions, derive(Clone))]
48pub enum InputArg {
49    Pure(Vec<u8>),
50    Receiving(ObjectRef),
51    Object(ObjectArg),
52    FundsWithdrawal(FundsWithdrawalArg),
53}
54
55#[derive(Debug)]
56#[cfg_attr(debug_assertions, derive(Clone))]
57pub enum ObjectArgKind {
58    ImmObject(ObjectRef),
59    OwnedObject(ObjectRef),
60    ConsensusObject {
61        id: ObjectID,
62        initial_shared_version: SequenceNumber,
63    },
64}
65
66#[derive(Debug)]
67#[cfg_attr(debug_assertions, derive(Clone))]
68pub struct ObjectArg {
69    pub kind: ObjectArgKind,
70    /// Permissions, potentially refined/limited based on the input argument. For example if a
71    /// shared object is used but marked as read-only, the permissions would be refined to being
72    /// _only_ immutable usage.
73    pub refined_permissions: ObjectPermissions,
74}
75
76#[derive(Debug)]
77#[cfg_attr(debug_assertions, derive(Clone))]
78pub struct FundsWithdrawalArg {
79    // if true, it was from a compatibility object input, not a intentional withdrawal argument
80    pub from_compatibility_object: bool,
81    /// The full type `sui::funds_accumulator::Withdrawal<T>`
82    pub ty: Type,
83    pub owner: AccountAddress,
84    /// This amount is verified to be <= the max for the type described by the `T` in `ty`
85    pub amount: U256,
86}
87
88#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
89pub enum Type {
90    Bool,
91    U8,
92    U16,
93    U32,
94    U64,
95    U128,
96    U256,
97    Address,
98    Signer,
99    Vector(Rc<Vector>),
100    Datatype(Rc<Datatype>),
101    Reference(/* is mut */ bool, Rc<Type>),
102}
103
104#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
105pub struct Vector {
106    pub abilities: AbilitySet,
107    pub element_type: Type,
108}
109
110#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
111pub struct Datatype {
112    pub abilities: AbilitySet,
113    pub module: ModuleId,
114    pub name: Identifier,
115    pub type_arguments: Vec<Type>,
116}
117
118#[derive(Debug, Clone)]
119pub enum InputType {
120    Bytes,
121    Fixed(Type),
122}
123
124#[derive(Debug)]
125pub enum Command {
126    MoveCall(Box<MoveCall>),
127    TransferObjects(Vec<Argument>, Argument),
128    SplitCoins(Argument, Vec<Argument>),
129    MergeCoins(Argument, Vec<Argument>),
130    MakeMoveVec(/* T for vector<T> */ Option<Type>, Vec<Argument>),
131    Publish(PackagePayload, Vec<ObjectID>, ResolvedLinkage),
132    Upgrade(
133        PackagePayload,
134        Vec<ObjectID>,
135        ObjectID,
136        Argument,
137        ResolvedLinkage,
138    ),
139}
140
141#[derive(Debug, Clone)]
142pub enum PackagePayload {
143    Serialized(Vec<Vec<u8>>),
144    Deserialized(DeserializedPackage),
145}
146
147// A Deserialized but not yet verified package created as part of loading.
148#[derive(Debug, Clone)]
149pub struct DeserializedPackage {
150    // NB: Modules are deserialized but not yet verified. They _are_ bounds checked though.
151    pub deserialized_modules: Vec<CompiledModule>,
152    // Sum of the sizes of all modules in (serialized) bytes, used for metering
153    pub total_bytes: usize,
154    // The computed digest of the package --
155    // `MovePackage::compute_digest_for_modules_and_deps` with `hash_modules` set to `true`.
156    pub computed_digest: [u8; 32],
157}
158
159#[derive(Debug)]
160pub struct LoadedFunctionInstantiation {
161    pub parameters: Vec<Type>,
162    pub return_: Vec<Type>,
163}
164
165#[derive(Debug)]
166pub struct LoadedFunction {
167    pub version_mid: ModuleId,
168    pub original_mid: ModuleId,
169    pub name: Identifier,
170    pub type_arguments: Vec<Type>,
171    pub signature: LoadedFunctionInstantiation,
172    pub linkage: ExecutableLinkage,
173    pub instruction_length: CodeOffset,
174    pub definition_index: FunctionDefinitionIndex,
175    pub visibility: Visibility,
176    pub is_entry: bool,
177    pub is_native: bool,
178}
179
180#[derive(Debug)]
181pub struct MoveCall {
182    pub function: LoadedFunction,
183    pub arguments: Vec<Argument>,
184}
185
186pub use sui_types::transaction::Argument;
187
188//**************************************************************************************************
189// impl
190//**************************************************************************************************
191
192impl ObjectArg {
193    pub fn id(&self) -> ObjectID {
194        self.kind.id()
195    }
196}
197
198impl ObjectArgKind {
199    pub fn id(&self) -> ObjectID {
200        match self {
201            Self::ImmObject(oref) | Self::OwnedObject(oref) => oref.0,
202            Self::ConsensusObject { id, .. } => *id,
203        }
204    }
205}
206
207impl Type {
208    pub fn abilities(&self) -> AbilitySet {
209        match self {
210            Type::Bool
211            | Type::U8
212            | Type::U16
213            | Type::U32
214            | Type::U64
215            | Type::U128
216            | Type::U256
217            | Type::Address => AbilitySet::PRIMITIVES,
218            Type::Signer => AbilitySet::SIGNER,
219            Type::Reference(_, _) => AbilitySet::REFERENCES,
220            Type::Vector(v) => v.abilities,
221            Type::Datatype(dt) => dt.abilities,
222        }
223    }
224
225    pub fn is_tx_context(&self) -> TxContextKind {
226        let (is_mut, inner) = match self {
227            Type::Reference(is_mut, inner) => (*is_mut, inner),
228            _ => return TxContextKind::None,
229        };
230        let Type::Datatype(dt) = &**inner else {
231            return TxContextKind::None;
232        };
233        if dt.qualified_ident() == RESOLVED_TX_CONTEXT {
234            if is_mut {
235                TxContextKind::Mutable
236            } else {
237                TxContextKind::Immutable
238            }
239        } else {
240            TxContextKind::None
241        }
242    }
243    pub fn all_addresses(&self) -> IndexSet<AccountAddress> {
244        match self {
245            Type::Bool
246            | Type::U8
247            | Type::U16
248            | Type::U32
249            | Type::U64
250            | Type::U128
251            | Type::U256
252            | Type::Address
253            | Type::Signer => IndexSet::new(),
254            Type::Vector(v) => v.element_type.all_addresses(),
255            Type::Reference(_, inner) => inner.all_addresses(),
256            Type::Datatype(dt) => dt.all_addresses(),
257        }
258    }
259
260    pub fn node_count(&self) -> u64 {
261        use Type::*;
262        let mut total = 0u64;
263        let mut stack = vec![self];
264
265        while let Some(ty) = stack.pop() {
266            total = total.saturating_add(1);
267            match ty {
268                Bool | U8 | U16 | U32 | U64 | U128 | U256 | Address | Signer => {}
269                Vector(v) => stack.push(&v.element_type),
270                Reference(_, inner) => stack.push(inner),
271                Datatype(dt) => {
272                    stack.extend(&dt.type_arguments);
273                }
274            }
275        }
276
277        total
278    }
279
280    pub fn is_reference(&self) -> bool {
281        match self {
282            Type::Bool
283            | Type::U8
284            | Type::U16
285            | Type::U32
286            | Type::U64
287            | Type::U128
288            | Type::U256
289            | Type::Address
290            | Type::Signer
291            | Type::Vector(_)
292            | Type::Datatype(_) => false,
293            Type::Reference(_, _) => true,
294        }
295    }
296}
297
298impl Datatype {
299    pub fn qualified_ident(&self) -> (&AccountAddress, &IdentStr, &IdentStr) {
300        (
301            self.module.address(),
302            self.module.name(),
303            self.name.as_ident_str(),
304        )
305    }
306
307    pub fn all_addresses(&self) -> IndexSet<AccountAddress> {
308        let mut addresses = IndexSet::new();
309        addresses.insert(*self.module.address());
310        for arg in &self.type_arguments {
311            addresses.extend(arg.all_addresses());
312        }
313        addresses
314    }
315}
316
317impl Command {
318    pub fn arguments_mut(&mut self) -> Box<dyn Iterator<Item = &mut Argument> + '_> {
319        match self {
320            Command::MoveCall(mc) => Box::new(mc.arguments.iter_mut()),
321            Command::TransferObjects(objs, recipient) => {
322                Box::new(objs.iter_mut().chain(std::iter::once(recipient)))
323            }
324            Command::SplitCoins(coin, amounts) => {
325                Box::new(std::iter::once(coin).chain(amounts.iter_mut()))
326            }
327            Command::MergeCoins(coin, coins) => {
328                Box::new(std::iter::once(coin).chain(coins.iter_mut()))
329            }
330            Command::MakeMoveVec(_, elements) => Box::new(elements.iter_mut()),
331            Command::Publish(_, _, _) => Box::new(std::iter::empty()),
332            Command::Upgrade(_, _, _, obj, _) => Box::new(std::iter::once(obj)),
333        }
334    }
335
336    pub fn arguments(&self) -> Box<dyn Iterator<Item = &Argument> + '_> {
337        match self {
338            Command::MoveCall(mc) => Box::new(mc.arguments.iter()),
339            Command::TransferObjects(objs, recipient) => {
340                Box::new(objs.iter().chain(std::iter::once(recipient)))
341            }
342            Command::SplitCoins(coin, amounts) => {
343                Box::new(std::iter::once(coin).chain(amounts.iter()))
344            }
345            Command::MergeCoins(coin, coins) => Box::new(std::iter::once(coin).chain(coins.iter())),
346            Command::MakeMoveVec(_, elements) => Box::new(elements.iter()),
347            Command::Publish(_, _, _) => Box::new(std::iter::empty()),
348            Command::Upgrade(_, _, _, obj, _) => Box::new(std::iter::once(obj)),
349        }
350    }
351}
352
353//**************************************************************************************************
354// Traits
355//**************************************************************************************************
356
357impl TryFrom<Type> for TypeTag {
358    type Error = &'static str;
359    fn try_from(ty: Type) -> Result<Self, Self::Error> {
360        Ok(match ty {
361            Type::Bool => TypeTag::Bool,
362            Type::U8 => TypeTag::U8,
363            Type::U16 => TypeTag::U16,
364            Type::U32 => TypeTag::U32,
365            Type::U64 => TypeTag::U64,
366            Type::U128 => TypeTag::U128,
367            Type::U256 => TypeTag::U256,
368            Type::Address => TypeTag::Address,
369            Type::Signer => TypeTag::Signer,
370            Type::Vector(inner) => {
371                let Vector { element_type, .. } = &*inner;
372                TypeTag::Vector(Box::new(element_type.clone().try_into()?))
373            }
374            Type::Datatype(dt) => {
375                let dt: &Datatype = &dt;
376                TypeTag::Struct(Box::new(dt.try_into()?))
377            }
378            Type::Reference(_, _) => return Err("unexpected reference type"),
379        })
380    }
381}
382
383impl TryFrom<&Datatype> for StructTag {
384    type Error = &'static str;
385
386    fn try_from(dt: &Datatype) -> Result<Self, Self::Error> {
387        let Datatype {
388            module,
389            name,
390            type_arguments,
391            ..
392        } = dt;
393        Ok(StructTag {
394            address: *module.address(),
395            module: module.name().to_owned(),
396            name: name.to_owned(),
397            type_params: type_arguments
398                .iter()
399                .map(|t| t.clone().try_into())
400                .collect::<Result<Vec<TypeTag>, _>>()?,
401        })
402    }
403}
404
405//**************************************************************************************************
406// Tests
407//**************************************************************************************************
408
409#[test]
410fn enum_size() {
411    assert_eq!(std::mem::size_of::<Type>(), 16);
412}