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
132
133
134
135
136
137
138
139
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

// Copyright 2019 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

//! Evm factory.
use super::{interpreter::SharedCache, vmtype::VMType};
use cfx_types::U256;
#[cfg(test)]
use cfx_vm_types::CallType;
use cfx_vm_types::{ActionParams, Exec, Spec};
use std::sync::Arc;

/// Evm factory. Creates appropriate Evm.
#[derive(Clone)]
pub struct Factory {
    evm: VMType,
    evm_cache: Arc<SharedCache<false>>,
    evm_cache_cancun: Arc<SharedCache<true>>,
}

impl Factory {
    /// Create fresh instance of VM
    /// Might choose implementation depending on supplied gas.
    pub fn create(
        &self, params: ActionParams, spec: &Spec, depth: usize,
    ) -> Box<dyn Exec> {
        use super::interpreter::Interpreter;
        // Assert there is only one type. Parity Ethereum is dead and no more
        // types will be added.
        match self.evm {
            VMType::Interpreter => {}
        };

        match (Self::can_fit_in_usize(&params.gas), spec.cancun_opcodes) {
            (true, true) => Box::new(Interpreter::<usize, true>::new(
                params,
                self.evm_cache_cancun.clone(),
                spec,
                depth,
            )),
            (true, false) => Box::new(Interpreter::<usize, false>::new(
                params,
                self.evm_cache.clone(),
                spec,
                depth,
            )),
            (false, true) => Box::new(Interpreter::<U256, true>::new(
                params,
                self.evm_cache_cancun.clone(),
                spec,
                depth,
            )),
            (false, false) => Box::new(Interpreter::<U256, false>::new(
                params,
                self.evm_cache.clone(),
                spec,
                depth,
            )),
        }
    }

    /// Create new instance of specific `VMType` factory, with a size in bytes
    /// for caching jump destinations.
    pub fn new(evm: VMType, cache_size: usize) -> Self {
        Factory {
            evm,
            evm_cache: Arc::new(SharedCache::new(cache_size)),
            evm_cache_cancun: Arc::new(SharedCache::new(cache_size)),
        }
    }

    fn can_fit_in_usize(gas: &U256) -> bool {
        gas == &U256::from(gas.low_u64() as usize)
    }
}

impl Default for Factory {
    /// Returns native rust evm factory
    fn default() -> Factory {
        Factory {
            evm: VMType::Interpreter,
            evm_cache: Arc::new(SharedCache::default()),
            evm_cache_cancun: Arc::new(SharedCache::default()),
        }
    }
}

#[test]
fn test_create_vm() {
    use cfx_bytes::Bytes;
    use cfx_vm_types::{tests::MockContext, Context};

    let mut params = ActionParams::default();
    params.call_type = CallType::None;
    params.code = Some(Arc::new(Bytes::default()));
    let context = MockContext::new();
    let _vm =
        Factory::default().create(params, context.spec(), context.depth());
}

/// Create tests by injecting different VM factories
#[macro_export]
macro_rules! evm_test(
	($name_test: ident: $name_int: ident) => {
		#[test]
		fn $name_int() {
			$name_test(Factory::new(VMType::Interpreter, 1024 * 32));
		}
	}
);

/// Create ignored tests by injecting different VM factories
#[macro_export]
macro_rules! evm_test_ignore(
	($name_test: ident: $name_int: ident) => {
		#[test]
		#[ignore]
		#[cfg(feature = "ignored-tests")]
		fn $name_int() {
			$name_test(Factory::new(VMType::Interpreter, 1024 * 32));
		}
	}
);