sui_adapter_latest/static_programmable_transactions/execution/
values.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::BTreeMap;
5
6use crate::static_programmable_transactions::{env::Env, typing::ast::Type};
7use move_binary_format::errors::{PartialVMError, PartialVMResult};
8use move_core_types::account_address::AccountAddress;
9use move_core_types::runtime_value::MoveTypeLayout;
10use move_core_types::u256::U256;
11use move_vm_runtime::execution::interpreter::locals::{BaseHeap as VMBaseHeap, BaseHeapId};
12use move_vm_runtime::shared::views::ValueVisitor;
13use move_vm_runtime::{
14    execution::values::{self, Struct, VMValueCast, Value as VMValue, VectorSpecialization},
15    shared::views::ValueView,
16};
17use sui_types::{
18    base_types::{ObjectID, SequenceNumber},
19    digests::TransactionDigest,
20    error::ExecutionError,
21    move_package::{UpgradeCap, UpgradeReceipt, UpgradeTicket},
22};
23pub enum InputValue<'a> {
24    Bytes(&'a ByteValue),
25    Loaded(Local<'a>),
26}
27
28pub enum ByteValue {
29    Pure(Vec<u8>),
30    Receiving {
31        id: ObjectID,
32        version: SequenceNumber,
33    },
34}
35
36/// A memory location that can be borrowed or moved from
37pub struct Local<'a>(&'a mut Locals, u16);
38
39/// A set of memory locations that can be borrowed or moved from. Used for inputs and results
40pub struct Locals {
41    heap: VMBaseHeap,
42    locations: BTreeMap<u16, BaseHeapId>,
43}
44
45#[derive(Debug)]
46pub struct Value(VMValue);
47
48impl Locals {
49    pub fn new<Items>(values: Items) -> Result<Self, ExecutionError>
50    where
51        Items: IntoIterator<Item = Option<Value>>,
52        Items::IntoIter: ExactSizeIterator,
53    {
54        let values = values.into_iter();
55        let n = values.len();
56        assert_invariant!(n <= u16::MAX as usize, "Locals size exceeds u16::MAX");
57        // TODO(vm-rewrite): Look into not allocating invalid memory slots ahead of time. For now
58        // we do this for ease, but we should be able to optimize this further.
59        let mut heap = VMBaseHeap::new();
60        let mut locations = BTreeMap::new();
61        for (i, v) in values.enumerate() {
62            let alloc_idx = match v {
63                Some(v) => heap.allocate_value(v.0),
64                // If the value is None, we leave the local invalid
65                None => heap.allocate_value(VMValue::invalid()),
66            }
67            .map_err(iv("allocate local"))?;
68            locations.insert(checked_as!(i, u16)?, alloc_idx);
69        }
70        Ok(Self { heap, locations })
71    }
72
73    pub fn new_invalid(n: usize) -> Result<Self, ExecutionError> {
74        assert_invariant!(n <= u16::MAX as usize, "Locals size exceeds u16::MAX");
75        let mut heap = VMBaseHeap::new();
76        let mut locations = BTreeMap::new();
77        for i in 0..n {
78            let alloc_idx = heap
79                .allocate_value(VMValue::invalid())
80                .map_err(iv("allocate local"))?;
81            locations.insert(checked_as!(i, u16)?, alloc_idx);
82        }
83        Ok(Self { heap, locations })
84    }
85
86    pub fn local(&mut self, index: u16) -> Result<Local<'_>, ExecutionError> {
87        Ok(Local(self, index))
88    }
89}
90
91impl Local<'_> {
92    fn to_resolved_location(&self) -> Result<BaseHeapId, ExecutionError> {
93        self.0
94            .locations
95            .get(&self.1)
96            .copied()
97            .ok_or_else(|| make_invariant_violation!("local index {} out of bounds", self.1))
98    }
99
100    /// Does the local contain a value?
101    pub fn is_invalid(&self) -> Result<bool, ExecutionError> {
102        self.0
103            .heap
104            .is_invalid(self.to_resolved_location()?)
105            .map_err(iv("out of bounds"))
106    }
107
108    pub fn store(&mut self, value: Value) -> Result<(), ExecutionError> {
109        let val: values::Reference = self
110            .0
111            .heap
112            .borrow_loc(self.to_resolved_location()?)
113            .map_err(iv("store loc"))?
114            .cast()
115            .map_err(iv("cast to reference"))?;
116        val.write_ref(value.0).map_err(iv("store loc"))?;
117        Ok(())
118    }
119
120    /// Move the value out of the local
121    pub fn move_(&mut self) -> Result<Value, ExecutionError> {
122        assert_invariant!(!self.is_invalid()?, "cannot move invalid local");
123        self.0
124            .heap
125            .take_loc(self.to_resolved_location()?)
126            .map_err(iv("move loc"))
127            .map(Value)
128    }
129
130    /// Copy the value out in the local
131    pub fn copy(&self) -> Result<Value, ExecutionError> {
132        assert_invariant!(!self.is_invalid()?, "cannot copy invalid local");
133        let val: values::Reference = self
134            .0
135            .heap
136            .borrow_loc(self.to_resolved_location()?)
137            .map_err(iv("copy loc"))?
138            .cast()
139            .map_err(iv("cast to reference"))?;
140        val.read_ref().map_err(iv("copy loc")).map(Value)
141    }
142
143    /// Borrow the local, creating a reference to the value
144    pub fn borrow(&mut self) -> Result<Value, ExecutionError> {
145        assert_invariant!(!self.is_invalid()?, "cannot borrow invalid local");
146        self.0
147            .heap
148            .borrow_loc(self.to_resolved_location()?)
149            .map_err(iv("borrow loc"))
150            .map(Value)
151    }
152
153    pub fn move_if_valid(&mut self) -> Result<Option<Value>, ExecutionError> {
154        if self.is_invalid()? {
155            Ok(None)
156        } else {
157            Ok(Some(self.move_()?))
158        }
159    }
160}
161
162impl Value {
163    pub fn copy(&self) -> Result<Self, ExecutionError> {
164        Ok(Value(self.0.copy_value()))
165    }
166
167    /// Read the value, giving an invariant violation if the value is not a reference
168    pub fn read_ref(self) -> Result<Self, ExecutionError> {
169        let value: values::Reference = self.0.cast().map_err(iv("cast"))?;
170        Ok(Self(value.read_ref().map_err(iv("read ref"))?))
171    }
172
173    /// This function will invariant violation on an invalid cast
174    pub fn cast<V>(self) -> Result<V, ExecutionError>
175    where
176        VMValue: VMValueCast<V>,
177    {
178        self.0.cast().map_err(iv("cast"))
179    }
180
181    pub fn deserialize(env: &Env, bytes: &[u8], ty: Type) -> Result<Value, ExecutionError> {
182        let layout = env.runtime_layout(&ty)?;
183        let Some(value) = VMValue::simple_deserialize(bytes, &layout) else {
184            // we already checked the layout of pure bytes during typing
185            // and objects should already be valid
186            invariant_violation!("unable to deserialize value to type {ty:?}")
187        };
188        Ok(Value(value))
189    }
190
191    pub fn typed_serialize(&self, layout: &MoveTypeLayout) -> Option<Vec<u8>> {
192        self.0.typed_serialize(layout)
193    }
194
195    /// Used for getting access to the inner VMValue for tracing purposes.
196    pub(super) fn inner_for_tracing(&self) -> &VMValue {
197        &self.0
198    }
199}
200
201impl From<VMValue> for Value {
202    fn from(value: VMValue) -> Self {
203        Value(value)
204    }
205}
206
207impl From<Value> for VMValue {
208    fn from(value: Value) -> Self {
209        value.0
210    }
211}
212
213impl VMValueCast<Value> for VMValue {
214    fn cast(self) -> Result<Value, PartialVMError> {
215        Ok(self.into())
216    }
217}
218
219impl ValueView for Value {
220    fn visit(&self, visitor: &mut impl ValueVisitor) -> PartialVMResult<()> {
221        self.0.visit(visitor)
222    }
223}
224
225//**************************************************************************************************
226// Value Construction
227//**************************************************************************************************
228
229impl Value {
230    pub fn id(address: AccountAddress) -> Self {
231        // ID { address }
232        Self(VMValue::struct_(Struct::pack([VMValue::address(address)])))
233    }
234
235    pub fn uid(address: AccountAddress) -> Self {
236        // UID { ID { address } }
237        Self(VMValue::struct_(Struct::pack([Self::id(address).0])))
238    }
239
240    pub fn receiving(id: ObjectID, version: SequenceNumber) -> Self {
241        Self(VMValue::struct_(Struct::pack([
242            Self::id(id.into()).0,
243            VMValue::u64(version.into()),
244        ])))
245    }
246
247    pub fn balance(amount: u64) -> Self {
248        // Balance { amount }
249        Self(VMValue::struct_(Struct::pack([VMValue::u64(amount)])))
250    }
251
252    /// The uid _must_ be registered by the object runtime before being called
253    pub fn coin(id: ObjectID, amount: u64) -> Self {
254        Self(VMValue::struct_(Struct::pack([
255            Self::uid(id.into()).0,
256            Self::balance(amount).0,
257        ])))
258    }
259
260    /// Constructs a `sui::funds_accumulator::Withdrawal` value
261    pub fn funds_accumulator_withdrawal(owner: AccountAddress, limit: U256) -> Self {
262        // public struct Withdrawal has drop {
263        //     owner: address,
264        //     limit: u256,
265        // }
266        Self(VMValue::struct_(Struct::pack([
267            VMValue::address(owner),
268            VMValue::u256(limit),
269        ])))
270    }
271
272    pub fn vec_pack(ty: Type, values: Vec<Self>) -> Result<Self, ExecutionError> {
273        let specialization: VectorSpecialization = ty
274            .try_into()
275            .map_err(|e| make_invariant_violation!("Unable to specialize vector: {e}"))?;
276        let vec = values::Vector::pack(specialization, values.into_iter().map(|v| v.0))
277            .map_err(iv("pack"))?;
278        Ok(Self(vec))
279    }
280
281    /// Should be called once at the start of a transaction to populate the location with the
282    /// transaction context.
283    pub fn new_tx_context(digest: TransactionDigest) -> Result<Self, ExecutionError> {
284        // public struct TxContext has drop {
285        //     sender: address,
286        //     tx_hash: vector<u8>,
287        //     epoch: u64,
288        //     epoch_timestamp_ms: u64,
289        //     ids_created: u64,
290        // }
291        Ok(Self(VMValue::struct_(Struct::pack([
292            VMValue::address(AccountAddress::ZERO),
293            VMValue::vector_u8(digest.inner().iter().copied()),
294            VMValue::u64(0),
295            VMValue::u64(0),
296            VMValue::u64(0),
297        ]))))
298    }
299
300    pub fn one_time_witness() -> Result<Self, ExecutionError> {
301        // public struct <ONE_TIME_WITNESS> has drop{
302        //     _dummy: bool,
303        // }
304        Ok(Self(VMValue::struct_(Struct::pack([VMValue::bool(true)]))))
305    }
306}
307
308//**************************************************************************************************
309// Coin Functions
310//**************************************************************************************************
311
312impl Value {
313    pub fn unpack_coin(self) -> Result<(ObjectID, u64), ExecutionError> {
314        let [id, balance] = unpack(self.0)?;
315        // unpack UID
316        let [id] = unpack(id)?;
317        // unpack ID
318        let [id] = unpack(id)?;
319        let id: AccountAddress = id.cast().map_err(iv("cast"))?;
320        // unpack Balance
321        let [balance] = unpack(balance)?;
322        let balance: u64 = balance.cast().map_err(iv("cast"))?;
323        Ok((ObjectID::from(id), balance))
324    }
325
326    pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
327        let balance_value_ref = borrow_coin_ref_balance_value(self.0)?;
328        let balance_value_ref: values::Reference = balance_value_ref.cast().map_err(iv("cast"))?;
329        let balance_value = balance_value_ref.read_ref().map_err(iv("read ref"))?;
330        balance_value.cast().map_err(iv("cast"))
331    }
332
333    /// The coin value MUST be checked before calling this function, if `amount` is greater than
334    /// the value of the coin, it will return an invariant violation.
335    pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
336        coin_ref_modify_balance(self.0, |balance| {
337            let Some(new_balance) = balance.checked_sub(amount) else {
338                invariant_violation!("coin balance {balance} is less than {amount}")
339            };
340            Ok(new_balance)
341        })
342    }
343
344    /// The coin max value MUST be checked before calling this function, if `amount` plus the current
345    /// balance is greater than `u64::MAX`, it will return an invariant violation.
346    pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
347        coin_ref_modify_balance(self.0, |balance| {
348            let Some(new_balance) = balance.checked_add(amount) else {
349                invariant_violation!("coin balance {balance} + {amount} is greater than u64::MAX")
350            };
351            Ok(new_balance)
352        })
353    }
354}
355
356fn coin_ref_modify_balance(
357    coin_ref: VMValue,
358    modify: impl FnOnce(u64) -> Result<u64, ExecutionError>,
359) -> Result<(), ExecutionError> {
360    let balance_value_ref = borrow_coin_ref_balance_value(coin_ref)?;
361    let reference: values::Reference = balance_value_ref.copy_value().cast().map_err(iv("cast"))?;
362    let balance: u64 = reference
363        .read_ref()
364        .map_err(iv("read ref"))?
365        .cast()
366        .map_err(iv("cast"))?;
367    let new_balance = modify(balance)?;
368    let reference: values::Reference = balance_value_ref.cast().map_err(iv("cast"))?;
369    reference
370        .write_ref(VMValue::u64(new_balance))
371        .map_err(iv("write ref"))
372}
373
374fn borrow_coin_ref_balance_value(coin_ref: VMValue) -> Result<VMValue, ExecutionError> {
375    let coin_ref: values::StructRef = coin_ref.cast().map_err(iv("cast"))?;
376    let balance = coin_ref.borrow_field(1).map_err(iv("borrow field"))?;
377    let balance: values::StructRef = balance.cast().map_err(iv("cast"))?;
378    balance.borrow_field(0).map_err(iv("borrow field"))
379}
380
381//**************************************************************************************************
382// Upgrades
383//**************************************************************************************************
384
385impl Value {
386    pub fn upgrade_cap(cap: UpgradeCap) -> Self {
387        // public struct UpgradeCap has key, store {
388        //     id: UID,
389        //     package: ID,
390        //     version: u64,
391        //     policy: u8,
392        // }
393        let UpgradeCap {
394            id,
395            package,
396            version,
397            policy,
398        } = cap;
399        Self(VMValue::struct_(Struct::pack([
400            Self::uid(id.id.bytes.into()).0,
401            Self::id(package.bytes.into()).0,
402            VMValue::u64(version),
403            VMValue::u8(policy),
404        ])))
405    }
406
407    pub fn upgrade_receipt(receipt: UpgradeReceipt) -> Self {
408        // public struct UpgradeReceipt {
409        //     cap: ID,
410        //     package: ID,
411        // }
412        let UpgradeReceipt { cap, package } = receipt;
413        Self(VMValue::struct_(Struct::pack([
414            Self::id(cap.bytes.into()).0,
415            Self::id(package.bytes.into()).0,
416        ])))
417    }
418
419    pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
420        //  public struct UpgradeTicket {
421        //     cap: ID,
422        //     package: ID,
423        //     policy: u8,
424        //     digest: vector<u8>,
425        // }
426        // unpack UpgradeTicket
427        let [cap, package, policy, digest] = unpack(self.0)?;
428        // unpack cap ID
429        let [cap] = unpack(cap)?;
430        let cap: AccountAddress = cap.cast().map_err(iv("cast"))?;
431        // unpack package ID
432        let [package] = unpack(package)?;
433        let package: AccountAddress = package.cast().map_err(iv("cast"))?;
434        // unpack policy
435        let policy: u8 = policy.cast().map_err(iv("cast"))?;
436        // unpack digest
437        let digest: Vec<u8> = digest.cast().map_err(iv("cast"))?;
438        Ok(UpgradeTicket {
439            cap: sui_types::id::ID::new(cap.into()),
440            package: sui_types::id::ID::new(package.into()),
441            policy,
442            digest,
443        })
444    }
445}
446
447fn unpack<const N: usize>(value: VMValue) -> Result<[VMValue; N], ExecutionError> {
448    let value: values::Struct = value.cast().map_err(iv("cast"))?;
449    let unpacked = value.unpack().collect::<Vec<_>>();
450    assert_invariant!(unpacked.len() == N, "Expected {N} fields, got {unpacked:?}");
451    Ok(unpacked.try_into().unwrap())
452}
453
454const fn iv(case: &str) -> impl FnOnce(PartialVMError) -> ExecutionError + use<'_> {
455    move |e| make_invariant_violation!("unexpected {case} failure {e:?}")
456}