rtvm_interpreter/instructions/
host.rs

1use 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            // EIP-1884: Repricing for trie-size-dependent opcodes
23            700
24        } else if SPEC::enabled(TANGERINE) {
25            400
26        } else {
27            20
28        }
29    );
30    push!(interpreter, balance);
31}
32
33/// EIP-1884: Repricing for trie-size-dependent opcodes
34pub 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
61/// EIP-1052: EXTCODEHASH opcode
62pub 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    // Note: this can't panic because we resized memory to fit.
101    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        // blockhash should push zero if number is same as current block number.
113        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
159/// EIP-1153: Transient storage opcodes
160/// Store value to transient storage
161pub 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
171/// EIP-1153: Transient storage opcodes
172/// Load value from transient storage
173pub 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        // SAFETY: stack bounds already checked few lines above
204        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    // EIP-3529: Reduction in refunds
225    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}