Skip to main content

yevm_core/ops/
chain.rs

1use yevm_base::{Acc, Int};
2
3use crate::{
4    Call,
5    evm::{Context, Evm, EvmResult, EvmYield, Fetch, HaltReason, mem_check_int},
6    state::State,
7};
8
9pub fn address(evm: &mut Evm, ctx: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
10    evm.gas_charge(2)?;
11    let r = ctx.this.to();
12    evm.push(r)?;
13    Ok(())
14}
15
16pub fn balance<S: State>(evm: &mut Evm, _: &Context, _: &Call, state: &mut S) -> EvmResult<()> {
17    evm.gas_charge(100)?;
18    let [acc] = evm.peek()?;
19    let acc: Acc = acc.to();
20    let Some(balance) = state.balance(&acc) else {
21        return Err(EvmYield::Fetch(Fetch::Balance(acc)));
22    };
23
24    if state.is_cold_acc(&acc) {
25        evm.warm_acc(&acc);
26        evm.gas_charge(2_500)?;
27    }
28    evm.push(balance)?;
29    if let Some(step) = evm.step.as_mut() {
30        step.debug.push(format!("BALANCE: {balance:?}"));
31    }
32    Ok(())
33}
34
35pub fn origin(evm: &mut Evm, ctx: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
36    evm.gas_charge(2)?;
37    evm.push(ctx.origin.to())?;
38    Ok(())
39}
40
41pub fn caller(evm: &mut Evm, _: &Context, call: &Call, _: &mut dyn State) -> EvmResult<()> {
42    evm.gas_charge(2)?;
43    let caller = call.by;
44    evm.push(caller.to())?;
45    if let Some(step) = evm.step.as_mut() {
46        step.debug.push(format!("CALLER: {caller:?}"));
47    }
48    Ok(())
49}
50
51pub fn callvalue(evm: &mut Evm, _: &Context, call: &Call, _: &mut dyn State) -> EvmResult<()> {
52    evm.gas_charge(2)?;
53    evm.push(call.eth)?;
54    Ok(())
55}
56
57pub fn calldataload(evm: &mut Evm, _: &Context, call: &Call, _: &mut dyn State) -> EvmResult<()> {
58    evm.gas_charge(3)?;
59    let [offset] = evm.peek_usize()?;
60    let mut word = [0u8; 32];
61    if offset < call.data.0.len() {
62        let copy = (call.data.0.len() - offset).min(32);
63        word[..copy].copy_from_slice(&call.data.0[offset..offset + copy]);
64    }
65    evm.push(yevm_base::Int::from(&word[..]))?;
66    Ok(())
67}
68
69pub fn calldatasize(evm: &mut Evm, _: &Context, call: &Call, _: &mut dyn State) -> EvmResult<()> {
70    evm.gas_charge(2)?;
71    evm.push(call.data.0.len().into())?;
72    Ok(())
73}
74
75pub fn calldatacopy(evm: &mut Evm, _: &Context, call: &Call, _: &mut dyn State) -> EvmResult<()> {
76    evm.gas_charge(3)?;
77    let [dest_offset, offset, size] = evm.peek::<3>()?;
78    mem_check_int(dest_offset, size)?;
79    let (dest_offset, size) = (dest_offset.as_usize(), size.as_usize());
80    evm.mem_expand(dest_offset, size)?;
81    evm.gas_charge(3 * size.div_ceil(32) as i64)?;
82    let data_len = call.data.0.len();
83    let (lo, hi) = if offset >= Int::from(data_len) {
84        (data_len, data_len)
85    } else {
86        let o = offset.as_usize();
87        let h = o.saturating_add(size).min(data_len);
88        (o.min(data_len), h)
89    };
90    let len = (lo..hi).len();
91    if len > 0 {
92        let data = &call.data.0[lo..hi];
93        evm.mem_put(dest_offset, len, data)?;
94    }
95    if len < size {
96        let pad = size - (lo..hi).len();
97        let data = vec![0; pad];
98        evm.mem_put(dest_offset + len, size - len, &data)?;
99    }
100    Ok(())
101}
102
103pub fn codesize(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
104    evm.gas_charge(2)?;
105    let size = evm.code.len();
106    evm.push(size.into())?;
107    Ok(())
108}
109
110pub fn codecopy(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
111    evm.gas_charge(3)?;
112    let [dest_offset, offset, size] = evm.peek::<3>()?;
113    mem_check_int(dest_offset, size)?;
114    let (dest_offset, size) = (dest_offset.as_usize(), size.as_usize());
115    evm.mem_expand(dest_offset, size)?;
116    evm.gas_charge(3 * size.div_ceil(32) as i64)?;
117    let code_len = evm.code.len();
118    // Use full 256-bit comparison: offset >= code_len means no overlap (avoids truncation bug)
119    let (lo, hi) = if offset >= Int::from(code_len) {
120        (code_len, code_len)
121    } else {
122        let o = offset.as_usize();
123        let h = o.saturating_add(size).min(code_len);
124        (o.min(code_len), h)
125    };
126    let len = (lo..hi).len();
127    let mut ret = vec![0u8; size];
128    ret[0..len].copy_from_slice(&evm.code[lo..hi]);
129    evm.mem_put(dest_offset, size, &ret)?;
130    Ok(())
131}
132
133pub fn gasprice(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
134    evm.gas_charge(2)?;
135    evm.push(evm.gas_price)?;
136    Ok(())
137}
138
139pub fn extcodesize<S: State>(evm: &mut Evm, _: &Context, _: &Call, state: &mut S) -> EvmResult<()> {
140    evm.gas_charge(100)?;
141    let [acc] = evm.peek()?;
142    let acc: Acc = acc.to();
143    let Some((code, _)) = state.code(&acc) else {
144        return Err(EvmYield::Fetch(Fetch::Code(acc)));
145    };
146    if state.is_cold_acc(&acc) {
147        evm.warm_acc(&acc);
148        evm.gas_charge(2_500)?;
149    }
150    evm.push(code.0.len().into())?;
151    Ok(())
152}
153
154pub fn extcodecopy<S: State>(evm: &mut Evm, _: &Context, _: &Call, state: &mut S) -> EvmResult<()> {
155    evm.gas_charge(100)?;
156    let [acc, dest_offset, offset, size] = evm.peek()?;
157    let acc: Acc = acc.to();
158    mem_check_int(dest_offset, size)?;
159    let (dest_offset, size) = (dest_offset.as_usize(), size.as_usize());
160
161    let Some((code, _)) = state.code(&acc) else {
162        return Err(EvmYield::Fetch(Fetch::Code(acc)));
163    };
164    if state.is_cold_acc(&acc) {
165        evm.warm_acc(&acc);
166        evm.gas_charge(2_500)?;
167    }
168    evm.mem_expand(dest_offset, size)?;
169    evm.gas_charge(3 * size.div_ceil(32) as i64)?;
170
171    let code_len = code.0.len();
172    let (lo, hi) = if offset >= Int::from(code_len) {
173        (code_len, code_len)
174    } else {
175        let o = offset.as_usize();
176        let h = o.saturating_add(size).min(code_len);
177        (o.min(code_len), h)
178    };
179    let len = (lo..hi).len();
180    if len > 0 {
181        let data = &code.0[lo..hi];
182        evm.mem_put(dest_offset, len, data)?;
183    }
184    if len < size {
185        let pad = size - (lo..hi).len();
186        let data = vec![0; pad];
187        evm.mem_put(dest_offset + len, size - len, &data)?;
188    }
189    Ok(())
190}
191
192pub fn returndatasize(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
193    evm.gas_charge(2)?;
194    evm.push(evm.ret.len().into())?;
195    Ok(())
196}
197
198pub fn returndatacopy(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
199    evm.gas_charge(3)?;
200    let [dest_offset, offset, size] = evm.peek::<3>()?;
201    mem_check_int(offset, size)?;
202    mem_check_int(dest_offset, size)?;
203    // EVM spec: halt if copy range exceeds return data buffer (256-bit check)
204    // Fast path: when offset+size fits in usize and <= ret.len(), no need for 256-bit.
205    // Otherwise use 256-bit comparison (avoids BadCopyRange when offset/size truncate).
206    let ret_len = evm.ret.len();
207    let ret_len_int = Int::from(ret_len);
208    let (offset_u, size_u) = (offset.as_usize(), size.as_usize());
209    let ok_fast = offset_u
210        .checked_add(size_u)
211        .map(|end| end <= ret_len)
212        .unwrap_or(false);
213    if !ok_fast {
214        let add = yevm_base::math::lift(|[a, b]| a + b);
215        let gt = yevm_base::math::lift(|[a, b]| {
216            if a > b {
217                yevm_base::math::U256::ONE
218            } else {
219                yevm_base::math::U256::ZERO
220            }
221        });
222        let end = add([offset, size]);
223        if !gt([end, ret_len_int]).is_zero() {
224            return Err(EvmYield::Halt(HaltReason::BadCopyRange));
225        }
226    }
227    let (dest_offset, offset, size) = (dest_offset.as_usize(), offset.as_usize(), size.as_usize());
228    evm.gas_charge(3 * size.div_ceil(32) as i64)?;
229
230    let ret_len = evm.ret.len();
231    let (lo, copy_len) = if offset >= ret_len {
232        (ret_len, 0)
233    } else {
234        let copy_len = size.min(ret_len - offset);
235        (offset, copy_len)
236    };
237    if copy_len > 0 {
238        let data = evm.ret[lo..lo + copy_len].to_vec();
239        evm.mem_put(dest_offset, copy_len, &data)?;
240    }
241    if copy_len < size {
242        let pad = size - copy_len;
243        let data = vec![0; pad];
244        evm.mem_put(dest_offset + copy_len, pad, &data)?;
245    }
246    Ok(())
247}
248
249pub fn extcodehash<S: State>(evm: &mut Evm, _: &Context, _: &Call, state: &mut S) -> EvmResult<()> {
250    evm.gas_charge(100)?;
251    let [acc] = evm.peek()?;
252    let acc: Acc = acc.to();
253    let Some(account) = state.acc(&acc) else {
254        return Err(EvmYield::Fetch(Fetch::Code(acc)));
255    };
256    if state.is_cold_acc(&acc) {
257        evm.warm_acc(&acc);
258        evm.gas_charge(2_500)?;
259    }
260    // EIP-1052 / EIP-161: return 0 for empty accounts (balance=0, nonce=0, code=empty)
261    let hash = if account.code.0.0.is_empty() && account.nonce.is_zero() && account.value.is_zero()
262    {
263        Int::ZERO
264    } else if account.code.1.is_zero() && account.code.0.0.is_empty() {
265        // Non-empty account (has balance or nonce) with no code: return keccak256("")
266        yevm_misc::keccak256(&[])
267    } else {
268        account.code.1
269    };
270    evm.push(hash)?;
271    Ok(())
272}
273
274pub fn blockhash<S: State>(evm: &mut Evm, _: &Context, _: &Call, state: &mut S) -> EvmResult<()> {
275    evm.gas_charge(20)?;
276    let [number] = evm.peek()?;
277    let number = number.as_u64();
278    let current = evm.head.number.as_u64();
279    // EVM spec: return 0 for current/future blocks and blocks older than 256
280    if number >= current || current - number > 256 {
281        evm.push(Int::ZERO)?;
282        return Ok(());
283    }
284    let Some(head) = state.head(number) else {
285        return Err(EvmYield::Fetch(Fetch::BlockHash(number)));
286    };
287    evm.push(head.hash)?;
288    Ok(())
289}
290
291pub fn coinbase(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
292    evm.gas_charge(2)?;
293    evm.push(evm.head.coinbase.to())?;
294    Ok(())
295}
296
297pub fn timestamp(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
298    evm.gas_charge(2)?;
299    evm.push(evm.head.timestamp)?;
300    Ok(())
301}
302
303pub fn number(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
304    evm.gas_charge(2)?;
305    evm.push(evm.head.number.to())?;
306    Ok(())
307}
308
309pub fn prevrandao(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
310    evm.gas_charge(2)?;
311    evm.push(evm.head.prevrandao)?;
312    Ok(())
313}
314
315pub fn gaslimit(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
316    evm.gas_charge(2)?;
317    evm.push(evm.head.gas_limit)?;
318    Ok(())
319}
320
321pub fn chainid(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
322    evm.gas_charge(2)?;
323    evm.push(evm.chain_id)?;
324    Ok(())
325}
326
327pub fn selfbalance<S: State>(
328    evm: &mut Evm,
329    ctx: &Context,
330    _: &Call,
331    state: &mut S,
332) -> EvmResult<()> {
333    evm.gas_charge(5)?;
334    let acc: Acc = ctx.this;
335    let Some(balance) = state.balance(&acc) else {
336        return Err(EvmYield::Fetch(Fetch::Balance(acc)));
337    };
338    evm.push(balance)?;
339    Ok(())
340}
341
342pub fn basefee(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
343    evm.gas_charge(2)?;
344    evm.push(evm.head.base_fee)?;
345    Ok(())
346}
347
348pub fn blobhash(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
349    evm.gas_charge(3)?;
350    let [index] = evm.peek()?;
351    let hash = index
352        .as_usize_checked()
353        .and_then(|i| evm.blob_hashes.get(i))
354        .copied()
355        .unwrap_or(Int::ZERO);
356    evm.push(hash)?;
357    Ok(())
358}
359
360pub fn blobbasefee(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
361    evm.gas_charge(2)?;
362    // TODO: proper blob handling
363    // context: 0xd6859d613c7ffcfb371ec3c3eebc8ad37dc9480df0e6a304c80864b104d6c962/24935686:49
364    // excess = 0x0da161cb must lead to blob_base_fee = 0x12d4542f (source: chain state)
365    // yet it does not fit `fake_exponent` calculations described in the spec!
366
367    // let fee = evm.head.excess_blob_gas.map_or(Int::ZERO, |excess| {
368    //     crate::call::blob_base_fee(evm.head.number.as_u64(), excess)
369    // });
370    // evm.push(fee)?;
371
372    evm.push(Int::ONE)?;
373    Ok(())
374}