Skip to main content

iota_adapter_latest/
execution_value.rs

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