1use std::collections::BTreeSet;
5
6use move_binary_format::file_format::AbilitySet;
7use move_core_types::u256::U256;
8use move_vm_types::loaded_data::runtime_types::Type;
9use serde::Deserialize;
10use sui_types::{
11 base_types::{ObjectID, SequenceNumber, SuiAddress},
12 coin::Coin,
13 error::{ExecutionError, ExecutionErrorKind},
14 execution_status::CommandArgumentError,
15 funds_accumulator::Withdrawal,
16 object::Owner,
17 storage::{BackingPackageStore, ChildObjectResolver, StorageView},
18 transfer::Receiving,
19};
20
21pub trait SuiResolver: BackingPackageStore {
22 fn as_backing_package_store(&self) -> &dyn BackingPackageStore;
23}
24
25impl<T> SuiResolver for T
26where
27 T: BackingPackageStore,
28{
29 fn as_backing_package_store(&self) -> &dyn BackingPackageStore {
30 self
31 }
32}
33
34pub trait ExecutionState: StorageView + SuiResolver {
36 fn as_sui_resolver(&self) -> &dyn SuiResolver;
37 fn as_child_resolver(&self) -> &dyn ChildObjectResolver;
38}
39
40impl<T> ExecutionState for T
41where
42 T: StorageView,
43 T: SuiResolver,
44{
45 fn as_sui_resolver(&self) -> &dyn SuiResolver {
46 self
47 }
48
49 fn as_child_resolver(&self) -> &dyn ChildObjectResolver {
50 self
51 }
52}
53
54#[derive(Clone, Debug)]
55pub enum Mutability {
56 Mutable,
57 Immutable,
58 NonExclusiveWrite,
59}
60
61#[derive(Clone, Debug)]
62pub enum InputObjectMetadata {
63 Receiving {
64 id: ObjectID,
65 version: SequenceNumber,
66 },
67 InputObject {
68 id: ObjectID,
69 mutability: Mutability,
70 owner: Owner,
71 version: SequenceNumber,
72 },
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum UsageKind {
77 BorrowImm,
78 BorrowMut,
79 ByValue,
80}
81
82#[derive(Clone, Copy)]
83pub enum CommandKind {
84 MoveCall,
85 MakeMoveVec,
86 TransferObjects,
87 SplitCoins,
88 MergeCoins,
89 Publish,
90 Upgrade,
91}
92
93#[derive(Clone, Debug)]
94pub struct InputValue {
95 pub object_metadata: Option<InputObjectMetadata>,
97 pub inner: ResultValue,
98}
99
100#[derive(Clone, Debug)]
101pub struct ResultValue {
102 pub last_usage_kind: Option<UsageKind>,
106 pub value: Option<Value>,
107 pub shared_object_ids: BTreeSet<ObjectID>,
108}
109
110#[derive(Debug, Clone)]
111pub enum Value {
112 Object(ObjectValue),
113 Raw(RawValueType, Vec<u8>),
114 Receiving(ObjectID, SequenceNumber, Option<Type>),
115}
116
117#[derive(Debug, Clone)]
118pub struct ObjectValue {
119 pub type_: Type,
120 pub has_public_transfer: bool,
121 pub used_in_non_entry_move_call: bool,
125 pub contents: ObjectContents,
126}
127
128#[derive(Debug, Copy, Clone)]
129pub enum SizeBound {
130 Object(u64),
131 VectorElem(u64),
132 Raw(u64),
133}
134
135#[derive(Debug, Clone)]
136pub enum ObjectContents {
137 Coin(Coin),
138 Raw(Vec<u8>),
139}
140
141#[derive(Debug, Clone)]
142pub enum RawValueType {
143 Any,
144 Loaded {
145 ty: Type,
146 abilities: AbilitySet,
147 used_in_non_entry_move_call: bool,
148 },
149}
150
151impl InputObjectMetadata {
152 pub fn id(&self) -> ObjectID {
153 match self {
154 InputObjectMetadata::Receiving { id, .. } => *id,
155 InputObjectMetadata::InputObject { id, .. } => *id,
156 }
157 }
158
159 pub fn version(&self) -> SequenceNumber {
160 match self {
161 InputObjectMetadata::Receiving { version, .. } => *version,
162 InputObjectMetadata::InputObject { version, .. } => *version,
163 }
164 }
165}
166
167impl InputValue {
168 pub fn new_object(object_metadata: InputObjectMetadata, value: ObjectValue) -> Self {
169 let mut inner = ResultValue::new(Value::Object(value));
170 if let InputObjectMetadata::InputObject {
171 id,
172 owner: Owner::Shared { .. },
173 ..
174 } = &object_metadata
175 {
176 inner.shared_object_ids.insert(*id);
177 }
178 InputValue {
179 object_metadata: Some(object_metadata),
180 inner,
181 }
182 }
183
184 pub fn new_raw(ty: RawValueType, value: Vec<u8>) -> Self {
185 InputValue {
186 object_metadata: None,
187 inner: ResultValue::new(Value::Raw(ty, value)),
188 }
189 }
190
191 pub fn new_receiving_object(id: ObjectID, version: SequenceNumber) -> Self {
192 InputValue {
193 object_metadata: Some(InputObjectMetadata::Receiving { id, version }),
194 inner: ResultValue::new(Value::Receiving(id, version, None)),
195 }
196 }
197
198 pub fn withdrawal(withdrawal_ty: RawValueType, owner: SuiAddress, limit: U256) -> Self {
199 let value = Value::Raw(
200 withdrawal_ty,
201 bcs::to_bytes(&Withdrawal::new(owner, limit)).unwrap(),
202 );
203 InputValue {
204 object_metadata: None,
205 inner: ResultValue {
206 last_usage_kind: None,
207 value: Some(value),
208 shared_object_ids: BTreeSet::new(),
209 },
210 }
211 }
212}
213
214impl ResultValue {
215 pub fn new(value: Value) -> Self {
216 Self {
217 last_usage_kind: None,
218 value: Some(value),
219 shared_object_ids: BTreeSet::new(),
220 }
221 }
222}
223
224impl Value {
225 pub fn is_copyable(&self) -> bool {
226 match self {
227 Value::Object(_) => false,
228 Value::Raw(RawValueType::Any, _) => true,
229 Value::Raw(RawValueType::Loaded { abilities, .. }, _) => abilities.has_copy(),
230 Value::Receiving(_, _, _) => false,
231 }
232 }
233
234 pub fn write_bcs_bytes(
235 &self,
236 buf: &mut Vec<u8>,
237 bound: Option<SizeBound>,
238 ) -> Result<(), ExecutionError> {
239 match self {
240 Value::Object(obj_value) => obj_value.write_bcs_bytes(buf, bound)?,
241 Value::Raw(_, bytes) => buf.extend(bytes),
242 Value::Receiving(id, version, _) => {
243 buf.extend(Receiving::new(*id, *version).to_bcs_bytes())
244 }
245 }
246 if let Some(bound) = bound {
247 ensure_serialized_size(buf.len() as u64, bound)?;
248 }
249
250 Ok(())
251 }
252
253 pub fn was_used_in_non_entry_move_call(&self) -> bool {
254 match self {
255 Value::Object(obj) => obj.used_in_non_entry_move_call,
256 Value::Raw(RawValueType::Any, _) => false,
259 Value::Raw(
260 RawValueType::Loaded {
261 used_in_non_entry_move_call,
262 ..
263 },
264 _,
265 ) => *used_in_non_entry_move_call,
266 Value::Receiving(_, _, _) => false,
269 }
270 }
271}
272
273impl ObjectValue {
274 pub unsafe fn coin(type_: Type, coin: Coin) -> Self {
277 Self {
278 type_,
279 has_public_transfer: true,
280 used_in_non_entry_move_call: false,
281 contents: ObjectContents::Coin(coin),
282 }
283 }
284
285 pub fn ensure_public_transfer_eligible(&self) -> Result<(), ExecutionError> {
286 if !self.has_public_transfer {
287 return Err(ExecutionErrorKind::InvalidTransferObject.into());
288 }
289 Ok(())
290 }
291
292 pub fn write_bcs_bytes(
293 &self,
294 buf: &mut Vec<u8>,
295 bound: Option<SizeBound>,
296 ) -> Result<(), ExecutionError> {
297 match &self.contents {
298 ObjectContents::Raw(bytes) => buf.extend(bytes),
299 ObjectContents::Coin(coin) => buf.extend(coin.to_bcs_bytes()),
300 }
301 if let Some(bound) = bound {
302 ensure_serialized_size(buf.len() as u64, bound)?;
303 }
304 Ok(())
305 }
306}
307
308pub fn ensure_serialized_size(size: u64, bound: SizeBound) -> Result<(), ExecutionError> {
309 let bound_size = match bound {
310 SizeBound::Object(bound_size)
311 | SizeBound::VectorElem(bound_size)
312 | SizeBound::Raw(bound_size) => bound_size,
313 };
314 if size > bound_size {
315 let e = match bound {
316 SizeBound::Object(_) => ExecutionErrorKind::MoveObjectTooBig {
317 object_size: size,
318 max_object_size: bound_size,
319 },
320 SizeBound::VectorElem(_) => ExecutionErrorKind::MoveVectorElemTooBig {
321 value_size: size,
322 max_scaled_size: bound_size,
323 },
324 SizeBound::Raw(_) => ExecutionErrorKind::MoveRawValueTooBig {
325 value_size: size,
326 max_scaled_size: bound_size,
327 },
328 };
329 let msg = "Serialized bytes of value too large".to_owned();
330 return Err(ExecutionError::new_with_source(e, msg));
331 }
332 Ok(())
333}
334
335pub trait TryFromValue: Sized {
336 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError>;
337}
338
339impl TryFromValue for Value {
340 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
341 Ok(value)
342 }
343}
344
345impl TryFromValue for ObjectValue {
346 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
347 match value {
348 Value::Object(o) => Ok(o),
349 Value::Raw(RawValueType::Any, _) => Err(CommandArgumentError::TypeMismatch),
350 Value::Raw(RawValueType::Loaded { .. }, _) => Err(CommandArgumentError::TypeMismatch),
351 Value::Receiving(_, _, _) => Err(CommandArgumentError::TypeMismatch),
352 }
353 }
354}
355
356impl TryFromValue for SuiAddress {
357 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
358 try_from_value_prim(&value, Type::Address)
359 }
360}
361
362impl TryFromValue for u64 {
363 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
364 try_from_value_prim(&value, Type::U64)
365 }
366}
367
368fn try_from_value_prim<'a, T: Deserialize<'a>>(
369 value: &'a Value,
370 expected_ty: Type,
371) -> Result<T, CommandArgumentError> {
372 match value {
373 Value::Object(_) => Err(CommandArgumentError::TypeMismatch),
374 Value::Receiving(_, _, _) => Err(CommandArgumentError::TypeMismatch),
375 Value::Raw(RawValueType::Any, bytes) => {
376 bcs::from_bytes(bytes).map_err(|_| CommandArgumentError::InvalidBCSBytes)
377 }
378 Value::Raw(RawValueType::Loaded { ty, .. }, bytes) => {
379 if ty != &expected_ty {
380 return Err(CommandArgumentError::TypeMismatch);
381 }
382 bcs::from_bytes(bytes).map_err(|_| CommandArgumentError::InvalidBCSBytes)
383 }
384 }
385}