iota_move_natives_latest/
transfer.rs1use std::collections::VecDeque;
6
7use iota_sdk_types::{Address, ObjectId, Owner, StructTag, Version};
8use iota_types::{
9 account_abstraction::account::AuthenticatorFunctionRefV1Key,
10 dynamic_field::derive_dynamic_field_id, iota_sdk_types_conversions::struct_tag_core_to_sdk,
11};
12use move_binary_format::errors::{PartialVMError, PartialVMResult};
13use move_core_types::{
14 account_address::AccountAddress, gas_algebra::InternalGas, language_storage::TypeTag,
15 vm_status::StatusCode,
16};
17use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
18use move_vm_types::{
19 loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value,
20};
21use smallvec::smallvec;
22
23use super::object_runtime::{ObjectRuntime, TransferResult};
24use crate::{
25 NativesCostTable, get_receiver_object_id, get_tag_and_layouts,
26 object_runtime::object_store::ObjectResult,
27};
28
29const E_SHARED_NON_NEW_OBJECT: u64 = 0;
30const E_BCS_SERIALIZATION_FAILURE: u64 = 1;
31const E_RECEIVING_OBJECT_TYPE_MISMATCH: u64 = 2;
32const E_UNABLE_TO_RECEIVE_OBJECT: u64 = 3;
35const E_ACCOUNT_CANNOT_RECEIVE_OBJECT: u64 = 5;
38
39#[derive(Clone, Debug)]
40pub struct TransferReceiveObjectInternalCostParams {
41 pub transfer_receive_object_internal_cost_base: InternalGas,
42}
43
44pub fn receive_object_internal(
52 context: &mut NativeContext,
53 mut ty_args: Vec<Type>,
54 mut args: VecDeque<Value>,
55) -> PartialVMResult<NativeResult> {
56 debug_assert!(ty_args.len() == 1);
57 debug_assert!(args.len() == 3);
58 let transfer_receive_object_internal_cost_params = context
59 .extensions_mut()
60 .get::<NativesCostTable>()?
61 .transfer_receive_object_internal_cost_params
62 .clone();
63 native_charge_gas_early_exit!(
64 context,
65 transfer_receive_object_internal_cost_params.transfer_receive_object_internal_cost_base
66 );
67 let child_ty = ty_args.pop().unwrap();
68 let child_receiver_version: Version = pop_arg!(args, u64).into();
69 let child_receiver_object_id = args.pop_back().unwrap();
70 let parent = ObjectId::new(pop_arg!(args, AccountAddress).into_bytes());
71 assert!(args.is_empty());
72 let child_id = ObjectId::new(
73 get_receiver_object_id(child_receiver_object_id.copy_value().unwrap())
74 .unwrap()
75 .value_as::<AccountAddress>()
76 .unwrap()
77 .into_bytes(),
78 );
79 assert!(ty_args.is_empty());
80
81 let Some((tag, layout, annotated_layout)) = get_tag_and_layouts(context, &child_ty)? else {
82 return Ok(NativeResult::err(
83 context.gas_used(),
84 E_BCS_SERIALIZATION_FAILURE,
85 ));
86 };
87 let tag = struct_tag_core_to_sdk(&tag);
88
89 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
90 if object_runtime.protocol_config.enable_move_authentication() {
91 let authenticator_fun_ref_id = derive_dynamic_field_id(
95 parent,
96 &StructTag::new_authenticator_function_ref_v1_key().into(),
97 &AuthenticatorFunctionRefV1Key::default().to_bcs_bytes(),
98 )
99 .expect("should not fail this serialization");
100 if object_runtime.child_object_exists(parent, authenticator_fun_ref_id)? {
101 return Ok(NativeResult::err(
103 context.gas_used(),
104 E_ACCOUNT_CANNOT_RECEIVE_OBJECT,
105 ));
106 }
107 }
111
112 let child = match object_runtime.receive_object(
113 parent,
114 child_id,
115 child_receiver_version,
116 &child_ty,
117 &layout,
118 &annotated_layout,
119 tag,
120 ) {
121 Ok(None) => {
123 return Ok(NativeResult::err(
124 context.gas_used(),
125 E_UNABLE_TO_RECEIVE_OBJECT,
126 ));
127 }
128 Ok(Some(ObjectResult::Loaded(gv))) => gv,
129 Ok(Some(ObjectResult::MismatchedType)) => {
130 return Ok(NativeResult::err(
131 context.gas_used(),
132 E_RECEIVING_OBJECT_TYPE_MISMATCH,
133 ));
134 }
135 Err(x) => return Err(x),
136 };
137
138 Ok(NativeResult::ok(context.gas_used(), smallvec![child]))
139}
140
141#[derive(Clone, Debug)]
142pub struct TransferInternalCostParams {
143 pub transfer_transfer_internal_cost_base: InternalGas,
144}
145pub fn transfer_internal(
152 context: &mut NativeContext,
153 mut ty_args: Vec<Type>,
154 mut args: VecDeque<Value>,
155) -> PartialVMResult<NativeResult> {
156 debug_assert!(ty_args.len() == 1);
157 debug_assert!(args.len() == 2);
158
159 let transfer_transfer_internal_cost_params = context
160 .extensions_mut()
161 .get::<NativesCostTable>()?
162 .transfer_transfer_internal_cost_params
163 .clone();
164
165 native_charge_gas_early_exit!(
166 context,
167 transfer_transfer_internal_cost_params.transfer_transfer_internal_cost_base
168 );
169
170 let ty = ty_args.pop().unwrap();
171 let recipient = pop_arg!(args, AccountAddress);
172 let obj = args.pop_back().unwrap();
173
174 let owner = Owner::Address(Address::new(recipient.into_bytes()));
175 object_runtime_transfer(context, owner, ty, obj)?;
176 let cost = context.gas_used();
177 Ok(NativeResult::ok(cost, smallvec![]))
178}
179
180#[derive(Clone, Debug)]
181pub struct TransferFreezeObjectCostParams {
182 pub transfer_freeze_object_cost_base: InternalGas,
183}
184pub fn freeze_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_freeze_object_cost_params = context
199 .extensions_mut()
200 .get::<NativesCostTable>()?
201 .transfer_freeze_object_cost_params
202 .clone();
203
204 native_charge_gas_early_exit!(
205 context,
206 transfer_freeze_object_cost_params.transfer_freeze_object_cost_base
207 );
208
209 let ty = ty_args.pop().unwrap();
210 let obj = args.pop_back().unwrap();
211
212 object_runtime_transfer(context, Owner::Immutable, ty, obj)?;
213
214 Ok(NativeResult::ok(context.gas_used(), smallvec![]))
215}
216
217#[derive(Clone, Debug)]
218pub struct TransferShareObjectCostParams {
219 pub transfer_share_object_cost_base: InternalGas,
220}
221pub fn share_object(
228 context: &mut NativeContext,
229 mut ty_args: Vec<Type>,
230 mut args: VecDeque<Value>,
231) -> PartialVMResult<NativeResult> {
232 debug_assert!(ty_args.len() == 1);
233 debug_assert!(args.len() == 1);
234
235 let transfer_share_object_cost_params = context
236 .extensions_mut()
237 .get::<NativesCostTable>()?
238 .transfer_share_object_cost_params
239 .clone();
240
241 native_charge_gas_early_exit!(
242 context,
243 transfer_share_object_cost_params.transfer_share_object_cost_base
244 );
245
246 let ty = ty_args.pop().unwrap();
247 let obj = args.pop_back().unwrap();
248 let transfer_result = object_runtime_transfer(
249 context,
250 Owner::Shared(Default::default()),
253 ty,
254 obj,
255 )?;
256 let cost = context.gas_used();
257 Ok(match transfer_result {
258 TransferResult::New | TransferResult::SameOwner => NativeResult::ok(cost, smallvec![]),
261 TransferResult::OwnerChanged => NativeResult::err(cost, E_SHARED_NON_NEW_OBJECT),
262 })
263}
264
265fn object_runtime_transfer(
266 context: &mut NativeContext,
267 owner: Owner,
268 ty: Type,
269 obj: Value,
270) -> PartialVMResult<TransferResult> {
271 if !matches!(context.type_to_type_tag(&ty)?, TypeTag::Struct(_)) {
272 return Err(
273 PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
274 .with_message("IOTA verifier guarantees this is a struct".to_string()),
275 );
276 }
277
278 let obj_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
279 obj_runtime.transfer(owner, ty, obj)
280}