sui_adapter_v0/
execution_value.rs

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