use crate::{
executive_observer::{AddressPocket, TracerTrait},
stack::CallStackInfo,
state::State,
substate::Substate,
};
use cfx_vm_types::{self as vm, ActionParams, Spec};
use cfx_types::{
address_util::AddressUtil, Address, AddressSpaceUtil, AddressWithSpace,
Space,
};
use vm::Env;
use super::super::components::InternalRefContext;
fn available_admin_address(_spec: &Spec, address: &Address) -> bool {
address.is_user_account_address() || address.is_null_address()
}
pub fn suicide(
contract_address: &AddressWithSpace, refund_address: &AddressWithSpace,
state: &mut State, spec: &Spec, substate: &mut Substate, env: &Env,
creating_contract: bool, tracer: &mut dyn TracerTrait,
) -> vm::Result<()> {
let transaction_hash = env.transaction_hash;
let contract_create_in_same_tx = state
.created_at_transaction(contract_address, transaction_hash)?
|| creating_contract;
let soft_suicide = spec.cip151 && !contract_create_in_same_tx;
let balance = state.balance(contract_address)?;
if !soft_suicide {
tracer.selfdestruct(
&contract_address.address,
&refund_address.address,
balance,
);
substate.suicides.insert(contract_address.clone());
}
if (refund_address == contract_address && !soft_suicide)
|| (!spec.is_valid_address(&refund_address.address)
&& refund_address.space == Space::Native)
{
tracer.trace_internal_transfer(
AddressPocket::Balance(*contract_address),
AddressPocket::MintBurn,
balance,
);
state.sub_balance(contract_address, &balance)?;
state.sub_total_issued(balance);
if contract_address.space == Space::Ethereum {
state.sub_total_evm_tokens(balance);
}
} else if refund_address != contract_address {
trace!(target: "context", "Destroying {} -> {} (xfer: {})", contract_address.address, refund_address.address, balance);
tracer.trace_internal_transfer(
AddressPocket::Balance(*contract_address),
AddressPocket::Balance(*refund_address),
balance,
);
state.transfer_balance(contract_address, refund_address, &balance)?;
}
Ok(())
}
pub fn set_admin(
contract_address: Address, new_admin_address: Address,
params: &ActionParams, context: &mut InternalRefContext,
) -> vm::Result<()> {
let requester = ¶ms.sender;
debug!(
"set_admin requester {:?} contract {:?}, \
new_admin {:?}, admin_control_in_creation {:?}",
requester,
contract_address,
new_admin_address,
context.callstack.admin_control_in_creation(),
);
let clear_admin_in_create = context.callstack.admin_control_in_creation()
== Some(&contract_address.with_native_space())
&& new_admin_address.is_null_address();
if context.is_contract_address(&contract_address)?
&& context.state.exists(&contract_address.with_native_space())?
&& (context.state.admin(&contract_address)?.eq(requester)
|| clear_admin_in_create)
&& available_admin_address(&context.spec, &new_admin_address)
{
debug!("set_admin to {:?}", new_admin_address);
context
.state
.set_admin(&contract_address, &new_admin_address)?;
}
Ok(())
}
pub fn destroy(
contract_address: Address, params: &ActionParams, state: &mut State,
spec: &Spec, substate: &mut Substate, env: &Env,
tracer: &mut dyn TracerTrait, callstack: &CallStackInfo,
) -> vm::Result<()> {
debug!("contract_address={:?}", contract_address);
let requester = ¶ms.sender;
let admin = state.admin(&contract_address)?;
if admin == *requester {
suicide(
&contract_address.with_native_space(),
&admin.with_native_space(),
state,
spec,
substate,
env,
callstack.creating_contract(&contract_address.with_native_space()),
tracer,
)
} else {
Ok(())
}
}