cfx_executor/machine/
mod.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
5mod vm_factory;
6
7use super::builtin::Builtin;
8use crate::{
9    builtin::{
10        build_bls12_builtin_map, builtin_factory, ActivateAt,
11        AltBn128PairingPricer, Blake2FPricer, IfPricer, Linear, ModexpPricer,
12        StaticPlan,
13    },
14    internal_contract::InternalContractMap,
15    spec::{CommonParams, TransitionsBlockNumber, TransitionsEpochHeight},
16};
17use cfx_types::{Address, AddressWithSpace, Space, H256};
18use cfx_vm_types::Spec;
19use primitives::{block::BlockHeight, BlockNumber};
20use std::{collections::BTreeMap, sync::Arc};
21
22pub use vm_factory::VmFactory;
23pub type SpecCreationRules = dyn Fn(&mut Spec, BlockNumber) + Sync + Send;
24
25/// Extension trait for `TransitionsBlockNumber`: produces
26/// `ActivateAt::ByBlockNumber` via a field-selector closure. The closure
27/// type-checks against `&TransitionsBlockNumber`, so passing a field from
28/// `TransitionsEpochHeight` is a compile error.
29trait ActivateByNumbers {
30    fn activate_at(&self, f: impl Fn(&Self) -> u64) -> ActivateAt;
31}
32impl ActivateByNumbers for TransitionsBlockNumber {
33    fn activate_at(&self, f: impl Fn(&Self) -> u64) -> ActivateAt {
34        ActivateAt::ByBlockNumber(f(self))
35    }
36}
37
38/// Extension trait for `TransitionsEpochHeight`: produces
39/// `ActivateAt::ByEpochHeight` via a field-selector closure. The closure
40/// type-checks against `&TransitionsEpochHeight`, so passing a field from
41/// `TransitionsBlockNumber` is a compile error.
42trait ActivateByHeights {
43    fn activate_at(&self, f: impl Fn(&Self) -> u64) -> ActivateAt;
44}
45impl ActivateByHeights for TransitionsEpochHeight {
46    fn activate_at(&self, f: impl Fn(&Self) -> u64) -> ActivateAt {
47        ActivateAt::ByEpochHeight(f(self))
48    }
49}
50
51pub struct Machine {
52    params: CommonParams,
53    vm_factory: VmFactory,
54    builtins: Arc<BTreeMap<Address, Builtin>>,
55    builtins_evm: Arc<BTreeMap<Address, Builtin>>,
56    internal_contracts: Arc<InternalContractMap>,
57    #[cfg(test)]
58    max_depth: Option<usize>,
59}
60
61impl Machine {
62    pub fn new(params: CommonParams, vm_factory: VmFactory) -> Machine {
63        Machine {
64            params,
65            vm_factory,
66            builtins: Arc::new(BTreeMap::new()),
67            builtins_evm: Arc::new(BTreeMap::new()),
68            internal_contracts: Arc::new(InternalContractMap::default()),
69            #[cfg(test)]
70            max_depth: None,
71        }
72    }
73
74    pub fn new_with_builtin(
75        params: CommonParams, vm_factory: VmFactory,
76    ) -> Machine {
77        let builtin = new_builtin_map(&params, Space::Native);
78        let builtin_evm = new_builtin_map(&params, Space::Ethereum);
79
80        let internal_contracts = InternalContractMap::new(&params);
81        Machine {
82            params,
83            vm_factory,
84            builtins: Arc::new(builtin),
85            builtins_evm: Arc::new(builtin_evm),
86            internal_contracts: Arc::new(internal_contracts),
87            #[cfg(test)]
88            max_depth: None,
89        }
90    }
91
92    pub fn builtin(
93        &self, address: &AddressWithSpace, block_number: BlockNumber,
94        epoch_height: u64,
95    ) -> Option<&Builtin> {
96        let builtins = match address.space {
97            Space::Native => &self.builtins,
98            Space::Ethereum => &self.builtins_evm,
99        };
100        builtins.get(&address.address).and_then(|b| {
101            if b.is_active(block_number, epoch_height) {
102                Some(b)
103            } else {
104                None
105            }
106        })
107    }
108
109    /// Get the general parameters of the chain.
110    pub fn params(&self) -> &CommonParams { &self.params }
111
112    pub fn spec(&self, number: BlockNumber, height: BlockHeight) -> Spec {
113        self.params.spec(number, height)
114    }
115
116    #[cfg(test)]
117    pub fn set_max_depth(&mut self, max_depth: usize) {
118        self.max_depth = Some(max_depth)
119    }
120
121    #[cfg(test)]
122    pub fn spec_for_test(&self, number: u64) -> Spec {
123        let mut spec = self.spec(number, number);
124        if let Some(max_depth) = self.max_depth {
125            spec.max_depth = max_depth;
126        }
127        spec
128    }
129
130    /// Builtin-contracts for the chain..
131    pub fn builtins(&self) -> &BTreeMap<Address, Builtin> { &*self.builtins }
132
133    pub fn builtins_evm(&self) -> &BTreeMap<Address, Builtin> {
134        &*self.builtins_evm
135    }
136
137    /// Builtin-contracts for the chain..
138    pub fn internal_contracts(&self) -> &InternalContractMap {
139        &*self.internal_contracts
140    }
141
142    /// Get a VM factory that can execute on this state.
143    pub fn vm_factory(&self) -> VmFactory { self.vm_factory.clone() }
144
145    pub fn vm_factory_ref(&self) -> &VmFactory { &self.vm_factory }
146}
147
148fn new_builtin_map(
149    params: &CommonParams, space: Space,
150) -> BTreeMap<Address, Builtin> {
151    let mut btree = BTreeMap::new();
152
153    btree.insert(
154        Address::from(H256::from_low_u64_be(1)),
155        Builtin::new(
156            Box::new(StaticPlan(Linear::new(3000, 0))),
157            match space {
158                Space::Native => builtin_factory("ecrecover"),
159                Space::Ethereum => builtin_factory("ecrecover_evm"),
160            },
161            ActivateAt::GENESIS,
162        ),
163    );
164    btree.insert(
165        Address::from(H256::from_low_u64_be(2)),
166        Builtin::new(
167            Box::new(StaticPlan(Linear::new(60, 12))),
168            builtin_factory("sha256"),
169            ActivateAt::GENESIS,
170        ),
171    );
172    btree.insert(
173        Address::from(H256::from_low_u64_be(3)),
174        Builtin::new(
175            Box::new(StaticPlan(Linear::new(600, 120))),
176            builtin_factory("ripemd160"),
177            ActivateAt::GENESIS,
178        ),
179    );
180    btree.insert(
181        Address::from(H256::from_low_u64_be(4)),
182        Builtin::new(
183            Box::new(StaticPlan(Linear::new(15, 3))),
184            builtin_factory("identity"),
185            ActivateAt::GENESIS,
186        ),
187    );
188
189    // CIP-645e: EIP-2565
190    let mod_exp_pricer = IfPricer::new(
191        |spec| spec.cip645.eip2565,
192        ModexpPricer::new_berlin(200),
193        ModexpPricer::new_byzantium(20),
194    );
195    btree.insert(
196        Address::from(H256::from_low_u64_be(5)),
197        Builtin::new(
198            Box::new(mod_exp_pricer),
199            builtin_factory("modexp"),
200            params.transition_numbers.activate_at(|t| t.cip62),
201        ),
202    );
203
204    // CIP-645a: EIP-1108
205    let bn_add_pricer = IfPricer::new(
206        |spec| spec.cip645.eip1108,
207        Linear::new(150, 0),
208        Linear::new(500, 0),
209    );
210    btree.insert(
211        Address::from(H256::from_low_u64_be(6)),
212        Builtin::new(
213            Box::new(bn_add_pricer),
214            builtin_factory("alt_bn128_add"),
215            params.transition_numbers.activate_at(|t| t.cip62),
216        ),
217    );
218
219    // CIP-645a: EIP-1108
220    let bn_mul_pricer = IfPricer::new(
221        |spec| spec.cip645.eip1108,
222        Linear::new(6_000, 0),
223        Linear::new(40_000, 0),
224    );
225    btree.insert(
226        Address::from(H256::from_low_u64_be(7)),
227        Builtin::new(
228            Box::new(bn_mul_pricer),
229            builtin_factory("alt_bn128_mul"),
230            params.transition_numbers.activate_at(|t| t.cip62),
231        ),
232    );
233
234    // CIP-645a: EIP-1108
235    let bn_pair_pricer = IfPricer::new(
236        |spec| spec.cip645.eip1108,
237        AltBn128PairingPricer::new(45_000, 34_000),
238        AltBn128PairingPricer::new(100_000, 80_000),
239    );
240    btree.insert(
241        Address::from(H256::from_low_u64_be(8)),
242        Builtin::new(
243            Box::new(bn_pair_pricer),
244            builtin_factory("alt_bn128_pairing"),
245            params.transition_numbers.activate_at(|t| t.cip62),
246        ),
247    );
248    btree.insert(
249        Address::from(H256::from_low_u64_be(9)),
250        Builtin::new(
251            Box::new(StaticPlan(Blake2FPricer::new(1))),
252            builtin_factory("blake2_f"),
253            params.transition_numbers.activate_at(|t| t.cip92),
254        ),
255    );
256    btree.insert(
257        Address::from(H256::from_low_u64_be(10)),
258        Builtin::new(
259            Box::new(StaticPlan(Linear::new(50000, 0))),
260            builtin_factory("kzg_point_eval"),
261            params.transition_numbers.activate_at(|t| t.cip144),
262        ),
263    );
264    for (address, price_plan, bls12_impl) in build_bls12_builtin_map() {
265        btree.insert(
266            Address::from(H256::from_low_u64_be(address)),
267            Builtin::new(
268                price_plan,
269                bls12_impl,
270                params.transition_heights.activate_at(|t| t.eip2537),
271            ),
272        );
273    }
274    btree
275}