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