1use 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
30pub 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 pub object_metadata: Option<InputObjectMetadata>,
90 pub inner: ResultValue,
91}
92
93#[derive(Clone, Debug)]
94pub struct ResultValue {
95 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 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 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 Value::Receiving(_, _, _) => false,
236 }
237 }
238}
239
240impl ObjectValue {
241 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}