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	if context.interpreter.extend.is_read_only() {
45		context.interpreter.halt(InstructionResult::Revert);
46		return;
47	}
48
49	popn!([value, code_offset, len], context.interpreter);
50	let len = as_usize_or_fail!(context.interpreter, len);
51
52	// TODO: We do not charge for the new code in storage. When implementing the new gas:
53	// Introduce EthInstantiateWithCode, which shall charge gas based on the code length.
54	// See #9577 for more context.
55	let val = crate::U256::from_revm_u256(&value);
56	gas!(
57		context.interpreter,
58		RuntimeCosts::Instantiate {
59			input_data_len: len as u32, // We charge for initcode execution
60			balance_transfer: Pallet::<E::T>::has_balance(val),
61			dust_transfer: Pallet::<E::T>::has_dust(val),
62		}
63	);
64
65	let mut code = Bytes::new();
66	if len != 0 {
67		// EIP-3860: Limit initcode
68		if len > revm::primitives::eip3860::MAX_INITCODE_SIZE {
69			context.interpreter.halt(InstructionResult::CreateInitCodeSizeLimit);
70			return;
71		}
72
73		let code_offset = as_usize_or_fail!(context.interpreter, code_offset);
74		resize_memory!(context.interpreter, code_offset, len);
75		code =
76			Bytes::copy_from_slice(context.interpreter.memory.slice_len(code_offset, len).as_ref());
77	}
78
79	// EIP-1014: Skinny CREATE2
80	let scheme = if IS_CREATE2 {
81		popn!([salt], context.interpreter);
82		CreateScheme::Create2 { salt }
83	} else {
84		gas_legacy!(context.interpreter, revm_gas::CREATE);
85		CreateScheme::Create
86	};
87
88	// Call host to interact with target contract
89	context
90		.interpreter
91		.bytecode
92		.set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new(CreateInputs {
93			caller: context.interpreter.extend.address().0.into(),
94			scheme,
95			value,
96			init_code: code,
97			gas_limit: u64::MAX, // TODO: set the right limit
98		}))));
99}
100
101/// Implements the CALL instruction.
102///
103/// Message call with value transfer to another account.
104pub fn call<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
105	popn!([local_gas_limit, to, value], context.interpreter);
106	let to = to.into_address();
107	// TODO: Max gas limit is not possible in a real Ethereum situation. This issue will be
108	// addressed in #9577.
109	let _local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
110
111	let has_transfer = !value.is_zero();
112	if context.interpreter.runtime_flag.is_static() && has_transfer {
113		context.interpreter.halt(InstructionResult::CallNotAllowedInsideStatic);
114		return;
115	}
116
117	let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
118	else {
119		return;
120	};
121
122	let scheme = CallScheme::Call;
123	let input = CallInput::SharedBuffer(input);
124
125	let Some(gas_limit) = calc_call_gas(context.interpreter, to, scheme, input.len(), value) else {
126		return;
127	};
128
129	// Call host to interact with target contract
130	context
131		.interpreter
132		.bytecode
133		.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
134			input,
135			gas_limit,
136			target_address: to,
137			caller: Address::default(),
138			bytecode_address: to,
139			value: CallValue::Transfer(value),
140			scheme,
141			is_static: context.interpreter.runtime_flag.is_static(),
142			return_memory_offset,
143		}))));
144}
145
146/// Implements the CALLCODE instruction.
147///
148/// Message call with alternative account's code.
149///
150/// Isn't supported yet: [`solc` no longer emits it since Solidity v0.3.0 in 2016]
151/// (https://soliditylang.org/blog/2016/03/11/solidity-0.3.0-release-announcement/).
152pub fn call_code<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
153	context.interpreter.halt(revm::interpreter::InstructionResult::NotActivated);
154}
155
156/// Implements the DELEGATECALL instruction.
157///
158/// Message call with alternative account's code but same sender and value.
159pub fn delegate_call<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
160	popn!([local_gas_limit, to], context.interpreter);
161	let to = Address::from_word(B256::from(to));
162	// TODO: Max gas limit is not possible in a real Ethereum situation. This issue will be
163	// addressed in #9577.
164	let _local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
165
166	let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
167	else {
168		return;
169	};
170
171	let scheme = CallScheme::DelegateCall;
172	let input = CallInput::SharedBuffer(input);
173
174	let Some(gas_limit) = calc_call_gas(context.interpreter, to, scheme, input.len(), U256::ZERO)
175	else {
176		return;
177	};
178
179	// Call host to interact with target contract
180	context
181		.interpreter
182		.bytecode
183		.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
184			input,
185			gas_limit,
186			target_address: Default::default(),
187			caller: Default::default(),
188			bytecode_address: to,
189			value: CallValue::Apparent(Default::default()),
190			scheme,
191			is_static: context.interpreter.runtime_flag.is_static(),
192			return_memory_offset,
193		}))));
194}
195
196/// Implements the STATICCALL instruction.
197///
198/// Static message call (cannot modify state).
199pub fn static_call<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
200	popn!([local_gas_limit, to], context.interpreter);
201	let to = Address::from_word(B256::from(to));
202	// TODO: Max gas limit is not possible in a real Ethereum situation. This issue will be
203	// addressed in #9577.
204	let _local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
205
206	let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter)
207	else {
208		return;
209	};
210
211	let scheme = CallScheme::StaticCall;
212	let input = CallInput::SharedBuffer(input);
213
214	let Some(gas_limit) = calc_call_gas(context.interpreter, to, scheme, input.len(), U256::ZERO)
215	else {
216		return;
217	};
218
219	// Call host to interact with target contract
220	context
221		.interpreter
222		.bytecode
223		.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs {
224			input,
225			gas_limit,
226			target_address: to,
227			caller: Default::default(),
228			bytecode_address: to,
229			value: CallValue::Transfer(U256::ZERO),
230			scheme,
231			is_static: true,
232			return_memory_offset,
233		}))));
234}