rtvm_interpreter/instructions/
host.rs1use crate::{
2 gas::{self, warm_cold_cost},
3 interpreter::Interpreter,
4 primitives::{Bytes, Log, LogData, Spec, SpecId::*, B256, U256},
5 Host, InstructionResult, SStoreResult,
6};
7use core::cmp::min;
8use rtvm_primitives::BLOCK_HASH_HISTORY;
9use std::vec::Vec;
10
11pub fn balance<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
12 pop_address!(interpreter, address);
13 let Some((balance, is_cold)) = host.balance(address) else {
14 interpreter.instruction_result = InstructionResult::FatalExternalError;
15 return;
16 };
17 gas!(
18 interpreter,
19 if SPEC::enabled(BERLIN) {
20 warm_cold_cost(is_cold)
21 } else if SPEC::enabled(ISTANBUL) {
22 700
24 } else if SPEC::enabled(TANGERINE) {
25 400
26 } else {
27 20
28 }
29 );
30 push!(interpreter, balance);
31}
32
33pub fn selfbalance<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
35 check!(interpreter, ISTANBUL);
36 gas!(interpreter, gas::LOW);
37 let Some((balance, _)) = host.balance(interpreter.contract.target_address) else {
38 interpreter.instruction_result = InstructionResult::FatalExternalError;
39 return;
40 };
41 push!(interpreter, balance);
42}
43
44pub fn extcodesize<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
45 pop_address!(interpreter, address);
46 let Some((code, is_cold)) = host.code(address) else {
47 interpreter.instruction_result = InstructionResult::FatalExternalError;
48 return;
49 };
50 if SPEC::enabled(BERLIN) {
51 gas!(interpreter, warm_cold_cost(is_cold));
52 } else if SPEC::enabled(TANGERINE) {
53 gas!(interpreter, 700);
54 } else {
55 gas!(interpreter, 20);
56 }
57
58 push!(interpreter, U256::from(code.len()));
59}
60
61pub fn extcodehash<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
63 check!(interpreter, CONSTANTINOPLE);
64 pop_address!(interpreter, address);
65 let Some((code_hash, is_cold)) = host.code_hash(address) else {
66 interpreter.instruction_result = InstructionResult::FatalExternalError;
67 return;
68 };
69 if SPEC::enabled(BERLIN) {
70 gas!(interpreter, warm_cold_cost(is_cold));
71 } else if SPEC::enabled(ISTANBUL) {
72 gas!(interpreter, 700);
73 } else {
74 gas!(interpreter, 400);
75 }
76 push_b256!(interpreter, code_hash);
77}
78
79pub fn extcodecopy<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
80 pop_address!(interpreter, address);
81 pop!(interpreter, memory_offset, code_offset, len_u256);
82
83 let Some((code, is_cold)) = host.code(address) else {
84 interpreter.instruction_result = InstructionResult::FatalExternalError;
85 return;
86 };
87
88 let len = as_usize_or_fail!(interpreter, len_u256);
89 gas_or_fail!(
90 interpreter,
91 gas::extcodecopy_cost(SPEC::SPEC_ID, len as u64, is_cold)
92 );
93 if len == 0 {
94 return;
95 }
96 let memory_offset = as_usize_or_fail!(interpreter, memory_offset);
97 let code_offset = min(as_usize_saturated!(code_offset), code.len());
98 resize_memory!(interpreter, memory_offset, len);
99
100 interpreter
102 .shared_memory
103 .set_data(memory_offset, code_offset, len, &code.original_bytes());
104}
105
106pub fn blockhash<H: Host + ?Sized>(interpreter: &mut Interpreter, host: &mut H) {
107 gas!(interpreter, gas::BLOCKHASH);
108 pop_top!(interpreter, number);
109
110 if let Some(diff) = host.env().block.number.checked_sub(*number) {
111 let diff = as_usize_saturated!(diff);
112 if diff <= BLOCK_HASH_HISTORY && diff != 0 {
114 let Some(hash) = host.block_hash(*number) else {
115 interpreter.instruction_result = InstructionResult::FatalExternalError;
116 return;
117 };
118 *number = U256::from_be_bytes(hash.0);
119 return;
120 }
121 }
122 *number = U256::ZERO;
123}
124
125pub fn sload<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
126 pop_top!(interpreter, index);
127 let Some((value, is_cold)) = host.sload(interpreter.contract.target_address, *index) else {
128 interpreter.instruction_result = InstructionResult::FatalExternalError;
129 return;
130 };
131 gas!(interpreter, gas::sload_cost(SPEC::SPEC_ID, is_cold));
132 *index = value;
133}
134
135pub fn sstore<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
136 require_non_staticcall!(interpreter);
137
138 pop!(interpreter, index, value);
139 let Some(SStoreResult {
140 original_value: original,
141 present_value: old,
142 new_value: new,
143 is_cold,
144 }) = host.sstore(interpreter.contract.target_address, index, value)
145 else {
146 interpreter.instruction_result = InstructionResult::FatalExternalError;
147 return;
148 };
149 gas_or_fail!(interpreter, {
150 let remaining_gas = interpreter.gas.remaining();
151 gas::sstore_cost(SPEC::SPEC_ID, original, old, new, remaining_gas, is_cold)
152 });
153 refund!(
154 interpreter,
155 gas::sstore_refund(SPEC::SPEC_ID, original, old, new)
156 );
157}
158
159pub fn tstore<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
162 check!(interpreter, CANCUN);
163 require_non_staticcall!(interpreter);
164 gas!(interpreter, gas::WARM_STORAGE_READ_COST);
165
166 pop!(interpreter, index, value);
167
168 host.tstore(interpreter.contract.target_address, index, value);
169}
170
171pub fn tload<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
174 check!(interpreter, CANCUN);
175 gas!(interpreter, gas::WARM_STORAGE_READ_COST);
176
177 pop_top!(interpreter, index);
178
179 *index = host.tload(interpreter.contract.target_address, *index);
180}
181
182pub fn log<const N: usize, H: Host + ?Sized>(interpreter: &mut Interpreter, host: &mut H) {
183 require_non_staticcall!(interpreter);
184
185 pop!(interpreter, offset, len);
186 let len = as_usize_or_fail!(interpreter, len);
187 gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64));
188 let data = if len == 0 {
189 Bytes::new()
190 } else {
191 let offset = as_usize_or_fail!(interpreter, offset);
192 resize_memory!(interpreter, offset, len);
193 Bytes::copy_from_slice(interpreter.shared_memory.slice(offset, len))
194 };
195
196 if interpreter.stack.len() < N {
197 interpreter.instruction_result = InstructionResult::StackUnderflow;
198 return;
199 }
200
201 let mut topics = Vec::with_capacity(N);
202 for _ in 0..N {
203 topics.push(B256::from(unsafe { interpreter.stack.pop_unsafe() }));
205 }
206
207 let log = Log {
208 address: interpreter.contract.target_address,
209 data: LogData::new(topics, data).expect("LogData should have <=4 topics"),
210 };
211
212 host.log(log);
213}
214
215pub fn selfdestruct<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
216 require_non_staticcall!(interpreter);
217 pop_address!(interpreter, target);
218
219 let Some(res) = host.selfdestruct(interpreter.contract.target_address, target) else {
220 interpreter.instruction_result = InstructionResult::FatalExternalError;
221 return;
222 };
223
224 if !SPEC::enabled(LONDON) && !res.previously_destroyed {
226 refund!(interpreter, gas::SELFDESTRUCT)
227 }
228 gas!(interpreter, gas::selfdestruct_cost(SPEC::SPEC_ID, res));
229
230 interpreter.instruction_result = InstructionResult::SelfDestruct;
231}