referrerpolicy=no-referrer-when-downgrade

pallet_revive/vm/evm/instructions/
contract.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18mod call_helpers;
19
20use super::{utility::IntoAddress, Context};
21use crate::{
22	vm::{evm::U256Converter, Ext, RuntimeCosts},
23	Pallet,
24};
25use alloc::boxed::Box;
26pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges};
27use revm::{
28	context_interface::CreateScheme,
29	interpreter::{
30		gas as revm_gas,
31		interpreter_action::{
32			CallInputs, CallScheme, CallValue, CreateInputs, FrameInput, InterpreterAction,
33		},
34		interpreter_types::{LoopControl, RuntimeFlag, StackTr},
35		CallInput, InstructionResult,
36	},
37	primitives::{Address, Bytes, B256, U256},
38};
39
40/// Implements the CREATE/CREATE2 instruction.
41///
42/// Creates a new contract with provided bytecode.
43pub fn create<'ext, const IS_CREATE2: bool, E: Ext>(context: Context<'_, 'ext, E>) {
44	require_non_staticcall!(context.interpreter);
45
46	popn!([value, code_offset, len], context.interpreter);
47	let len = as_usize_or_fail!(context.interpreter, len);
48
49	// TODO: We do not charge for the new code in storage. When implementing the new gas:
50	// Introduce EthInstantiateWithCode, which shall charge gas based on the code length.
51	// See #9577 for more context.
52	let val = crate::U256::from_revm_u256(&value);
53	gas!(
54		context.interpreter,
55		RuntimeCosts::Instantiate {
56			input_data_len: len as u32, // We charge for initcode execution
57			balance_transfer: Pallet::<E::T>::has_balance(val),
58			dust_transfer: Pallet::<E::T>::has_dust(val),
59		}
60	);
61
62	let mut code = Bytes::new();
63	if len != 0 {
64		// EIP-3860: Limit initcode
65		if len > revm::primitives::eip3860::MAX_INITCODE_SIZE {
66			context.interpreter.halt(InstructionResult::CreateInitCodeSizeLimit);
67			return;
68		}
69
70		let code_offset = as_usize_or_fail!(context.interpreter, code_offset);
71		resize_memory!(context.interpreter, code_offset, len);
72		code =
73			Bytes::copy_from_slice(context.interpreter.memory.slice_len(code_offset, len).as_ref());
74	}
75
76	// EIP-1014: Skinny CREATE2
77	let scheme = if IS_CREATE2 {
78		popn!([salt], context.interpreter);
79		CreateScheme::Create2 { salt }
80	} else {
81		gas_legacy!(context.interpreter, revm_gas::CREATE);
82		CreateScheme::Create
83	};
84
85	// Call host to interact with target contract
86	context
87		.interpreter
88		.bytecode
89		.set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(CreateInputs {
90			caller: context.interpreter.extend.address().0.into(),
91			scheme,
92			value,
93			init_code: code,
94			gas_limit: u64::MAX, // TODO: set the right limit
95		}))));
96}
97
98/// Implements the CALL instruction.
99///
100/// Message call with value transfer to another account.
101pub fn call<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
102	popn!([local_gas_limit, to, value], context.interpreter);
103	let to = to.into_address();
104	// TODO: Max gas limit is not possible in a real Ethereum situation. This issue will be
105	// addressed in #9577.
106	let _local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
107
108	let has_transfer = !value.is_zero();
109	if context.interpreter.runtime_flag.is_static() && has_transfer {
110		context.interpreter.halt(InstructionResult::CallNotAllowedInsideStatic);
111		return;
112	}
113
114	let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
115	else {
116		return;
117	};
118
119	let scheme = CallScheme::Call;
120	let input = CallInput::SharedBuffer(input);
121
122	let Some(gas_limit) = calc_call_gas(context.interpreter, to, scheme, input.len(), value) else {
123		return;
124	};
125
126	// Call host to interact with target contract
127	context
128		.interpreter
129		.bytecode
130		.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
131			input,
132			gas_limit,
133			target_address: to,
134			caller: Address::default(),
135			bytecode_address: to,
136			value: CallValue::Transfer(value),
137			scheme,
138			is_static: context.interpreter.runtime_flag.is_static(),
139			return_memory_offset,
140		}))));
141}
142
143/// Implements the CALLCODE instruction.
144///
145/// Message call with alternative account's code.
146///
147/// Isn't supported yet: [`solc` no longer emits it since Solidity v0.3.0 in 2016]
148/// (https://soliditylang.org/blog/2016/03/11/solidity-0.3.0-release-announcement/).
149pub fn call_code<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
150	context.interpreter.halt(revm::interpreter::InstructionResult::NotActivated);
151}
152
153/// Implements the DELEGATECALL instruction.
154///
155/// Message call with alternative account's code but same sender and value.
156pub fn delegate_call<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
157	popn!([local_gas_limit, to], context.interpreter);
158	let to = Address::from_word(B256::from(to));
159	// TODO: Max gas limit is not possible in a real Ethereum situation. This issue will be
160	// addressed in #9577.
161	let _local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
162
163	let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
164	else {
165		return;
166	};
167
168	let scheme = CallScheme::DelegateCall;
169	let input = CallInput::SharedBuffer(input);
170
171	let Some(gas_limit) = calc_call_gas(context.interpreter, to, scheme, input.len(), U256::ZERO)
172	else {
173		return;
174	};
175
176	// Call host to interact with target contract
177	context
178		.interpreter
179		.bytecode
180		.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
181			input,
182			gas_limit,
183			target_address: Default::default(),
184			caller: Default::default(),
185			bytecode_address: to,
186			value: CallValue::Apparent(Default::default()),
187			scheme,
188			is_static: context.interpreter.runtime_flag.is_static(),
189			return_memory_offset,
190		}))));
191}
192
193/// Implements the STATICCALL instruction.
194///
195/// Static message call (cannot modify state).
196pub fn static_call<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
197	popn!([local_gas_limit, to], context.interpreter);
198	let to = Address::from_word(B256::from(to));
199	// TODO: Max gas limit is not possible in a real Ethereum situation. This issue will be
200	// addressed in #9577.
201	let _local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
202
203	let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
204	else {
205		return;
206	};
207
208	let scheme = CallScheme::StaticCall;
209	let input = CallInput::SharedBuffer(input);
210
211	let Some(gas_limit) = calc_call_gas(context.interpreter, to, scheme, input.len(), U256::ZERO)
212	else {
213		return;
214	};
215
216	// Call host to interact with target contract
217	context
218		.interpreter
219		.bytecode
220		.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
221			input,
222			gas_limit,
223			target_address: to,
224			caller: Default::default(),
225			bytecode_address: to,
226			value: CallValue::Transfer(U256::ZERO),
227			scheme,
228			is_static: true,
229			return_memory_offset,
230		}))));
231}