sui_adapter_latest/static_programmable_transactions/execution/
values.rs1use 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
39pub struct Local<'a>(&'a mut Locals, u16);
41
42pub 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 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 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 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 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 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 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 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 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 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 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
232impl Value {
237 pub fn id(address: AccountAddress) -> Self {
238 Self(VMValue::struct_(Struct::pack([VMValue::address(address)])))
240 }
241
242 pub fn uid(address: AccountAddress) -> Self {
243 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 Self(VMValue::struct_(Struct::pack([VMValue::u64(amount)])))
257 }
258
259 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 pub fn funds_accumulator_withdrawal(owner: AccountAddress, limit: U256) -> Self {
269 Self(VMValue::struct_(Struct::pack([
274 VMValue::address(owner),
275 VMValue::u256(limit),
276 ])))
277 }
278
279 pub fn vec_pack(ty: Type, values: Vec<Self>) -> Result<Self, ExecutionError> {
280 let specialization: VectorSpecialization = ty
281 .try_into()
282 .map_err(|e| make_invariant_violation!("Unable to specialize vector: {e}"))?;
283 let vec = values::Vector::pack(specialization, values.into_iter().map(|v| v.0))
284 .map_err(iv("pack"))?;
285 Ok(Self(vec))
286 }
287
288 pub fn new_tx_context(digest: TransactionDigest) -> Result<Self, ExecutionError> {
291 Ok(Self(VMValue::struct_(Struct::pack([
299 VMValue::address(AccountAddress::ZERO),
300 VMValue::vector_u8(digest.inner().iter().copied()),
301 VMValue::u64(0),
302 VMValue::u64(0),
303 VMValue::u64(0),
304 ]))))
305 }
306
307 pub fn one_time_witness() -> Result<Self, ExecutionError> {
308 Ok(Self(VMValue::struct_(Struct::pack([VMValue::bool(true)]))))
312 }
313}
314
315impl Value {
320 pub fn unpack_coin(self) -> Result<(ObjectID, u64), ExecutionError> {
321 let [id, balance] = unpack(self.0)?;
322 let [id] = unpack(id)?;
324 let [id] = unpack(id)?;
326 let id: AccountAddress = id.cast().map_err(iv("cast"))?;
327 let [balance] = unpack(balance)?;
329 let balance: u64 = balance.cast().map_err(iv("cast"))?;
330 Ok((ObjectID::from(id), balance))
331 }
332
333 pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
334 let balance_value_ref = borrow_coin_ref_balance_value(self.0)?;
335 let balance_value_ref: values::Reference = balance_value_ref.cast().map_err(iv("cast"))?;
336 let balance_value = balance_value_ref.read_ref().map_err(iv("read ref"))?;
337 balance_value.cast().map_err(iv("cast"))
338 }
339
340 pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
343 coin_ref_modify_balance(self.0, |balance| {
344 let Some(new_balance) = balance.checked_sub(amount) else {
345 invariant_violation!("coin balance {balance} is less than {amount}")
346 };
347 Ok(new_balance)
348 })
349 }
350
351 pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
354 coin_ref_modify_balance(self.0, |balance| {
355 let Some(new_balance) = balance.checked_add(amount) else {
356 invariant_violation!("coin balance {balance} + {amount} is greater than u64::MAX")
357 };
358 Ok(new_balance)
359 })
360 }
361}
362
363fn coin_ref_modify_balance(
364 coin_ref: VMValue,
365 modify: impl FnOnce(u64) -> Result<u64, ExecutionError>,
366) -> Result<(), ExecutionError> {
367 let balance_value_ref = borrow_coin_ref_balance_value(coin_ref)?;
368 let reference: values::Reference = balance_value_ref.copy_value().cast().map_err(iv("cast"))?;
369 let balance: u64 = reference
370 .read_ref()
371 .map_err(iv("read ref"))?
372 .cast()
373 .map_err(iv("cast"))?;
374 let new_balance = modify(balance)?;
375 let reference: values::Reference = balance_value_ref.cast().map_err(iv("cast"))?;
376 reference
377 .write_ref(VMValue::u64(new_balance))
378 .map_err(iv("write ref"))
379}
380
381fn borrow_coin_ref_balance_value(coin_ref: VMValue) -> Result<VMValue, ExecutionError> {
382 let coin_ref: values::StructRef = coin_ref.cast().map_err(iv("cast"))?;
383 let balance = coin_ref.borrow_field(1).map_err(iv("borrow field"))?;
384 let balance: values::StructRef = balance.cast().map_err(iv("cast"))?;
385 balance.borrow_field(0).map_err(iv("borrow field"))
386}
387
388impl Value {
393 pub fn upgrade_cap(cap: UpgradeCap) -> Self {
394 let UpgradeCap {
401 id,
402 package,
403 version,
404 policy,
405 } = cap;
406 Self(VMValue::struct_(Struct::pack([
407 Self::uid(id.id.bytes.into()).0,
408 Self::id(package.bytes.into()).0,
409 VMValue::u64(version),
410 VMValue::u8(policy),
411 ])))
412 }
413
414 pub fn upgrade_receipt(receipt: UpgradeReceipt) -> Self {
415 let UpgradeReceipt { cap, package } = receipt;
420 Self(VMValue::struct_(Struct::pack([
421 Self::id(cap.bytes.into()).0,
422 Self::id(package.bytes.into()).0,
423 ])))
424 }
425
426 pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
427 let [cap, package, policy, digest] = unpack(self.0)?;
435 let [cap] = unpack(cap)?;
437 let cap: AccountAddress = cap.cast().map_err(iv("cast"))?;
438 let [package] = unpack(package)?;
440 let package: AccountAddress = package.cast().map_err(iv("cast"))?;
441 let policy: u8 = policy.cast().map_err(iv("cast"))?;
443 let digest: Vec<u8> = digest.cast().map_err(iv("cast"))?;
445 Ok(UpgradeTicket {
446 cap: sui_types::id::ID::new(cap.into()),
447 package: sui_types::id::ID::new(package.into()),
448 policy,
449 digest,
450 })
451 }
452}
453
454fn unpack<const N: usize>(value: VMValue) -> Result<[VMValue; N], ExecutionError> {
455 let value: values::Struct = value.cast().map_err(iv("cast"))?;
456 let unpacked = value.unpack().collect::<Vec<_>>();
457 assert_invariant!(unpacked.len() == N, "Expected {N} fields, got {unpacked:?}");
458 Ok(unpacked.try_into().unwrap())
459}
460
461const fn iv(case: &str) -> impl FnOnce(PartialVMError) -> ExecutionError + use<'_> {
462 move |e| make_invariant_violation!("unexpected {case} failure {e:?}")
463}