sui_adapter_latest/static_programmable_transactions/execution/
values.rs

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