sui_adapter_latest/static_programmable_transactions/execution/
values.rs1use 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
36pub struct Local<'a>(&'a mut Locals, u16);
38
39pub 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 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 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 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 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 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 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 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 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 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 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
225impl Value {
230 pub fn id(address: AccountAddress) -> Self {
231 Self(VMValue::struct_(Struct::pack([VMValue::address(address)])))
233 }
234
235 pub fn uid(address: AccountAddress) -> Self {
236 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 Self(VMValue::struct_(Struct::pack([VMValue::u64(amount)])))
250 }
251
252 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 pub fn funds_accumulator_withdrawal(owner: AccountAddress, limit: U256) -> Self {
262 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 pub fn new_tx_context(digest: TransactionDigest) -> Result<Self, ExecutionError> {
284 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 Ok(Self(VMValue::struct_(Struct::pack([VMValue::bool(true)]))))
305 }
306}
307
308impl Value {
313 pub fn unpack_coin(self) -> Result<(ObjectID, u64), ExecutionError> {
314 let [id, balance] = unpack(self.0)?;
315 let [id] = unpack(id)?;
317 let [id] = unpack(id)?;
319 let id: AccountAddress = id.cast().map_err(iv("cast"))?;
320 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 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 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
381impl Value {
386 pub fn upgrade_cap(cap: UpgradeCap) -> Self {
387 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 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 let [cap, package, policy, digest] = unpack(self.0)?;
428 let [cap] = unpack(cap)?;
430 let cap: AccountAddress = cap.cast().map_err(iv("cast"))?;
431 let [package] = unpack(package)?;
433 let package: AccountAddress = package.cast().map_err(iv("cast"))?;
434 let policy: u8 = policy.cast().map_err(iv("cast"))?;
436 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}