referrerpolicy=no-referrer-when-downgrade

pallet_revive/vm/evm/instructions/
host.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
18use super::Context;
19
20use crate::{
21	storage::WriteOutcome,
22	vec::Vec,
23	vm::{evm::U256Converter, Ext},
24	DispatchError, Key, RuntimeCosts,
25};
26use revm::{
27	interpreter::{interpreter_types::StackTr, InstructionResult},
28	primitives::{Bytes, U256},
29};
30
31/// Implements the BALANCE instruction.
32///
33/// Gets the balance of the given account.
34pub fn balance<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
35	gas!(context.interpreter, RuntimeCosts::BalanceOf);
36	popn_top!([], top, context.interpreter);
37	let h160 = sp_core::H160::from_slice(&top.to_be_bytes::<32>()[12..]);
38	*top = context.interpreter.extend.balance_of(&h160).into_revm_u256();
39}
40
41/// EIP-1884: Repricing for trie-size-dependent opcodes
42pub fn selfbalance<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
43	gas!(context.interpreter, RuntimeCosts::Balance);
44	let balance = context.interpreter.extend.balance();
45	push!(context.interpreter, balance.into_revm_u256());
46}
47
48/// Implements the EXTCODESIZE instruction.
49///
50/// Gets the size of an account's code.
51pub fn extcodesize<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
52	popn_top!([], top, context.interpreter);
53	gas!(context.interpreter, RuntimeCosts::CodeSize);
54	let h160 = sp_core::H160::from_slice(&top.to_be_bytes::<32>()[12..]);
55	let code_size = context.interpreter.extend.code_size(&h160);
56	*top = U256::from(code_size);
57}
58
59/// EIP-1052: EXTCODEHASH opcode
60pub fn extcodehash<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
61	popn_top!([], top, context.interpreter);
62	gas!(context.interpreter, RuntimeCosts::CodeHash);
63	let h160 = sp_core::H160::from_slice(&top.to_be_bytes::<32>()[12..]);
64	let code_hash = context.interpreter.extend.code_hash(&h160);
65	*top = U256::from_be_bytes(code_hash.0);
66}
67
68/// Implements the EXTCODECOPY instruction.
69///
70/// Copies a portion of an account's code to memory.
71pub fn extcodecopy<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
72	popn!([address, memory_offset, code_offset, len_u256], context.interpreter);
73	let len = as_usize_or_fail!(context.interpreter, len_u256);
74
75	gas!(context.interpreter, RuntimeCosts::ExtCodeCopy(len as u32));
76	let address = sp_core::H160::from_slice(&address.to_be_bytes::<32>()[12..]);
77
78	if len == 0 {
79		return;
80	}
81	let memory_offset = as_usize_or_fail!(context.interpreter, memory_offset);
82	let code_offset = as_usize_saturated!(code_offset);
83
84	resize_memory!(context.interpreter, memory_offset, len);
85
86	let mut buf = context.interpreter.memory.slice_mut(memory_offset, len);
87	// Note: This can't panic because we resized memory to fit.
88	context.interpreter.extend.copy_code_slice(&mut buf, &address, code_offset);
89}
90
91/// Implements the BLOCKHASH instruction.
92///
93/// Gets the hash of one of the 256 most recent complete blocks.
94pub fn blockhash<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
95	gas!(context.interpreter, RuntimeCosts::BlockHash);
96	popn_top!([], number, context.interpreter);
97	let requested_number = <sp_core::U256 as U256Converter>::from_revm_u256(&number);
98
99	// blockhash should push zero if number is not within valid range.
100	if let Some(hash) = context.interpreter.extend.block_hash(requested_number) {
101		*number = U256::from_be_bytes(hash.0)
102	} else {
103		*number = U256::ZERO
104	};
105}
106
107/// Implements the SLOAD instruction.
108///
109/// Loads a word from storage.
110pub fn sload<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
111	popn_top!([], index, context.interpreter);
112	// NB: SLOAD loads 32 bytes from storage (i.e. U256).
113	gas!(context.interpreter, RuntimeCosts::GetStorage(32));
114	let key = Key::Fix(index.to_be_bytes());
115	let value = context.interpreter.extend.get_storage(&key);
116
117	*index = if let Some(storage_value) = value {
118		// sload always reads a word
119		let Ok::<[u8; 32], _>(bytes) = storage_value.try_into() else {
120			context.interpreter.halt(InstructionResult::FatalExternalError);
121			return
122		};
123		U256::from_be_bytes(bytes)
124	} else {
125		// the key was never written before
126		U256::ZERO
127	};
128}
129
130fn store_helper<'ext, E: Ext>(
131	context: Context<'_, 'ext, E>,
132	cost_before: RuntimeCosts,
133	set_function: fn(&mut E, &Key, Option<Vec<u8>>, bool) -> Result<WriteOutcome, DispatchError>,
134	adjust_cost: fn(new_bytes: u32, old_bytes: u32) -> RuntimeCosts,
135) {
136	if context.interpreter.extend.is_read_only() {
137		context.interpreter.halt(InstructionResult::Revert);
138		return;
139	}
140
141	popn!([index, value], context.interpreter);
142
143	// Charge gas before set_storage and later adjust it down to the true gas cost
144	let Ok(charged_amount) = context.interpreter.extend.gas_meter_mut().charge(cost_before) else {
145		context.interpreter.halt(InstructionResult::OutOfGas);
146		return;
147	};
148
149	let key = Key::Fix(index.to_be_bytes());
150	let take_old = false;
151	let Ok(write_outcome) = set_function(
152		context.interpreter.extend,
153		&key,
154		Some(value.to_be_bytes::<32>().to_vec()),
155		take_old,
156	) else {
157		context.interpreter.halt(InstructionResult::FatalExternalError);
158		return;
159	};
160
161	context
162		.interpreter
163		.extend
164		.gas_meter_mut()
165		.adjust_gas(charged_amount, adjust_cost(32, write_outcome.old_len()));
166}
167
168/// Implements the SSTORE instruction.
169///
170/// Stores a word to storage.
171pub fn sstore<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
172	store_helper(
173		context,
174		RuntimeCosts::SetStorage { new_bytes: 32, old_bytes: 0 },
175		|ext, key, value, take_old| ext.set_storage(key, value, take_old),
176		|new_bytes, old_bytes| RuntimeCosts::SetStorage { new_bytes, old_bytes },
177	);
178}
179
180/// EIP-1153: Transient storage opcodes
181/// Store value to transient storage
182pub fn tstore<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
183	store_helper(
184		context,
185		RuntimeCosts::SetTransientStorage { new_bytes: 32, old_bytes: 0 },
186		|ext, key, value, take_old| ext.set_transient_storage(key, value, take_old),
187		|new_bytes, old_bytes| RuntimeCosts::SetTransientStorage { new_bytes, old_bytes },
188	);
189}
190
191/// EIP-1153: Transient storage opcodes
192/// Load value from transient storage
193pub fn tload<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
194	popn_top!([], index, context.interpreter);
195	gas!(context.interpreter, RuntimeCosts::GetTransientStorage(32));
196
197	let key = Key::Fix(index.to_be_bytes());
198	let bytes = context.interpreter.extend.get_transient_storage(&key);
199	*index = if let Some(storage_value) = bytes {
200		if storage_value.len() != 32 {
201			// tload always reads a word
202			context.interpreter.halt(InstructionResult::FatalExternalError);
203			return;
204		}
205		let mut bytes = [0u8; 32];
206		bytes.copy_from_slice(&storage_value);
207		U256::from_be_bytes(bytes)
208	} else {
209		// the key was never written before
210		U256::ZERO
211	};
212}
213
214/// Implements the LOG0-LOG4 instructions.
215///
216/// Appends log record with N topics.
217pub fn log<'ext, const N: usize, E: Ext>(context: Context<'_, 'ext, E>) {
218	if context.interpreter.extend.is_read_only() {
219		context.interpreter.halt(InstructionResult::Revert);
220		return;
221	}
222
223	popn!([offset, len], context.interpreter);
224	let len = as_usize_or_fail!(context.interpreter, len);
225	if len as u32 > context.interpreter.extend.max_value_size() {
226		context
227			.interpreter
228			.halt(revm::interpreter::InstructionResult::InvalidOperandOOG);
229		return;
230	}
231
232	gas!(context.interpreter, RuntimeCosts::DepositEvent { num_topic: N as u32, len: len as u32 });
233	let data = if len == 0 {
234		Bytes::new()
235	} else {
236		let offset = as_usize_or_fail!(context.interpreter, offset);
237		resize_memory!(context.interpreter, offset, len);
238		Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref())
239	};
240	if context.interpreter.stack.len() < N {
241		context.interpreter.halt(InstructionResult::StackUnderflow);
242		return;
243	}
244	let Some(topics) = <_ as StackTr>::popn::<N>(&mut context.interpreter.stack) else {
245		context.interpreter.halt(InstructionResult::StackUnderflow);
246		return;
247	};
248
249	let topics = topics.into_iter().map(|v| sp_core::H256::from(v.to_be_bytes())).collect();
250
251	context.interpreter.extend.deposit_event(topics, data.to_vec());
252}
253
254/// Implements the SELFDESTRUCT instruction.
255///
256/// Halt execution and register account for later deletion.
257pub fn selfdestruct<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
258	// TODO: for now this instruction is not supported
259	context.interpreter.halt(InstructionResult::NotActivated);
260}