1use super::object_runtime::{ObjectRuntime, TransferResult};
5use crate::{
6 get_receiver_object_id, get_tag_and_layouts, object_runtime::object_store::ObjectResult,
7 NativesCostTable,
8};
9use move_binary_format::errors::{PartialVMError, PartialVMResult};
10use move_core_types::{
11 account_address::AccountAddress, gas_algebra::InternalGas, language_storage::TypeTag,
12 vm_status::StatusCode,
13};
14use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
15use move_vm_types::{
16 loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value,
17};
18use smallvec::smallvec;
19use std::collections::VecDeque;
20use sui_types::{
21 base_types::{MoveObjectType, ObjectID, SequenceNumber},
22 object::Owner,
23};
24
25const E_SHARED_NON_NEW_OBJECT: u64 = 0;
26const E_BCS_SERIALIZATION_FAILURE: u64 = 1;
27const E_RECEIVING_OBJECT_TYPE_MISMATCH: u64 = 2;
28const E_UNABLE_TO_RECEIVE_OBJECT: u64 = 3;
31
32#[derive(Clone, Debug)]
33pub struct TransferReceiveObjectInternalCostParams {
34 pub transfer_receive_object_internal_cost_base: InternalGas,
35}
36pub fn receive_object_internal(
43 context: &mut NativeContext,
44 mut ty_args: Vec<Type>,
45 mut args: VecDeque<Value>,
46) -> PartialVMResult<NativeResult> {
47 debug_assert!(ty_args.len() == 1);
48 debug_assert!(args.len() == 3);
49 let transfer_receive_object_internal_cost_params = context
50 .extensions_mut()
51 .get::<NativesCostTable>()
52 .transfer_receive_object_internal_cost_params
53 .clone();
54 native_charge_gas_early_exit!(
55 context,
56 transfer_receive_object_internal_cost_params.transfer_receive_object_internal_cost_base
57 );
58 let child_ty = ty_args.pop().unwrap();
59 let child_receiver_sequence_number: SequenceNumber = pop_arg!(args, u64).into();
60 let child_receiver_object_id = args.pop_back().unwrap();
61 let parent = pop_arg!(args, AccountAddress).into();
62 assert!(args.is_empty());
63 let child_id: ObjectID = get_receiver_object_id(child_receiver_object_id.copy_value().unwrap())
64 .unwrap()
65 .value_as::<AccountAddress>()
66 .unwrap()
67 .into();
68 assert!(ty_args.is_empty());
69
70 let Some((tag, layout, annotated_layout)) = get_tag_and_layouts(context, &child_ty)? else {
71 return Ok(NativeResult::err(
72 context.gas_used(),
73 E_BCS_SERIALIZATION_FAILURE,
74 ));
75 };
76
77 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
78 let child = match object_runtime.receive_object(
79 parent,
80 child_id,
81 child_receiver_sequence_number,
82 &child_ty,
83 &layout,
84 &annotated_layout,
85 MoveObjectType::from(tag),
86 ) {
87 Ok(None) => {
89 return Ok(NativeResult::err(
90 context.gas_used(),
91 E_UNABLE_TO_RECEIVE_OBJECT,
92 ))
93 }
94 Ok(Some(ObjectResult::Loaded(gv))) => gv,
95 Ok(Some(ObjectResult::MismatchedType)) => {
96 return Ok(NativeResult::err(
97 context.gas_used(),
98 E_RECEIVING_OBJECT_TYPE_MISMATCH,
99 ))
100 }
101 Err(x) => return Err(x),
102 };
103
104 Ok(NativeResult::ok(context.gas_used(), smallvec![child]))
105}
106
107#[derive(Clone, Debug)]
108pub struct TransferInternalCostParams {
109 pub transfer_transfer_internal_cost_base: InternalGas,
110}
111pub fn transfer_internal(
117 context: &mut NativeContext,
118 mut ty_args: Vec<Type>,
119 mut args: VecDeque<Value>,
120) -> PartialVMResult<NativeResult> {
121 debug_assert!(ty_args.len() == 1);
122 debug_assert!(args.len() == 2);
123
124 let transfer_transfer_internal_cost_params = context
125 .extensions_mut()
126 .get::<NativesCostTable>()
127 .transfer_transfer_internal_cost_params
128 .clone();
129
130 native_charge_gas_early_exit!(
131 context,
132 transfer_transfer_internal_cost_params.transfer_transfer_internal_cost_base
133 );
134
135 let ty = ty_args.pop().unwrap();
136 let recipient = pop_arg!(args, AccountAddress);
137 let obj = args.pop_back().unwrap();
138
139 let owner = Owner::AddressOwner(recipient.into());
140 object_runtime_transfer(context, owner, ty, obj)?;
141 let cost = context.gas_used();
142 Ok(NativeResult::ok(cost, smallvec![]))
143}
144
145#[derive(Clone, Debug)]
146pub struct TransferFreezeObjectCostParams {
147 pub transfer_freeze_object_cost_base: InternalGas,
148}
149pub fn freeze_object(
155 context: &mut NativeContext,
156 mut ty_args: Vec<Type>,
157 mut args: VecDeque<Value>,
158) -> PartialVMResult<NativeResult> {
159 debug_assert!(ty_args.len() == 1);
160 debug_assert!(args.len() == 1);
161
162 let transfer_freeze_object_cost_params = context
163 .extensions_mut()
164 .get::<NativesCostTable>()
165 .transfer_freeze_object_cost_params
166 .clone();
167
168 native_charge_gas_early_exit!(
169 context,
170 transfer_freeze_object_cost_params.transfer_freeze_object_cost_base
171 );
172
173 let ty = ty_args.pop().unwrap();
174 let obj = args.pop_back().unwrap();
175
176 object_runtime_transfer(context, Owner::Immutable, ty, obj)?;
177
178 Ok(NativeResult::ok(context.gas_used(), smallvec![]))
179}
180
181#[derive(Clone, Debug)]
182pub struct TransferShareObjectCostParams {
183 pub transfer_share_object_cost_base: InternalGas,
184}
185pub fn share_object(
191 context: &mut NativeContext,
192 mut ty_args: Vec<Type>,
193 mut args: VecDeque<Value>,
194) -> PartialVMResult<NativeResult> {
195 debug_assert!(ty_args.len() == 1);
196 debug_assert!(args.len() == 1);
197
198 let transfer_share_object_cost_params = context
199 .extensions_mut()
200 .get::<NativesCostTable>()
201 .transfer_share_object_cost_params
202 .clone();
203
204 native_charge_gas_early_exit!(
205 context,
206 transfer_share_object_cost_params.transfer_share_object_cost_base
207 );
208
209 let ty = ty_args.pop().unwrap();
210 let obj = args.pop_back().unwrap();
211 let transfer_result = object_runtime_transfer(
212 context,
213 Owner::Shared {
216 initial_shared_version: SequenceNumber::new(),
217 },
218 ty,
219 obj,
220 )?;
221 let cost = context.gas_used();
222 Ok(match transfer_result {
223 TransferResult::New | TransferResult::SameOwner => NativeResult::ok(cost, smallvec![]),
226 TransferResult::OwnerChanged => NativeResult::err(cost, E_SHARED_NON_NEW_OBJECT),
227 })
228}
229
230fn object_runtime_transfer(
231 context: &mut NativeContext,
232 owner: Owner,
233 ty: Type,
234 obj: Value,
235) -> PartialVMResult<TransferResult> {
236 if !matches!(context.type_to_type_tag(&ty)?, TypeTag::Struct(_)) {
237 return Err(
238 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
239 .with_message("Sui verifier guarantees this is a struct".to_string()),
240 );
241 }
242
243 let obj_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
244 obj_runtime.transfer(owner, ty, obj)
245}