Skip to main content

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