cfx_executor/internal_contract/components/
contract.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 std::{collections::HashMap, sync::Arc};
6
7use cfx_bytes::Bytes;
8use cfx_types::{Address, H256};
9use cfx_vm_types::{self as vm, ActionParams, GasLeft, Spec};
10use keccak_hash::keccak;
11use primitives::BlockNumber;
12use solidity_abi::ABIDecodeError;
13
14use crate::spec::CommonParams;
15
16use super::{
17    InternalRefContext, InternalTrapResult, IsActive, SolidityFunctionTrait,
18};
19
20lazy_static! {
21    static ref INTERNAL_CONTRACT_CODE: Arc<Bytes> =
22        Arc::new(vec![0u8, 0u8, 0u8, 0u8]);
23    static ref INTERNAL_CONTRACT_CODE_HASH: H256 = keccak([0u8, 0u8, 0u8, 0u8]);
24}
25
26/// Native implementation of an internal contract.
27pub trait InternalContractTrait: Send + Sync + IsActive {
28    /// Address of the internal contract
29    fn address(&self) -> &Address;
30
31    /// Time point to run `new_contract_with_admin` for such a internal contract
32    fn initialize_block(&self, params: &CommonParams) -> BlockNumber;
33
34    /// A hash-map for solidity function sig and execution handler.
35    fn get_func_table(&self) -> &SolFnTable;
36
37    /// execute this internal contract on the given parameters.
38    fn execute(
39        &self, params: &ActionParams, context: &mut InternalRefContext,
40    ) -> InternalTrapResult<GasLeft> {
41        let func_table = self.get_func_table();
42
43        let (solidity_fn, call_params) =
44            match load_solidity_fn(&params.data, func_table, context.spec) {
45                Ok(res) => res,
46                Err(err) => {
47                    return InternalTrapResult::Return(Err(err));
48                }
49            };
50
51        solidity_fn.execute(call_params, params, context)
52    }
53
54    fn code(&self) -> Arc<Bytes> { INTERNAL_CONTRACT_CODE.clone() }
55
56    fn code_hash(&self) -> H256 { *INTERNAL_CONTRACT_CODE_HASH }
57
58    fn code_size(&self) -> usize { INTERNAL_CONTRACT_CODE.len() }
59}
60
61pub type SolFnTable = HashMap<[u8; 4], Box<dyn SolidityFunctionTrait>>;
62
63fn load_solidity_fn<'a>(
64    data: &'a Option<Bytes>, func_table: &'a SolFnTable, spec: &'a Spec,
65) -> vm::Result<(&'a Box<dyn SolidityFunctionTrait>, &'a [u8])> {
66    let call_data = data.as_ref().ok_or(ABIDecodeError("None call data"))?;
67    let (fn_sig_slice, call_params) = if call_data.len() < 4 {
68        return Err(ABIDecodeError("Incomplete function signature").into());
69    } else {
70        call_data.split_at(4)
71    };
72
73    let mut fn_sig = [0u8; 4];
74    fn_sig.clone_from_slice(fn_sig_slice);
75
76    let solidity_fn = func_table
77        .get(&fn_sig)
78        .filter(|&func| func.is_active(spec))
79        .ok_or(vm::Error::InternalContract("unsupported function".into()))?;
80    Ok((solidity_fn, call_params))
81}
82
83/// A marco to implement an internal contract.
84#[macro_export]
85macro_rules! make_solidity_contract {
86    ( $(#[$attr:meta])* $visibility:vis struct $name:ident ($addr:expr, "placeholder"); ) => {
87        $crate::make_solidity_contract! {
88            $(#[$attr])* $visibility struct $name ($addr, || Default::default(), initialize: |_: &CommonParams| u64::MAX, is_active: |_: &Spec| false);
89        }
90    };
91    ( $(#[$attr:meta])* $visibility:vis struct $name:ident ($addr:expr, $gen_table:expr, "active_at_genesis"); ) => {
92        $crate::make_solidity_contract! {
93            $(#[$attr])* $visibility struct $name ($addr, $gen_table, initialize: |_: &CommonParams| 0u64, is_active: |_: &Spec| true);
94        }
95    };
96    ( $(#[$attr:meta])* $visibility:vis struct $name:ident ($addr:expr, $gen_table:expr, initialize: $init:expr, is_active: $is_active:expr); ) => {
97        $(#[$attr])*
98        $visibility struct $name {
99            function_table: SolFnTable
100        }
101
102        impl $name {
103            pub fn instance() -> Self {
104                Self {
105                    function_table: $gen_table()
106                }
107            }
108        }
109
110        impl InternalContractTrait for $name {
111            fn address(&self) -> &Address { &$addr }
112            fn get_func_table(&self) -> &SolFnTable { &self.function_table }
113            fn initialize_block(&self, param: &CommonParams) -> BlockNumber{ $init(param) }
114        }
115
116        impl IsActive for $name {
117            fn is_active(&self, spec: &Spec) -> bool {$is_active(spec)}
118        }
119    };
120}
121
122/// A marco to construct the functions table for an internal contract for a list
123/// of types implements `SolidityFunctionTrait`.
124#[macro_export]
125macro_rules! make_function_table {
126    ($($func:ty), *) => { {
127        let mut table = SolFnTable::new();
128        $({ let f = <$func>::instance(); table.insert(f.function_sig(), Box::new(f)); }) *
129        table
130    } }
131}