sui_adapter_latest/
execution_value.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::BTreeSet;
5
6use move_binary_format::file_format::AbilitySet;
7use move_core_types::u256::U256;
8use move_vm_types::loaded_data::runtime_types::Type;
9use serde::Deserialize;
10use sui_types::{
11    base_types::{ObjectID, SequenceNumber, SuiAddress},
12    coin::Coin,
13    error::{ExecutionError, ExecutionErrorKind},
14    execution_status::CommandArgumentError,
15    funds_accumulator::Withdrawal,
16    object::Owner,
17    storage::{BackingPackageStore, ChildObjectResolver, StorageView},
18    transfer::Receiving,
19};
20
21pub trait SuiResolver: BackingPackageStore {
22    fn as_backing_package_store(&self) -> &dyn BackingPackageStore;
23}
24
25impl<T> SuiResolver for T
26where
27    T: BackingPackageStore,
28{
29    fn as_backing_package_store(&self) -> &dyn BackingPackageStore {
30        self
31    }
32}
33
34/// Interface with the store necessary to execute a programmable transaction
35pub trait ExecutionState: StorageView + SuiResolver {
36    fn as_sui_resolver(&self) -> &dyn SuiResolver;
37    fn as_child_resolver(&self) -> &dyn ChildObjectResolver;
38}
39
40impl<T> ExecutionState for T
41where
42    T: StorageView,
43    T: SuiResolver,
44{
45    fn as_sui_resolver(&self) -> &dyn SuiResolver {
46        self
47    }
48
49    fn as_child_resolver(&self) -> &dyn ChildObjectResolver {
50        self
51    }
52}
53
54#[derive(Clone, Debug)]
55pub enum Mutability {
56    Mutable,
57    Immutable,
58    NonExclusiveWrite,
59}
60
61#[derive(Clone, Debug)]
62pub enum InputObjectMetadata {
63    Receiving {
64        id: ObjectID,
65        version: SequenceNumber,
66    },
67    InputObject {
68        id: ObjectID,
69        mutability: Mutability,
70        owner: Owner,
71        version: SequenceNumber,
72    },
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum UsageKind {
77    BorrowImm,
78    BorrowMut,
79    ByValue,
80}
81
82#[derive(Clone, Copy)]
83pub enum CommandKind {
84    MoveCall,
85    MakeMoveVec,
86    TransferObjects,
87    SplitCoins,
88    MergeCoins,
89    Publish,
90    Upgrade,
91}
92
93#[derive(Clone, Debug)]
94pub struct InputValue {
95    /// Used to remember the object ID and owner even if the value is taken
96    pub object_metadata: Option<InputObjectMetadata>,
97    pub inner: ResultValue,
98}
99
100#[derive(Clone, Debug)]
101pub struct ResultValue {
102    /// This is used primarily for values that have `copy` but not `drop` as they must have been
103    /// copied after the last borrow, otherwise we cannot consider the last "copy" to be instead
104    /// a "move" of the value.
105    pub last_usage_kind: Option<UsageKind>,
106    pub value: Option<Value>,
107    pub shared_object_ids: BTreeSet<ObjectID>,
108}
109
110#[derive(Debug, Clone)]
111pub enum Value {
112    Object(ObjectValue),
113    Raw(RawValueType, Vec<u8>),
114    Receiving(ObjectID, SequenceNumber, Option<Type>),
115}
116
117#[derive(Debug, Clone)]
118pub struct ObjectValue {
119    pub type_: Type,
120    pub has_public_transfer: bool,
121    // true if it has been used in a public, non-entry Move call
122    // In other words, false if all usages have been with non-Move commands or
123    // entry Move functions
124    pub used_in_non_entry_move_call: bool,
125    pub contents: ObjectContents,
126}
127
128#[derive(Debug, Copy, Clone)]
129pub enum SizeBound {
130    Object(u64),
131    VectorElem(u64),
132    Raw(u64),
133}
134
135#[derive(Debug, Clone)]
136pub enum ObjectContents {
137    Coin(Coin),
138    Raw(Vec<u8>),
139}
140
141#[derive(Debug, Clone)]
142pub enum RawValueType {
143    Any,
144    Loaded {
145        ty: Type,
146        abilities: AbilitySet,
147        used_in_non_entry_move_call: bool,
148    },
149}
150
151impl InputObjectMetadata {
152    pub fn id(&self) -> ObjectID {
153        match self {
154            InputObjectMetadata::Receiving { id, .. } => *id,
155            InputObjectMetadata::InputObject { id, .. } => *id,
156        }
157    }
158
159    pub fn version(&self) -> SequenceNumber {
160        match self {
161            InputObjectMetadata::Receiving { version, .. } => *version,
162            InputObjectMetadata::InputObject { version, .. } => *version,
163        }
164    }
165}
166
167impl InputValue {
168    pub fn new_object(object_metadata: InputObjectMetadata, value: ObjectValue) -> Self {
169        let mut inner = ResultValue::new(Value::Object(value));
170        if let InputObjectMetadata::InputObject {
171            id,
172            owner: Owner::Shared { .. },
173            ..
174        } = &object_metadata
175        {
176            inner.shared_object_ids.insert(*id);
177        }
178        InputValue {
179            object_metadata: Some(object_metadata),
180            inner,
181        }
182    }
183
184    pub fn new_raw(ty: RawValueType, value: Vec<u8>) -> Self {
185        InputValue {
186            object_metadata: None,
187            inner: ResultValue::new(Value::Raw(ty, value)),
188        }
189    }
190
191    pub fn new_receiving_object(id: ObjectID, version: SequenceNumber) -> Self {
192        InputValue {
193            object_metadata: Some(InputObjectMetadata::Receiving { id, version }),
194            inner: ResultValue::new(Value::Receiving(id, version, None)),
195        }
196    }
197
198    pub fn withdrawal(withdrawal_ty: RawValueType, owner: SuiAddress, limit: U256) -> Self {
199        let value = Value::Raw(
200            withdrawal_ty,
201            bcs::to_bytes(&Withdrawal::new(owner, limit)).unwrap(),
202        );
203        InputValue {
204            object_metadata: None,
205            inner: ResultValue {
206                last_usage_kind: None,
207                value: Some(value),
208                shared_object_ids: BTreeSet::new(),
209            },
210        }
211    }
212}
213
214impl ResultValue {
215    pub fn new(value: Value) -> Self {
216        Self {
217            last_usage_kind: None,
218            value: Some(value),
219            shared_object_ids: BTreeSet::new(),
220        }
221    }
222}
223
224impl Value {
225    pub fn is_copyable(&self) -> bool {
226        match self {
227            Value::Object(_) => false,
228            Value::Raw(RawValueType::Any, _) => true,
229            Value::Raw(RawValueType::Loaded { abilities, .. }, _) => abilities.has_copy(),
230            Value::Receiving(_, _, _) => false,
231        }
232    }
233
234    pub fn write_bcs_bytes(
235        &self,
236        buf: &mut Vec<u8>,
237        bound: Option<SizeBound>,
238    ) -> Result<(), ExecutionError> {
239        match self {
240            Value::Object(obj_value) => obj_value.write_bcs_bytes(buf, bound)?,
241            Value::Raw(_, bytes) => buf.extend(bytes),
242            Value::Receiving(id, version, _) => {
243                buf.extend(Receiving::new(*id, *version).to_bcs_bytes())
244            }
245        }
246        if let Some(bound) = bound {
247            ensure_serialized_size(buf.len() as u64, bound)?;
248        }
249
250        Ok(())
251    }
252
253    pub fn was_used_in_non_entry_move_call(&self) -> bool {
254        match self {
255            Value::Object(obj) => obj.used_in_non_entry_move_call,
256            // Any is only used for Pure inputs, and if it was used by &mut it would have switched
257            // to Loaded
258            Value::Raw(RawValueType::Any, _) => false,
259            Value::Raw(
260                RawValueType::Loaded {
261                    used_in_non_entry_move_call,
262                    ..
263                },
264                _,
265            ) => *used_in_non_entry_move_call,
266            // Only thing you can do with a `Receiving<T>` is consume it, so once it's used it
267            // can't be used again.
268            Value::Receiving(_, _, _) => false,
269        }
270    }
271}
272
273impl ObjectValue {
274    /// # Safety
275    /// We must have the Type is the coin type, but we are unable to check it at this spot
276    pub unsafe fn coin(type_: Type, coin: Coin) -> Self {
277        Self {
278            type_,
279            has_public_transfer: true,
280            used_in_non_entry_move_call: false,
281            contents: ObjectContents::Coin(coin),
282        }
283    }
284
285    pub fn ensure_public_transfer_eligible(&self) -> Result<(), ExecutionError> {
286        if !self.has_public_transfer {
287            return Err(ExecutionErrorKind::InvalidTransferObject.into());
288        }
289        Ok(())
290    }
291
292    pub fn write_bcs_bytes(
293        &self,
294        buf: &mut Vec<u8>,
295        bound: Option<SizeBound>,
296    ) -> Result<(), ExecutionError> {
297        match &self.contents {
298            ObjectContents::Raw(bytes) => buf.extend(bytes),
299            ObjectContents::Coin(coin) => buf.extend(coin.to_bcs_bytes()),
300        }
301        if let Some(bound) = bound {
302            ensure_serialized_size(buf.len() as u64, bound)?;
303        }
304        Ok(())
305    }
306}
307
308pub fn ensure_serialized_size(size: u64, bound: SizeBound) -> Result<(), ExecutionError> {
309    let bound_size = match bound {
310        SizeBound::Object(bound_size)
311        | SizeBound::VectorElem(bound_size)
312        | SizeBound::Raw(bound_size) => bound_size,
313    };
314    if size > bound_size {
315        let e = match bound {
316            SizeBound::Object(_) => ExecutionErrorKind::MoveObjectTooBig {
317                object_size: size,
318                max_object_size: bound_size,
319            },
320            SizeBound::VectorElem(_) => ExecutionErrorKind::MoveVectorElemTooBig {
321                value_size: size,
322                max_scaled_size: bound_size,
323            },
324            SizeBound::Raw(_) => ExecutionErrorKind::MoveRawValueTooBig {
325                value_size: size,
326                max_scaled_size: bound_size,
327            },
328        };
329        let msg = "Serialized bytes of value too large".to_owned();
330        return Err(ExecutionError::new_with_source(e, msg));
331    }
332    Ok(())
333}
334
335pub trait TryFromValue: Sized {
336    fn try_from_value(value: Value) -> Result<Self, CommandArgumentError>;
337}
338
339impl TryFromValue for Value {
340    fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
341        Ok(value)
342    }
343}
344
345impl TryFromValue for ObjectValue {
346    fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
347        match value {
348            Value::Object(o) => Ok(o),
349            Value::Raw(RawValueType::Any, _) => Err(CommandArgumentError::TypeMismatch),
350            Value::Raw(RawValueType::Loaded { .. }, _) => Err(CommandArgumentError::TypeMismatch),
351            Value::Receiving(_, _, _) => Err(CommandArgumentError::TypeMismatch),
352        }
353    }
354}
355
356impl TryFromValue for SuiAddress {
357    fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
358        try_from_value_prim(&value, Type::Address)
359    }
360}
361
362impl TryFromValue for u64 {
363    fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
364        try_from_value_prim(&value, Type::U64)
365    }
366}
367
368fn try_from_value_prim<'a, T: Deserialize<'a>>(
369    value: &'a Value,
370    expected_ty: Type,
371) -> Result<T, CommandArgumentError> {
372    match value {
373        Value::Object(_) => Err(CommandArgumentError::TypeMismatch),
374        Value::Receiving(_, _, _) => Err(CommandArgumentError::TypeMismatch),
375        Value::Raw(RawValueType::Any, bytes) => {
376            bcs::from_bytes(bytes).map_err(|_| CommandArgumentError::InvalidBCSBytes)
377        }
378        Value::Raw(RawValueType::Loaded { ty, .. }, bytes) => {
379            if ty != &expected_ty {
380                return Err(CommandArgumentError::TypeMismatch);
381            }
382            bcs::from_bytes(bytes).map_err(|_| CommandArgumentError::InvalidBCSBytes)
383        }
384    }
385}