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