cfx_executor/internal_contract/impls/
admin.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use crate::{
6    executive_observer::{AddressPocket, TracerTrait},
7    stack::CallStackInfo,
8    state::State,
9    substate::Substate,
10};
11use cfx_vm_types::{self as vm, ActionParams, Spec};
12
13use cfx_types::{
14    address_util::AddressUtil, Address, AddressSpaceUtil, AddressWithSpace,
15    Space,
16};
17use vm::Env;
18
19use super::super::components::InternalRefContext;
20
21fn available_admin_address(_spec: &Spec, address: &Address) -> bool {
22    address.is_user_account_address() || address.is_null_address()
23}
24
25/// The Actual Implementation of `suicide`.
26/// The contract which has non zero `collateral_for_storage` cannot suicide,
27/// otherwise it will:
28///   1. refund collateral for code
29///   2. refund sponsor balance
30///   3. refund contract balance
31///   4. kill the contract
32pub fn suicide(
33    contract_address: &AddressWithSpace, refund_address: &AddressWithSpace,
34    state: &mut State, spec: &Spec, substate: &mut Substate, env: &Env,
35    creating_contract: bool, tracer: &mut dyn TracerTrait,
36) -> vm::Result<()> {
37    // After CIP-151, contract can only be killed in the same transaction as
38    // its creation.
39    let transaction_hash = env.transaction_hash;
40    let contract_create_in_same_tx = state
41        .created_at_transaction(contract_address, transaction_hash)?
42        || creating_contract;
43    let soft_suicide = spec.cip151 && !contract_create_in_same_tx;
44
45    let balance = state.balance(contract_address)?;
46
47    if !soft_suicide {
48        substate.suicides.insert(contract_address.clone());
49    }
50
51    if (refund_address == contract_address && !soft_suicide)
52        || (!spec.is_valid_address(&refund_address.address)
53            && refund_address.space == Space::Native)
54    {
55        tracer.trace_internal_transfer(
56            AddressPocket::Balance(*contract_address),
57            AddressPocket::MintBurn,
58            balance,
59        );
60        // When destroying, the balance will be burnt.
61        state.sub_balance(contract_address, &balance)?;
62        state.sub_total_issued(balance);
63        if contract_address.space == Space::Ethereum {
64            state.sub_total_evm_tokens(balance);
65        }
66    } else if refund_address != contract_address {
67        trace!(target: "context", "Destroying {} -> {} (xfer: {})", contract_address.address, refund_address.address, balance);
68        tracer.trace_internal_transfer(
69            AddressPocket::Balance(*contract_address),
70            AddressPocket::Balance(*refund_address),
71            balance,
72        );
73        tracer.selfdestruct(
74            contract_address.space,
75            &contract_address.address,
76            &refund_address.address,
77            balance,
78        );
79        state.transfer_balance(contract_address, refund_address, &balance)?;
80    }
81
82    Ok(())
83}
84
85/// Implementation of `set_admin(address,address)`.
86/// The input should consist of 20 bytes `contract_address` + 20 bytes
87/// `new_admin_address`
88pub fn set_admin(
89    contract_address: Address, new_admin_address: Address,
90    params: &ActionParams, context: &mut InternalRefContext,
91) -> vm::Result<()> {
92    let requester = &params.sender;
93    debug!(
94        "set_admin requester {:?} contract {:?}, \
95         new_admin {:?}, admin_control_in_creation {:?}",
96        requester,
97        contract_address,
98        new_admin_address,
99        context.callstack.admin_control_in_creation(),
100    );
101
102    let clear_admin_in_create = context.callstack.admin_control_in_creation()
103        == Some(&contract_address.with_native_space())
104        && new_admin_address.is_null_address();
105
106    if context.is_contract_address(&contract_address)?
107        && context.state.exists(&contract_address.with_native_space())?
108        // Allow set admin if requester matches or in contract creation to clear admin.
109        && (context.state.admin(&contract_address)?.eq(requester)
110        || clear_admin_in_create)
111        // Only allow user account to be admin, if not to clear admin.
112        && available_admin_address(&context.spec, &new_admin_address)
113    {
114        debug!("set_admin to {:?}", new_admin_address);
115        // Admin is cleared by set new_admin_address to null address.
116        context
117            .state
118            .set_admin(&contract_address, &new_admin_address)?;
119    }
120    Ok(())
121}
122
123/// Implementation of `destroy(address)`.
124/// The input should consist of 20 bytes `contract_address`
125pub fn destroy(
126    contract_address: Address, params: &ActionParams, state: &mut State,
127    spec: &Spec, substate: &mut Substate, env: &Env,
128    tracer: &mut dyn TracerTrait, callstack: &CallStackInfo,
129) -> vm::Result<()> {
130    debug!("contract_address={:?}", contract_address);
131
132    let requester = &params.sender;
133    let admin = state.admin(&contract_address)?;
134    if admin == *requester {
135        suicide(
136            &contract_address.with_native_space(),
137            &admin.with_native_space(),
138            state,
139            spec,
140            substate,
141            env,
142            callstack.creating_contract(&contract_address.with_native_space()),
143            tracer,
144        )
145    } else {
146        Ok(())
147    }
148}