Skip to main content

sui_adapter_latest/static_programmable_transactions/typing/
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::{
7        linkage::resolved_linkage::{ExecutableLinkage, ResolvedLinkage},
8        loading::ast::{self as L, PackagePayload},
9        spanned::Spanned,
10    },
11};
12use indexmap::{IndexMap, IndexSet};
13use move_core_types::u256::U256;
14use move_vm_runtime::execution::values::VectorSpecialization;
15use std::cell::OnceCell;
16use sui_types::base_types::{ObjectID, ObjectRef};
17
18//**************************************************************************************************
19// AST Nodes
20//**************************************************************************************************
21
22#[derive(Debug)]
23pub struct Transaction {
24    pub gas_payment: Option<GasPayment>,
25    /// Gathered BCS bytes from Pure inputs
26    pub bytes: IndexSet<Vec<u8>>,
27    // All input objects
28    pub objects: Vec<ObjectInput>,
29    /// All Withdrawal inputs
30    pub withdrawals: Vec<WithdrawalInput>,
31    /// All pure inputs
32    pub pure: Vec<PureInput>,
33    /// All receiving inputs
34    pub receiving: Vec<ReceivingInput>,
35    pub withdrawal_compatibility_conversions: IndexMap<Location, WithdrawalCompatibilityConversion>,
36    /// Original number of commands in the transaction. All Spanned indices in the AST should be
37    /// < `original_command_len`
38    pub original_command_len: usize,
39    pub commands: Commands,
40    pub unified_linkage: Option<ExecutableLinkage>,
41}
42
43/// The original index into the `input` vector of the transaction, before the inputs were split
44/// into their respective categories (objects, pure, or receiving).
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub struct InputIndex(pub u16);
47
48#[derive(Debug)]
49pub struct ObjectInput {
50    pub original_input_index: InputIndex,
51    pub arg: ObjectArg,
52    pub ty: Type,
53}
54
55pub type ByteIndex = usize;
56
57#[derive(Debug)]
58pub struct PureInput {
59    pub original_input_index: InputIndex,
60    // A index into `byte` table of BCS bytes
61    pub byte_index: ByteIndex,
62    // the type that the BCS bytes will be deserialized into
63    pub ty: Type,
64    // Information about where this constraint came from
65    pub constraint: BytesConstraint,
66}
67
68#[derive(Debug)]
69pub struct ReceivingInput {
70    pub original_input_index: InputIndex,
71    pub object_ref: ObjectRef,
72    pub ty: Type,
73    // Information about where this constraint came from
74    pub constraint: BytesConstraint,
75}
76
77#[derive(Debug)]
78pub struct WithdrawalInput {
79    pub original_input_index: InputIndex,
80    /// The full type.
81    /// Either `sui::funds_accumulator::Withdrawal<T>` for a direct source, or
82    /// `sui::allowance::AllowanceWithdrawal<T>` for an allowance source
83    pub ty: Type,
84    pub source: WithdrawalSource,
85    /// This amount is verified to be <= the max for the type described by the `T` in `ty`
86    pub amount: U256,
87}
88
89#[derive(Debug, Clone, Copy)]
90pub struct WithdrawalCompatibilityConversion {
91    // The pure input location of the owner address
92    pub owner: Location,
93    // Result index to conversion call to `sui::coin::redeem_funds`
94    pub conversion_result: u16,
95}
96
97pub type Commands = Vec<Command>;
98
99pub type ObjectArg = L::ObjectArg;
100
101pub type Type = L::Type;
102
103pub type WithdrawalSource = L::WithdrawalSource;
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106/// Information for a given constraint for input bytes
107pub struct BytesConstraint {
108    /// The command that first added this constraint
109    pub command: u16,
110    /// The argument in that command
111    pub argument: u16,
112}
113
114pub type ResultType = Vec<Type>;
115
116pub type Command = Spanned<Command_>;
117
118#[derive(Debug)]
119pub struct Command_ {
120    /// The command
121    pub command: Command__,
122    /// The type of the return values of the command
123    pub result_type: ResultType,
124    /// Markers to drop unused results from the command. These are inferred based on any usage
125    /// of the given result `Result(i,j)` after this command. This is leveraged by the borrow
126    /// checker to remove unused references to allow potentially reuse of parent references.
127    /// The value at result `j` is unused and can be dropped if `drop_value[j]` is true.
128    pub drop_values: Vec</* drop value */ bool>,
129    /// Marks if the command consumes by value either a legacy shared object, or a party object with
130    /// post-execution checks. A party object has post-execution checks if it is used with mutable
131    /// usage and is missing one of the mutable permissions.
132    pub incurs_post_execution_checks: bool,
133}
134
135#[derive(Debug)]
136pub enum Command__ {
137    MoveCall(Box<MoveCall>),
138    TransferObjects(Vec<Argument>, Argument),
139    SplitCoins(/* Coin<T> */ Type, Argument, Vec<Argument>),
140    MergeCoins(/* Coin<T> */ Type, Argument, Vec<Argument>),
141    MakeMoveVec(/* T for vector<T> */ Type, Vec<Argument>),
142    Publish(PackagePayload, Vec<ObjectID>, ResolvedLinkage),
143    Upgrade(
144        PackagePayload,
145        Vec<ObjectID>,
146        ObjectID,
147        Argument,
148        ResolvedLinkage,
149    ),
150}
151
152pub type LoadedFunctionInstantiation = L::LoadedFunctionInstantiation;
153
154pub type LoadedFunction = L::LoadedFunction;
155
156#[derive(Debug)]
157pub struct MoveCall {
158    pub function: LoadedFunction,
159    pub arguments: Vec<Argument>,
160}
161
162#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
163pub enum Location {
164    TxContext,
165    GasCoin,
166    ObjectInput(u16),
167    WithdrawalInput(u16),
168    PureInput(u16),
169    ReceivingInput(u16),
170    Result(u16, u16),
171}
172
173// Non borrowing usage of locations, moving or copying
174#[derive(Clone, Debug)]
175pub enum Usage {
176    Move(Location),
177    Copy {
178        location: Location,
179        /// Was this location borrowed at the time of copying?
180        /// Initially empty and populated by `memory_safety`
181        borrowed: OnceCell<bool>,
182    },
183}
184
185pub type Argument = Spanned<Argument_>;
186pub type Argument_ = (Argument__, Type);
187
188#[derive(Clone, Debug)]
189pub enum Argument__ {
190    /// Move or copy a value
191    Use(Usage),
192    /// Borrow a value, i.e. `&x` or `&mut x`
193    Borrow(/* mut */ bool, Location),
194    /// Read a value from a reference, i.e. `*&x`
195    Read(Usage),
196    /// Freeze a mutable reference, making an `&t` from `&mut t`
197    Freeze(Usage),
198}
199
200//**************************************************************************************************
201// impl
202//**************************************************************************************************
203
204impl Transaction {
205    pub fn types(&self) -> impl Iterator<Item = &Type> {
206        let pure_types = self.pure.iter().map(|p| &p.ty);
207        let object_types = self.objects.iter().map(|o| &o.ty);
208        let receiving_types = self.receiving.iter().map(|r| &r.ty);
209        let command_types = self.commands.iter().flat_map(command_types);
210        pure_types
211            .chain(object_types)
212            .chain(receiving_types)
213            .chain(command_types)
214    }
215}
216
217impl Usage {
218    pub fn new_move(location: Location) -> Usage {
219        Usage::Move(location)
220    }
221
222    pub fn new_copy(location: Location) -> Usage {
223        Usage::Copy {
224            location,
225            borrowed: OnceCell::new(),
226        }
227    }
228
229    pub fn location(&self) -> Location {
230        match self {
231            Usage::Move(location) => *location,
232            Usage::Copy { location, .. } => *location,
233        }
234    }
235}
236
237impl Argument__ {
238    pub fn new_move(location: Location) -> Self {
239        Self::Use(Usage::new_move(location))
240    }
241
242    pub fn new_copy(location: Location) -> Self {
243        Self::Use(Usage::new_copy(location))
244    }
245
246    pub fn location(&self) -> Location {
247        match self {
248            Self::Use(usage) | Self::Read(usage) => usage.location(),
249            Self::Borrow(_, location) => *location,
250            Self::Freeze(usage) => usage.location(),
251        }
252    }
253}
254
255impl Command__ {
256    pub fn arguments(&self) -> Box<dyn Iterator<Item = &Argument> + '_> {
257        match self {
258            Command__::MoveCall(mc) => Box::new(mc.arguments.iter()),
259            Command__::TransferObjects(objs, addr) => {
260                Box::new(objs.iter().chain(std::iter::once(addr)))
261            }
262            Command__::SplitCoins(_, coin, amounts) => {
263                Box::new(std::iter::once(coin).chain(amounts))
264            }
265            Command__::MergeCoins(_, target, sources) => {
266                Box::new(std::iter::once(target).chain(sources))
267            }
268            Command__::MakeMoveVec(_, elems) => Box::new(elems.iter()),
269            Command__::Publish(_, _, _) => Box::new(std::iter::empty()),
270            Command__::Upgrade(_, _, _, arg, _) => Box::new(std::iter::once(arg)),
271        }
272    }
273
274    pub fn types(&self) -> Box<dyn Iterator<Item = &Type> + '_> {
275        match self {
276            Command__::TransferObjects(args, arg) => {
277                Box::new(std::iter::once(arg).chain(args.iter()).map(argument_type))
278            }
279            Command__::SplitCoins(ty, arg, args) | Command__::MergeCoins(ty, arg, args) => {
280                Box::new(
281                    std::iter::once(arg)
282                        .chain(args.iter())
283                        .map(argument_type)
284                        .chain(std::iter::once(ty)),
285                )
286            }
287            Command__::MakeMoveVec(ty, args) => {
288                Box::new(args.iter().map(argument_type).chain(std::iter::once(ty)))
289            }
290            Command__::MoveCall(call) => Box::new(
291                call.arguments
292                    .iter()
293                    .map(argument_type)
294                    .chain(call.function.type_arguments.iter())
295                    .chain(call.function.signature.parameters.iter())
296                    .chain(call.function.signature.return_.iter()),
297            ),
298            Command__::Upgrade(_, _, _, arg, _) => {
299                Box::new(std::iter::once(arg).map(argument_type))
300            }
301            Command__::Publish(_, _, _) => Box::new(std::iter::empty()),
302        }
303    }
304
305    pub fn arguments_len(&self) -> usize {
306        let n = match self {
307            Command__::MoveCall(mc) => mc.arguments.len(),
308            Command__::TransferObjects(objs, _) => objs.len().saturating_add(1),
309            Command__::SplitCoins(_, _, amounts) => amounts.len().saturating_add(1),
310            Command__::MergeCoins(_, _, sources) => sources.len().saturating_add(1),
311            Command__::MakeMoveVec(_, elems) => elems.len(),
312            Command__::Publish(_, _, _) => 0,
313            Command__::Upgrade(_, _, _, _, _) => 1,
314        };
315        debug_assert_eq!(self.arguments().count(), n);
316        n
317    }
318}
319
320//**************************************************************************************************
321// Standalone functions
322//**************************************************************************************************
323
324pub fn command_types(cmd: &Command) -> impl Iterator<Item = &Type> {
325    let result_types = cmd.value.result_type.iter();
326    let command_types = cmd.value.command.types();
327    result_types.chain(command_types)
328}
329
330pub fn argument_type(arg: &Argument) -> &Type {
331    &arg.value.1
332}
333
334//**************************************************************************************************
335// traits
336//**************************************************************************************************
337
338impl TryFrom<Type> for VectorSpecialization {
339    type Error = &'static str;
340
341    fn try_from(value: Type) -> Result<Self, Self::Error> {
342        Ok(match value {
343            Type::U8 => VectorSpecialization::U8,
344            Type::U16 => VectorSpecialization::U16,
345            Type::U32 => VectorSpecialization::U32,
346            Type::U64 => VectorSpecialization::U64,
347            Type::U128 => VectorSpecialization::U128,
348            Type::U256 => VectorSpecialization::U256,
349            Type::Address => VectorSpecialization::Address,
350            Type::Bool => VectorSpecialization::Bool,
351            Type::Signer | Type::Vector(_) | Type::Datatype(_) => VectorSpecialization::Container,
352            Type::Reference(_, _) => return Err("unexpected reference in vector specialization"),
353        })
354    }
355}