1use crate::{
2 executive::{contract_address, gas_required_for},
3 executive_observer::AddressPocket,
4 internal_bail,
5 stack::{
6 Context, Executable, ExecutableOutcome, FrameResult, FrameReturn,
7 Resumable,
8 },
9};
10
11use cfx_parameters::block::CROSS_SPACE_GAS_RATIO;
12use cfx_statedb::Result as DbResult;
13use cfx_types::{
14 address_util::AddressUtil, Address, AddressSpaceUtil, Space, H256, U256,
15};
16use cfx_vm_interpreter::Finalize;
17use cfx_vm_types::{
18 self as vm, ActionParams, ActionValue, CallType, Context as _,
19 CreateContractAddress, CreateType, GasLeft, ParamsType, Spec,
20};
21use solidity_abi::ABIEncodable;
22use std::{marker::PhantomData, sync::Arc};
23
24use super::super::{
25 components::{InternalRefContext, InternalTrapResult, SolidityEventTrait},
26 contracts::cross_space::{
27 CallEvent, CreateEvent, ReturnEvent, WithdrawEvent,
28 },
29};
30
31pub fn create_gas(context: &InternalRefContext, code: &[u8]) -> DbResult<U256> {
32 let code_length = code.len();
33
34 let transaction_gas = gas_required_for(
35 true,
36 code,
37 None,
38 0,
39 &context.spec.to_consensus_spec(),
40 ) + context.spec.tx_gas as u64;
41
42 let create_gas = U256::from(context.spec.create_gas);
43
44 let address_mapping_gas = context.spec.sha3_gas * 2;
45
46 let create_log_gas = {
47 let log_data_length =
48 H256::len_bytes() * 4 + (code_length + 31) / 32 * 32;
49 context.spec.log_gas
50 + 3 * context.spec.log_topic_gas
51 + context.spec.log_data_gas * log_data_length
52 };
53
54 let return_log_gas = {
55 let log_data_length = H256::len_bytes();
56 context.spec.log_gas
57 + context.spec.log_topic_gas
58 + context.spec.log_data_gas * log_data_length
59 };
60
61 Ok(create_gas
62 + transaction_gas
63 + address_mapping_gas
64 + create_log_gas
65 + return_log_gas)
66}
67
68pub fn call_gas(
69 receiver: Address, params: &ActionParams, context: &InternalRefContext,
70 data: &[u8],
71) -> DbResult<U256> {
72 let data_length = data.len();
73
74 let transaction_gas = gas_required_for(
75 false,
76 data,
77 None,
78 0,
79 &context.spec.to_consensus_spec(),
80 ) + context.spec.tx_gas as u64;
81
82 let new_account = !context
83 .state
84 .exists_and_not_null(&receiver.with_evm_space())?;
85 let new_account_gas = if new_account {
86 context.spec.call_new_account_gas * context.spec.evm_gas_ratio
87 } else {
88 0
89 };
90
91 let transfer_gas = if params.value.value() > U256::zero() {
92 context.spec.call_value_transfer_gas
93 } else {
94 0
95 };
96
97 let call_gas =
98 U256::from(context.spec.call_gas) + new_account_gas + transfer_gas;
99
100 let address_mapping_gas = context.spec.sha3_gas * 2;
101
102 let call_log_gas = {
103 let log_data_length =
104 H256::len_bytes() * 4 + (data_length + 31) / 32 * 32;
105 context.spec.log_gas
106 + 3 * context.spec.log_topic_gas
107 + context.spec.log_data_gas * log_data_length
108 };
109
110 let return_log_gas = {
111 let log_data_length = H256::len_bytes();
112 context.spec.log_gas
113 + context.spec.log_topic_gas
114 + context.spec.log_data_gas * log_data_length
115 };
116
117 Ok(call_gas
118 + transaction_gas
119 + address_mapping_gas
120 + call_log_gas
121 + return_log_gas)
122}
123
124pub fn static_call_gas(spec: &Spec) -> U256 {
125 let call_gas = U256::from(spec.call_gas);
126 let address_mapping_gas = spec.sha3_gas * 2;
127
128 call_gas + address_mapping_gas
129}
130
131pub fn withdraw_gas(spec: &Spec) -> U256 {
132 let call_gas = U256::from(spec.call_value_transfer_gas);
133 let transaction_gas = spec.tx_gas;
134 let address_mapping_gas = spec.sha3_gas;
135 let log_gas = spec.log_gas
136 + spec.log_topic_gas * 3
137 + spec.log_data_gas * H256::len_bytes() * 2;
138
139 call_gas + transaction_gas + address_mapping_gas + log_gas
140}
141
142#[derive(Clone)]
143pub struct Resume {
144 pub params: ActionParams,
145 pub gas_retained: U256,
146 pub wait_return_log: bool,
147}
148
149impl Resumable for Resume {
150 fn resume(self: Box<Self>, result: FrameResult) -> Box<dyn Executable> {
151 let frame_return = match result {
152 Ok(r) => r,
153 Err(e) => {
154 return Box::new(PassResult {
155 params: self.params,
156 result: Err(e),
157 apply_state: false,
158 wait_return_log: self.wait_return_log,
159 });
160 }
161 };
162
163 let gas_left = self.gas_retained + frame_return.gas_left;
164 let apply_state = frame_return.apply_state;
165
166 let data = match frame_return {
167 FrameReturn {
168 apply_state: true,
169 create_address: Some(create_address),
170 ..
171 } => create_address.0.abi_encode().into(),
172 FrameReturn {
173 apply_state: true,
174 return_data,
175 create_address: None,
176 ..
177 } => return_data.to_vec().abi_encode().into(),
178 FrameReturn {
179 apply_state: false,
180 return_data,
181 ..
182 } => return_data,
183 };
184
185 Box::new(PassResult {
186 params: self.params,
187 apply_state,
188 result: Ok(GasLeft::NeedsReturn {
189 gas_left,
190 data,
191 apply_state,
192 }),
193 wait_return_log: self.wait_return_log,
194 })
195 }
196}
197
198pub struct PassResult {
199 params: ActionParams,
200 result: vm::Result<GasLeft>,
201 apply_state: bool,
202 wait_return_log: bool,
203}
204
205impl Executable for PassResult {
206 fn execute(
207 self: Box<Self>, mut context: Context,
208 ) -> DbResult<ExecutableOutcome> {
209 if self.wait_return_log {
210 ReturnEvent::log(
211 &(),
212 &self.apply_state,
213 &self.params,
214 &mut context.internal_ref(),
215 )
216 .expect("Must have no static flag");
217 }
218
219 let result = self
220 .result
221 .and_then(|r| r.charge_return_data_gas(context.spec()))
222 .finalize(context);
223 Ok(ExecutableOutcome::Return(result))
224 }
225}
226
227pub fn process_trap<T>(
228 result: vm::Result<(ActionParams, Box<dyn Resumable>)>,
229 _phantom: PhantomData<T>,
230) -> InternalTrapResult<T> {
231 match result {
232 Ok((p, r)) => InternalTrapResult::Invoke(p, r),
233 Err(err) => InternalTrapResult::Return(Err(err)),
234 }
235}
236
237pub fn call_to_evmcore(
238 receiver: Address, data: Vec<u8>, call_type: CallType,
239 params: &ActionParams, gas_left: U256, context: &mut InternalRefContext,
240) -> vm::Result<(ActionParams, Box<dyn Resumable>)> {
241 if context.depth >= context.spec.max_depth {
242 internal_bail!("Exceed Depth");
243 }
244
245 let value = params.value.value();
246
247 let call_gas = gas_left / CROSS_SPACE_GAS_RATIO
248 + if value > U256::zero() {
249 U256::from(context.spec.call_stipend)
250 } else {
251 U256::zero()
252 };
253 let reserved_gas = gas_left - gas_left / CROSS_SPACE_GAS_RATIO;
254
255 let mapped_sender = params.sender.evm_map();
256 let mapped_origin = params.original_sender.evm_map();
257
258 context.state.transfer_balance(
259 ¶ms.address.with_native_space(),
260 &mapped_sender,
261 &value,
262 )?;
263 context.state.add_total_evm_tokens(value);
264 context.tracer.trace_internal_transfer(
265 AddressPocket::Balance(params.address.with_native_space()),
266 AddressPocket::Balance(mapped_sender),
267 params.value.value(),
268 );
269
270 let address = receiver.with_evm_space();
271
272 let code = context.state.code(&address)?;
273 let code_hash = context.state.code_hash(&address)?;
274
275 let next_params = ActionParams {
276 space: Space::Ethereum,
277 sender: mapped_sender.address,
278 address: address.address,
279 value: ActionValue::Transfer(value),
280 code_address: address.address,
281 original_sender: mapped_origin.address,
282 storage_owner: mapped_sender.address,
283 gas: call_gas,
284 gas_price: params.gas_price,
285 code,
286 code_hash,
287 data: Some(data.clone()),
288 call_type,
289 create_type: CreateType::None,
290 params_type: vm::ParamsType::Separate,
291 };
292
293 let mut wait_return_log = false;
294
295 if call_type == CallType::Call {
296 let nonce = context.state.nonce(&mapped_sender)?;
297 context.state.inc_nonce(&mapped_sender)?;
298 CallEvent::log(
299 &(mapped_sender.address.0, address.address.0),
300 &(value, nonce, data),
301 params,
302 context,
303 )?;
304 wait_return_log = true;
305 }
306
307 Ok((
308 next_params,
309 Box::new(Resume {
310 params: params.clone(),
311 gas_retained: reserved_gas,
312 wait_return_log,
313 }),
314 ))
315}
316
317pub fn create_to_evmcore(
318 init: Vec<u8>, salt: Option<H256>, params: &ActionParams, gas_left: U256,
319 context: &mut InternalRefContext,
320) -> vm::Result<(ActionParams, Box<dyn Resumable>)> {
321 if context.depth >= context.spec.max_depth {
322 internal_bail!("Exceed Depth");
323 }
324
325 let call_gas = gas_left / CROSS_SPACE_GAS_RATIO
326 + if params.value.value() > U256::zero() {
327 U256::from(context.spec.call_stipend)
328 } else {
329 U256::zero()
330 };
331 let reserved_gas = gas_left - gas_left / CROSS_SPACE_GAS_RATIO;
332
333 let mapped_sender = params.sender.evm_map();
334 let mapped_origin = params.original_sender.evm_map();
335
336 let value = params.value.value();
337 context.state.transfer_balance(
338 ¶ms.address.with_native_space(),
339 &mapped_sender,
340 &value,
341 )?;
342 context.state.add_total_evm_tokens(value);
343 context.tracer.trace_internal_transfer(
344 AddressPocket::Balance(params.address.with_native_space()),
345 AddressPocket::Balance(mapped_sender),
346 params.value.value(),
347 );
348
349 let (address_scheme, create_type) = match salt {
350 None => (CreateContractAddress::FromSenderNonce, CreateType::CREATE),
351 Some(salt) => (
352 CreateContractAddress::FromSenderSaltAndCodeHash(salt),
353 CreateType::CREATE2,
354 ),
355 };
356 let (address_with_space, code_hash) = contract_address(
357 address_scheme,
358 context.env.number.into(),
359 &mapped_sender,
360 &context.state.nonce(&mapped_sender)?,
361 &init,
362 );
363 let address = address_with_space.address;
364
365 let next_params = ActionParams {
366 space: Space::Ethereum,
367 code_address: address,
368 address,
369 sender: mapped_sender.address,
370 original_sender: mapped_origin.address,
371 storage_owner: Address::zero(),
372 gas: call_gas,
373 gas_price: params.gas_price,
374 value: ActionValue::Transfer(value),
375 code: Some(Arc::new(init.clone())),
376 code_hash,
377 data: None,
378 call_type: CallType::None,
379 create_type,
380 params_type: ParamsType::Embedded,
381 };
382
383 let nonce = context.state.nonce(&mapped_sender)?;
384 context.state.inc_nonce(&mapped_sender)?;
385 CreateEvent::log(
386 &(mapped_sender.address.0, address.0),
387 &(value, nonce, init),
388 params,
389 context,
390 )?;
391
392 Ok((
393 next_params,
394 Box::new(Resume {
395 params: params.clone(),
396 gas_retained: reserved_gas,
397 wait_return_log: true,
398 }),
399 ))
400}
401
402pub fn withdraw_from_evmcore(
403 sender: Address, value: U256, params: &ActionParams,
404 context: &mut InternalRefContext,
405) -> vm::Result<()> {
406 let mapped_address = sender.evm_map();
407 let balance = context.state.balance(&mapped_address)?;
408 if balance < value {
409 internal_bail!(
410 "Not enough balance for withdrawing from mapped address"
411 );
412 }
413 context.state.transfer_balance(
414 &mapped_address,
415 &sender.with_native_space(),
416 &value,
417 )?;
418 context.state.sub_total_evm_tokens(value);
419 context.tracer.trace_internal_transfer(
420 AddressPocket::Balance(mapped_address),
421 AddressPocket::Balance(sender.with_native_space()),
422 value,
423 );
424
425 let nonce = context.state.nonce(&mapped_address)?;
426 context.state.inc_nonce(&mapped_address)?;
427 WithdrawEvent::log(
428 &(mapped_address.address.0, sender),
429 &(value, nonce),
430 params,
431 context,
432 )?;
433
434 Ok(())
435}
436
437pub fn mapped_balance(
438 address: Address, context: &mut InternalRefContext,
439) -> vm::Result<U256> {
440 Ok(context.state.balance(&address.evm_map())?)
441}
442
443pub fn mapped_nonce(
444 address: Address, context: &mut InternalRefContext,
445) -> vm::Result<U256> {
446 Ok(context.state.nonce(&address.evm_map())?)
447}