Skip to main content

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