1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2019 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

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]);
}

/// Native implementation of an internal contract.
pub trait InternalContractTrait: Send + Sync + IsActive {
    /// Address of the internal contract
    fn address(&self) -> &Address;

    /// Time point to run `new_contract_with_admin` for such a internal contract
    fn initialize_block(&self, params: &CommonParams) -> BlockNumber;

    /// A hash-map for solidity function sig and execution handler.
    fn get_func_table(&self) -> &SolFnTable;

    /// execute this internal contract on the given parameters.
    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(&params.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))
}

/// A marco to implement an internal contract.
#[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)}
        }
    };
}

/// A marco to construct the functions table for an internal contract for a list
/// of types implements `SolidityFunctionTrait`.
#[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
    } }
}