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