sui_move_natives_v0/
transfer.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use super::object_runtime::{ObjectRuntime, TransferResult};
5use crate::NativesCostTable;
6use move_binary_format::errors::{PartialVMError, PartialVMResult};
7use move_core_types::{
8    account_address::AccountAddress, gas_algebra::InternalGas, language_storage::TypeTag,
9    vm_status::StatusCode,
10};
11use move_vm_runtime::{native_charge_gas_early_exit, native_functions::NativeContext};
12use move_vm_types::{
13    loaded_data::runtime_types::Type, natives::function::NativeResult, pop_arg, values::Value,
14};
15use smallvec::smallvec;
16use std::collections::VecDeque;
17use sui_types::{base_types::SequenceNumber, object::Owner};
18
19const E_SHARED_NON_NEW_OBJECT: u64 = 0;
20
21#[derive(Clone, Debug)]
22pub struct TransferInternalCostParams {
23    pub transfer_transfer_internal_cost_base: InternalGas,
24}
25/***************************************************************************************************
26* native fun transfer_impl
27* Implementation of the Move native function `transfer_impl<T: key>(obj: T, recipient: address)`
28*   gas cost: transfer_transfer_internal_cost_base                  |  covers various fixed costs in the oper
29**************************************************************************************************/
30pub fn transfer_internal(
31    context: &mut NativeContext,
32    mut ty_args: Vec<Type>,
33    mut args: VecDeque<Value>,
34) -> PartialVMResult<NativeResult> {
35    debug_assert!(ty_args.len() == 1);
36    debug_assert!(args.len() == 2);
37
38    let transfer_transfer_internal_cost_params = context
39        .extensions_mut()
40        .get::<NativesCostTable>()
41        .transfer_transfer_internal_cost_params
42        .clone();
43
44    native_charge_gas_early_exit!(
45        context,
46        transfer_transfer_internal_cost_params.transfer_transfer_internal_cost_base
47    );
48
49    let ty = ty_args.pop().unwrap();
50    let recipient = pop_arg!(args, AccountAddress);
51    let obj = args.pop_back().unwrap();
52    let owner = Owner::AddressOwner(recipient.into());
53    object_runtime_transfer(context, owner, ty, obj)?;
54
55    Ok(NativeResult::ok(context.gas_used(), smallvec![]))
56}
57
58#[derive(Clone, Debug)]
59pub struct TransferFreezeObjectCostParams {
60    pub transfer_freeze_object_cost_base: InternalGas,
61}
62/***************************************************************************************************
63* native fun freeze_object
64* Implementation of the Move native function `freeze_object<T: key>(obj: T)`
65*   gas cost: transfer_freeze_object_cost_base                  |  covers various fixed costs in the oper
66**************************************************************************************************/
67pub fn freeze_object(
68    context: &mut NativeContext,
69    mut ty_args: Vec<Type>,
70    mut args: VecDeque<Value>,
71) -> PartialVMResult<NativeResult> {
72    debug_assert!(ty_args.len() == 1);
73    debug_assert!(args.len() == 1);
74
75    let transfer_freeze_object_cost_params = context
76        .extensions_mut()
77        .get::<NativesCostTable>()
78        .transfer_freeze_object_cost_params
79        .clone();
80
81    native_charge_gas_early_exit!(
82        context,
83        transfer_freeze_object_cost_params.transfer_freeze_object_cost_base
84    );
85
86    let ty = ty_args.pop().unwrap();
87    let obj = args.pop_back().unwrap();
88    object_runtime_transfer(context, Owner::Immutable, ty, obj)?;
89
90    Ok(NativeResult::ok(context.gas_used(), smallvec![]))
91}
92
93#[derive(Clone, Debug)]
94pub struct TransferShareObjectCostParams {
95    pub transfer_share_object_cost_base: InternalGas,
96}
97/***************************************************************************************************
98* native fun share_object
99* Implementation of the Move native function `share_object<T: key>(obj: T)`
100*   gas cost: transfer_share_object_cost_base                  |  covers various fixed costs in the oper
101**************************************************************************************************/
102pub fn share_object(
103    context: &mut NativeContext,
104    mut ty_args: Vec<Type>,
105    mut args: VecDeque<Value>,
106) -> PartialVMResult<NativeResult> {
107    debug_assert!(ty_args.len() == 1);
108    debug_assert!(args.len() == 1);
109
110    let transfer_share_object_cost_params = context
111        .extensions_mut()
112        .get::<NativesCostTable>()
113        .transfer_share_object_cost_params
114        .clone();
115
116    native_charge_gas_early_exit!(
117        context,
118        transfer_share_object_cost_params.transfer_share_object_cost_base
119    );
120
121    let ty = ty_args.pop().unwrap();
122    let obj = args.pop_back().unwrap();
123    let transfer_result = object_runtime_transfer(
124        context,
125        // Dummy version, to be filled with the correct initial version when the effects of the
126        // transaction are written to storage.
127        Owner::Shared {
128            initial_shared_version: SequenceNumber::new(),
129        },
130        ty,
131        obj,
132    )?;
133    let cost = context.gas_used();
134    Ok(match transfer_result {
135        // New means the ID was created in this transaction
136        // SameOwner means the object was previously shared and was re-shared; since
137        // shared objects cannot be taken by-value in the adapter, this can only
138        // happen via test_scenario
139        TransferResult::New | TransferResult::SameOwner => NativeResult::ok(cost, smallvec![]),
140        TransferResult::OwnerChanged => NativeResult::err(cost, E_SHARED_NON_NEW_OBJECT),
141    })
142}
143
144fn object_runtime_transfer(
145    context: &mut NativeContext,
146    owner: Owner,
147    ty: Type,
148    obj: Value,
149) -> PartialVMResult<TransferResult> {
150    if !matches!(context.type_to_type_tag(&ty)?, TypeTag::Struct(_)) {
151        return Err(
152            PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
153                .with_message("Sui verifier guarantees this is a struct".to_string()),
154        );
155    }
156
157    let obj_runtime: &mut ObjectRuntime = context.extensions_mut().get_mut();
158    obj_runtime.transfer(owner, ty, obj)
159}