use std::{collections::HashMap, sync::Arc};
use cfx_bytes::Bytes;
use cfx_types::{Address, H256};
use cfx_vm_types::{self as vm, ActionParams, GasLeft, Spec};
use keccak_hash::keccak;
use primitives::BlockNumber;
use solidity_abi::ABIDecodeError;
use crate::spec::CommonParams;
use super::{
InternalRefContext, InternalTrapResult, IsActive, SolidityFunctionTrait,
};
lazy_static! {
static ref INTERNAL_CONTRACT_CODE: Arc<Bytes> =
Arc::new(vec![0u8, 0u8, 0u8, 0u8]);
static ref INTERNAL_CONTRACT_CODE_HASH: H256 = keccak([0u8, 0u8, 0u8, 0u8]);
}
pub trait InternalContractTrait: Send + Sync + IsActive {
fn address(&self) -> &Address;
fn initialize_block(&self, params: &CommonParams) -> BlockNumber;
fn get_func_table(&self) -> &SolFnTable;
fn execute(
&self, params: &ActionParams, context: &mut InternalRefContext,
) -> InternalTrapResult<GasLeft> {
let func_table = self.get_func_table();
let (solidity_fn, call_params) =
match load_solidity_fn(¶ms.data, func_table, context.spec) {
Ok(res) => res,
Err(err) => {
return InternalTrapResult::Return(Err(err));
}
};
solidity_fn.execute(call_params, params, context)
}
fn code(&self) -> Arc<Bytes> { INTERNAL_CONTRACT_CODE.clone() }
fn code_hash(&self) -> H256 { *INTERNAL_CONTRACT_CODE_HASH }
fn code_size(&self) -> usize { INTERNAL_CONTRACT_CODE.len() }
}
pub type SolFnTable = HashMap<[u8; 4], Box<dyn SolidityFunctionTrait>>;
fn load_solidity_fn<'a>(
data: &'a Option<Bytes>, func_table: &'a SolFnTable, spec: &'a Spec,
) -> vm::Result<(&'a Box<dyn SolidityFunctionTrait>, &'a [u8])> {
let call_data = data.as_ref().ok_or(ABIDecodeError("None call data"))?;
let (fn_sig_slice, call_params) = if call_data.len() < 4 {
return Err(ABIDecodeError("Incomplete function signature").into());
} else {
call_data.split_at(4)
};
let mut fn_sig = [0u8; 4];
fn_sig.clone_from_slice(fn_sig_slice);
let solidity_fn = func_table
.get(&fn_sig)
.filter(|&func| func.is_active(spec))
.ok_or(vm::Error::InternalContract("unsupported function".into()))?;
Ok((solidity_fn, call_params))
}
#[macro_export]
macro_rules! make_solidity_contract {
( $(#[$attr:meta])* $visibility:vis struct $name:ident ($addr:expr, "placeholder"); ) => {
$crate::make_solidity_contract! {
$(#[$attr])* $visibility struct $name ($addr, || Default::default(), initialize: |_: &CommonParams| u64::MAX, is_active: |_: &Spec| false);
}
};
( $(#[$attr:meta])* $visibility:vis struct $name:ident ($addr:expr, $gen_table:expr, "active_at_genesis"); ) => {
$crate::make_solidity_contract! {
$(#[$attr])* $visibility struct $name ($addr, $gen_table, initialize: |_: &CommonParams| 0u64, is_active: |_: &Spec| true);
}
};
( $(#[$attr:meta])* $visibility:vis struct $name:ident ($addr:expr, $gen_table:expr, initialize: $init:expr, is_active: $is_active:expr); ) => {
$(#[$attr])*
$visibility struct $name {
function_table: SolFnTable
}
impl $name {
pub fn instance() -> Self {
Self {
function_table: $gen_table()
}
}
}
impl InternalContractTrait for $name {
fn address(&self) -> &Address { &$addr }
fn get_func_table(&self) -> &SolFnTable { &self.function_table }
fn initialize_block(&self, param: &CommonParams) -> BlockNumber{ $init(param) }
}
impl IsActive for $name {
fn is_active(&self, spec: &Spec) -> bool {$is_active(spec)}
}
};
}
#[macro_export]
macro_rules! make_function_table {
($($func:ty), *) => { {
let mut table = SolFnTable::new();
$({ let f = <$func>::instance(); table.insert(f.function_sig(), Box::new(f)); }) *
table
} }
}