iota_move_natives_latest/
transfer.rs1use std::collections::VecDeque;
6
7use iota_types::{
8 account_abstraction::account::AuthenticatorFunctionRefV1Key,
9 base_types::{MoveObjectType, ObjectID, SequenceNumber},
10 dynamic_field::derive_dynamic_field_id,
11 object::Owner,
12};
13use move_binary_format::errors::{PartialVMError, PartialVMResult};
14use move_core_types::{
15 account_address::AccountAddress, gas_algebra::InternalGas, language_storage::TypeTag,
16 vm_status::StatusCode,
17};
18use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
19use move_vm_types::{
20 loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value,
21};
22use smallvec::smallvec;
23
24use super::object_runtime::{ObjectRuntime, TransferResult};
25use crate::{
26 NativesCostTable, get_receiver_object_id, get_tag_and_layouts,
27 object_runtime::object_store::ObjectResult,
28};
29
30const E_SHARED_NON_NEW_OBJECT: u64 = 0;
31const E_BCS_SERIALIZATION_FAILURE: u64 = 1;
32const E_RECEIVING_OBJECT_TYPE_MISMATCH: u64 = 2;
33const E_UNABLE_TO_RECEIVE_OBJECT: u64 = 3;
36const E_ACCOUNT_CANNOT_RECEIVE_OBJECT: u64 = 5;
39
40#[derive(Clone, Debug)]
41pub struct TransferReceiveObjectInternalCostParams {
42 pub transfer_receive_object_internal_cost_base: InternalGas,
43}
44
45pub fn receive_object_internal(
53 context: &mut NativeContext,
54 mut ty_args: Vec<Type>,
55 mut args: VecDeque<Value>,
56) -> PartialVMResult<NativeResult> {
57 debug_assert!(ty_args.len() == 1);
58 debug_assert!(args.len() == 3);
59 let transfer_receive_object_internal_cost_params = context
60 .extensions_mut()
61 .get::<NativesCostTable>()?
62 .transfer_receive_object_internal_cost_params
63 .clone();
64 native_charge_gas_early_exit!(
65 context,
66 transfer_receive_object_internal_cost_params.transfer_receive_object_internal_cost_base
67 );
68 let child_ty = ty_args.pop().unwrap();
69 let child_receiver_sequence_number: SequenceNumber = pop_arg!(args, u64).into();
70 let child_receiver_object_id = args.pop_back().unwrap();
71 let parent = pop_arg!(args, AccountAddress).into();
72 assert!(args.is_empty());
73 let child_id: ObjectID = get_receiver_object_id(child_receiver_object_id.copy_value().unwrap())
74 .unwrap()
75 .value_as::<AccountAddress>()
76 .unwrap()
77 .into();
78 assert!(ty_args.is_empty());
79
80 let Some((tag, layout, annotated_layout)) = get_tag_and_layouts(context, &child_ty)? else {
81 return Ok(NativeResult::err(
82 context.gas_used(),
83 E_BCS_SERIALIZATION_FAILURE,
84 ));
85 };
86
87 let object_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut()?;
88 if object_runtime.protocol_config.enable_move_authentication() {
89 let authenticator_fun_ref_id = derive_dynamic_field_id(
93 parent,
94 &AuthenticatorFunctionRefV1Key::tag().into(),
95 &AuthenticatorFunctionRefV1Key::default().to_bcs_bytes(),
96 )
97 .expect("should not fail this serialization");
98 if object_runtime.child_object_exists(parent, authenticator_fun_ref_id)? {
99 return Ok(NativeResult::err(
101 context.gas_used(),
102 E_ACCOUNT_CANNOT_RECEIVE_OBJECT,
103 ));
104 }
105 }
109
110 let child = match object_runtime.receive_object(
111 parent,
112 child_id,
113 child_receiver_sequence_number,
114 &child_ty,
115 &layout,
116 &annotated_layout,
117 MoveObjectType::from(tag),
118 ) {
119 Ok(None) => {
121 return Ok(NativeResult::err(
122 context.gas_used(),
123 E_UNABLE_TO_RECEIVE_OBJECT,
124 ));
125 }
126 Ok(Some(ObjectResult::Loaded(gv))) => gv,
127 Ok(Some(ObjectResult::MismatchedType)) => {
128 return Ok(NativeResult::err(
129 context.gas_used(),
130 E_RECEIVING_OBJECT_TYPE_MISMATCH,
131 ));
132 }
133 Err(x) => return Err(x),
134 };
135
136 Ok(NativeResult::ok(context.gas_used(), smallvec![child]))
137}
138
139#[derive(Clone, Debug)]
140pub struct TransferInternalCostParams {
141 pub transfer_transfer_internal_cost_base: InternalGas,
142}
143pub fn transfer_internal(
150 context: &mut NativeContext,
151 mut ty_args: Vec<Type>,
152 mut args: VecDeque<Value>,
153) -> PartialVMResult<NativeResult> {
154 debug_assert!(ty_args.len() == 1);
155 debug_assert!(args.len() == 2);
156
157 let transfer_transfer_internal_cost_params = context
158 .extensions_mut()
159 .get::<NativesCostTable>()?
160 .transfer_transfer_internal_cost_params
161 .clone();
162
163 native_charge_gas_early_exit!(
164 context,
165 transfer_transfer_internal_cost_params.transfer_transfer_internal_cost_base
166 );
167
168 let ty = ty_args.pop().unwrap();
169 let recipient = pop_arg!(args, AccountAddress);
170 let obj = args.pop_back().unwrap();
171
172 let owner = Owner::AddressOwner(recipient.into());
173 object_runtime_transfer(context, owner, ty, obj)?;
174 let cost = context.gas_used();
175 Ok(NativeResult::ok(cost, smallvec![]))
176}
177
178#[derive(Clone, Debug)]
179pub struct TransferFreezeObjectCostParams {
180 pub transfer_freeze_object_cost_base: InternalGas,
181}
182pub fn freeze_object(
189 context: &mut NativeContext,
190 mut ty_args: Vec<Type>,
191 mut args: VecDeque<Value>,
192) -> PartialVMResult<NativeResult> {
193 debug_assert!(ty_args.len() == 1);
194 debug_assert!(args.len() == 1);
195
196 let transfer_freeze_object_cost_params = context
197 .extensions_mut()
198 .get::<NativesCostTable>()?
199 .transfer_freeze_object_cost_params
200 .clone();
201
202 native_charge_gas_early_exit!(
203 context,
204 transfer_freeze_object_cost_params.transfer_freeze_object_cost_base
205 );
206
207 let ty = ty_args.pop().unwrap();
208 let obj = args.pop_back().unwrap();
209
210 object_runtime_transfer(context, Owner::Immutable, ty, obj)?;
211
212 Ok(NativeResult::ok(context.gas_used(), smallvec![]))
213}
214
215#[derive(Clone, Debug)]
216pub struct TransferShareObjectCostParams {
217 pub transfer_share_object_cost_base: InternalGas,
218}
219pub fn share_object(
226 context: &mut NativeContext,
227 mut ty_args: Vec<Type>,
228 mut args: VecDeque<Value>,
229) -> PartialVMResult<NativeResult> {
230 debug_assert!(ty_args.len() == 1);
231 debug_assert!(args.len() == 1);
232
233 let transfer_share_object_cost_params = context
234 .extensions_mut()
235 .get::<NativesCostTable>()?
236 .transfer_share_object_cost_params
237 .clone();
238
239 native_charge_gas_early_exit!(
240 context,
241 transfer_share_object_cost_params.transfer_share_object_cost_base
242 );
243
244 let ty = ty_args.pop().unwrap();
245 let obj = args.pop_back().unwrap();
246 let transfer_result = object_runtime_transfer(
247 context,
248 Owner::Shared {
251 initial_shared_version: SequenceNumber::new(),
252 },
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}