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::file_format::{
12 AbilitySet, CodeOffset, FunctionDefinitionIndex, Visibility,
13};
14use move_core_types::{
15 account_address::AccountAddress,
16 identifier::IdentStr,
17 language_storage::{ModuleId, StructTag},
18 u256::U256,
19};
20use std::rc::Rc;
21use sui_types::{
22 Identifier, TypeTag,
23 base_types::{ObjectID, ObjectRef, RESOLVED_TX_CONTEXT, SequenceNumber, TxContextKind},
24 object::ObjectPermissions,
25};
26
27#[derive(Debug)]
32pub struct Transaction {
33 pub gas_payment: Option<GasPayment>,
34 pub inputs: Inputs,
35 pub original_command_len: usize,
38 pub commands: Commands,
39}
40
41pub type Inputs = Vec<(InputArg, InputType)>;
42
43pub type Commands = Vec<Command>;
44
45#[derive(Debug)]
46#[cfg_attr(debug_assertions, derive(Clone))]
47pub enum InputArg {
48 Pure(Vec<u8>),
49 Receiving(ObjectRef),
50 Object(ObjectArg),
51 FundsWithdrawal(FundsWithdrawalArg),
52}
53
54#[derive(Debug)]
55#[cfg_attr(debug_assertions, derive(Clone))]
56pub enum ObjectArgKind {
57 ImmObject(ObjectRef),
58 OwnedObject(ObjectRef),
59 ConsensusObject {
60 id: ObjectID,
61 initial_shared_version: SequenceNumber,
62 },
63}
64
65#[derive(Debug)]
66#[cfg_attr(debug_assertions, derive(Clone))]
67pub struct ObjectArg {
68 pub kind: ObjectArgKind,
69 pub refined_permissions: ObjectPermissions,
73}
74
75#[derive(Debug)]
76#[cfg_attr(debug_assertions, derive(Clone))]
77pub struct FundsWithdrawalArg {
78 pub from_compatibility_object: bool,
80 pub ty: Type,
82 pub owner: AccountAddress,
83 pub amount: U256,
85}
86
87#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
88pub enum Type {
89 Bool,
90 U8,
91 U16,
92 U32,
93 U64,
94 U128,
95 U256,
96 Address,
97 Signer,
98 Vector(Rc<Vector>),
99 Datatype(Rc<Datatype>),
100 Reference(bool, Rc<Type>),
101}
102
103#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
104pub struct Vector {
105 pub abilities: AbilitySet,
106 pub element_type: Type,
107}
108
109#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
110pub struct Datatype {
111 pub abilities: AbilitySet,
112 pub module: ModuleId,
113 pub name: Identifier,
114 pub type_arguments: Vec<Type>,
115}
116
117#[derive(Debug, Clone)]
118pub enum InputType {
119 Bytes,
120 Fixed(Type),
121}
122
123#[derive(Debug)]
124pub enum Command {
125 MoveCall(Box<MoveCall>),
126 TransferObjects(Vec<Argument>, Argument),
127 SplitCoins(Argument, Vec<Argument>),
128 MergeCoins(Argument, Vec<Argument>),
129 MakeMoveVec(Option<Type>, Vec<Argument>),
130 Publish(Vec<Vec<u8>>, Vec<ObjectID>, ResolvedLinkage),
131 Upgrade(
132 Vec<Vec<u8>>,
133 Vec<ObjectID>,
134 ObjectID,
135 Argument,
136 ResolvedLinkage,
137 ),
138}
139
140#[derive(Debug)]
141pub struct LoadedFunctionInstantiation {
142 pub parameters: Vec<Type>,
143 pub return_: Vec<Type>,
144}
145
146#[derive(Debug)]
147pub struct LoadedFunction {
148 pub version_mid: ModuleId,
149 pub original_mid: ModuleId,
150 pub name: Identifier,
151 pub type_arguments: Vec<Type>,
152 pub signature: LoadedFunctionInstantiation,
153 pub linkage: ExecutableLinkage,
154 pub instruction_length: CodeOffset,
155 pub definition_index: FunctionDefinitionIndex,
156 pub visibility: Visibility,
157 pub is_entry: bool,
158 pub is_native: bool,
159}
160
161#[derive(Debug)]
162pub struct MoveCall {
163 pub function: LoadedFunction,
164 pub arguments: Vec<Argument>,
165}
166
167pub use sui_types::transaction::Argument;
168
169impl ObjectArg {
174 pub fn id(&self) -> ObjectID {
175 self.kind.id()
176 }
177}
178
179impl ObjectArgKind {
180 pub fn id(&self) -> ObjectID {
181 match self {
182 Self::ImmObject(oref) | Self::OwnedObject(oref) => oref.0,
183 Self::ConsensusObject { id, .. } => *id,
184 }
185 }
186}
187
188impl Type {
189 pub fn abilities(&self) -> AbilitySet {
190 match self {
191 Type::Bool
192 | Type::U8
193 | Type::U16
194 | Type::U32
195 | Type::U64
196 | Type::U128
197 | Type::U256
198 | Type::Address => AbilitySet::PRIMITIVES,
199 Type::Signer => AbilitySet::SIGNER,
200 Type::Reference(_, _) => AbilitySet::REFERENCES,
201 Type::Vector(v) => v.abilities,
202 Type::Datatype(dt) => dt.abilities,
203 }
204 }
205
206 pub fn is_tx_context(&self) -> TxContextKind {
207 let (is_mut, inner) = match self {
208 Type::Reference(is_mut, inner) => (*is_mut, inner),
209 _ => return TxContextKind::None,
210 };
211 let Type::Datatype(dt) = &**inner else {
212 return TxContextKind::None;
213 };
214 if dt.qualified_ident() == RESOLVED_TX_CONTEXT {
215 if is_mut {
216 TxContextKind::Mutable
217 } else {
218 TxContextKind::Immutable
219 }
220 } else {
221 TxContextKind::None
222 }
223 }
224 pub fn all_addresses(&self) -> IndexSet<AccountAddress> {
225 match self {
226 Type::Bool
227 | Type::U8
228 | Type::U16
229 | Type::U32
230 | Type::U64
231 | Type::U128
232 | Type::U256
233 | Type::Address
234 | Type::Signer => IndexSet::new(),
235 Type::Vector(v) => v.element_type.all_addresses(),
236 Type::Reference(_, inner) => inner.all_addresses(),
237 Type::Datatype(dt) => dt.all_addresses(),
238 }
239 }
240
241 pub fn node_count(&self) -> u64 {
242 use Type::*;
243 let mut total = 0u64;
244 let mut stack = vec![self];
245
246 while let Some(ty) = stack.pop() {
247 total = total.saturating_add(1);
248 match ty {
249 Bool | U8 | U16 | U32 | U64 | U128 | U256 | Address | Signer => {}
250 Vector(v) => stack.push(&v.element_type),
251 Reference(_, inner) => stack.push(inner),
252 Datatype(dt) => {
253 stack.extend(&dt.type_arguments);
254 }
255 }
256 }
257
258 total
259 }
260
261 pub fn is_reference(&self) -> bool {
262 match self {
263 Type::Bool
264 | Type::U8
265 | Type::U16
266 | Type::U32
267 | Type::U64
268 | Type::U128
269 | Type::U256
270 | Type::Address
271 | Type::Signer
272 | Type::Vector(_)
273 | Type::Datatype(_) => false,
274 Type::Reference(_, _) => true,
275 }
276 }
277}
278
279impl Datatype {
280 pub fn qualified_ident(&self) -> (&AccountAddress, &IdentStr, &IdentStr) {
281 (
282 self.module.address(),
283 self.module.name(),
284 self.name.as_ident_str(),
285 )
286 }
287
288 pub fn all_addresses(&self) -> IndexSet<AccountAddress> {
289 let mut addresses = IndexSet::new();
290 addresses.insert(*self.module.address());
291 for arg in &self.type_arguments {
292 addresses.extend(arg.all_addresses());
293 }
294 addresses
295 }
296}
297
298impl Command {
299 pub fn arguments_mut(&mut self) -> Box<dyn Iterator<Item = &mut Argument> + '_> {
300 match self {
301 Command::MoveCall(mc) => Box::new(mc.arguments.iter_mut()),
302 Command::TransferObjects(objs, recipient) => {
303 Box::new(objs.iter_mut().chain(std::iter::once(recipient)))
304 }
305 Command::SplitCoins(coin, amounts) => {
306 Box::new(std::iter::once(coin).chain(amounts.iter_mut()))
307 }
308 Command::MergeCoins(coin, coins) => {
309 Box::new(std::iter::once(coin).chain(coins.iter_mut()))
310 }
311 Command::MakeMoveVec(_, elements) => Box::new(elements.iter_mut()),
312 Command::Publish(_, _, _) => Box::new(std::iter::empty()),
313 Command::Upgrade(_, _, _, obj, _) => Box::new(std::iter::once(obj)),
314 }
315 }
316
317 pub fn arguments(&self) -> Box<dyn Iterator<Item = &Argument> + '_> {
318 match self {
319 Command::MoveCall(mc) => Box::new(mc.arguments.iter()),
320 Command::TransferObjects(objs, recipient) => {
321 Box::new(objs.iter().chain(std::iter::once(recipient)))
322 }
323 Command::SplitCoins(coin, amounts) => {
324 Box::new(std::iter::once(coin).chain(amounts.iter()))
325 }
326 Command::MergeCoins(coin, coins) => Box::new(std::iter::once(coin).chain(coins.iter())),
327 Command::MakeMoveVec(_, elements) => Box::new(elements.iter()),
328 Command::Publish(_, _, _) => Box::new(std::iter::empty()),
329 Command::Upgrade(_, _, _, obj, _) => Box::new(std::iter::once(obj)),
330 }
331 }
332}
333
334impl TryFrom<Type> for TypeTag {
339 type Error = &'static str;
340 fn try_from(ty: Type) -> Result<Self, Self::Error> {
341 Ok(match ty {
342 Type::Bool => TypeTag::Bool,
343 Type::U8 => TypeTag::U8,
344 Type::U16 => TypeTag::U16,
345 Type::U32 => TypeTag::U32,
346 Type::U64 => TypeTag::U64,
347 Type::U128 => TypeTag::U128,
348 Type::U256 => TypeTag::U256,
349 Type::Address => TypeTag::Address,
350 Type::Signer => TypeTag::Signer,
351 Type::Vector(inner) => {
352 let Vector { element_type, .. } = &*inner;
353 TypeTag::Vector(Box::new(element_type.clone().try_into()?))
354 }
355 Type::Datatype(dt) => {
356 let dt: &Datatype = &dt;
357 TypeTag::Struct(Box::new(dt.try_into()?))
358 }
359 Type::Reference(_, _) => return Err("unexpected reference type"),
360 })
361 }
362}
363
364impl TryFrom<&Datatype> for StructTag {
365 type Error = &'static str;
366
367 fn try_from(dt: &Datatype) -> Result<Self, Self::Error> {
368 let Datatype {
369 module,
370 name,
371 type_arguments,
372 ..
373 } = dt;
374 Ok(StructTag {
375 address: *module.address(),
376 module: module.name().to_owned(),
377 name: name.to_owned(),
378 type_params: type_arguments
379 .iter()
380 .map(|t| t.clone().try_into())
381 .collect::<Result<Vec<TypeTag>, _>>()?,
382 })
383 }
384}
385
386#[test]
391fn enum_size() {
392 assert_eq!(std::mem::size_of::<Type>(), 16);
393}