Skip to main content

revm_interpreter/instructions/
stack.rs

1use crate::{
2    interpreter_types::{Immediates, InterpreterTypes as ITy, Jumps, RuntimeFlag, StackTr},
3    Host, InstructionContext as Ictx, InstructionExecResult as Result, InstructionResult,
4};
5use primitives::U256;
6
7/// Implements the POP instruction.
8///
9/// Removes the top item from the stack.
10pub fn pop<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
11    // Can ignore return. as relative N jump is safe operation.
12    popn!([_i], context.interpreter);
13    Ok(())
14}
15
16/// EIP-3855: PUSH0 instruction
17///
18/// Introduce a new instruction which pushes the constant value 0 onto the stack.
19pub fn push0<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
20    check!(context.interpreter, SHANGHAI);
21    push!(context.interpreter, U256::ZERO);
22    Ok(())
23}
24
25/// Implements the PUSH1-PUSH32 instructions.
26///
27/// Pushes N bytes from bytecode onto the stack as a 32-byte value.
28pub fn push<const N: usize, IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
29    let slice = context.interpreter.bytecode.read_slice(N);
30    if !context.interpreter.stack.push_slice(slice) {
31        return Err(InstructionResult::StackOverflow);
32    }
33
34    context.interpreter.bytecode.relative_jump(N as isize);
35    Ok(())
36}
37
38/// Implements the DUP1-DUP16 instructions.
39///
40/// Duplicates the Nth stack item to the top of the stack.
41pub fn dup<const N: usize, IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
42    if !context.interpreter.stack.dup(N) {
43        return Err(InstructionResult::StackOverflow);
44    }
45    Ok(())
46}
47
48/// Implements the SWAP1-SWAP16 instructions.
49///
50/// Swaps the top stack item with the Nth stack item.
51pub fn swap<const N: usize, IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
52    assert!(N != 0);
53    if !context.interpreter.stack.exchange(0, N) {
54        return Err(InstructionResult::StackUnderflow);
55    }
56    Ok(())
57}
58
59/// Implements the DUPN instruction.
60///
61/// Duplicates the Nth stack item to the top of the stack, with N given by an immediate.
62pub fn dupn<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
63    if !context.host.is_amsterdam_eip8024_enabled() {
64        return Err(InstructionResult::NotActivated);
65    }
66    let x: usize = context.interpreter.bytecode.read_u8().into();
67    if let Some(n) = decode_single(x) {
68        if !context.interpreter.stack.dup(n) {
69            return Err(InstructionResult::StackOverflow);
70        }
71        context.interpreter.bytecode.relative_jump(1);
72    } else {
73        return Err(InstructionResult::InvalidImmediateEncoding);
74    }
75    Ok(())
76}
77
78/// Implements the SWAPN instruction.
79///
80/// Swaps the top stack item with the N+1th stack item, with N given by an immediate.
81pub fn swapn<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
82    if !context.host.is_amsterdam_eip8024_enabled() {
83        return Err(InstructionResult::NotActivated);
84    }
85    let x: usize = context.interpreter.bytecode.read_u8().into();
86    if let Some(n) = decode_single(x) {
87        if !context.interpreter.stack.exchange(0, n) {
88            return Err(InstructionResult::StackUnderflow);
89        }
90        context.interpreter.bytecode.relative_jump(1);
91    } else {
92        return Err(InstructionResult::InvalidImmediateEncoding);
93    }
94    Ok(())
95}
96
97/// Implements the EXCHANGE instruction.
98///
99/// Swaps the N+1th stack item with the M+1th stack item, with N, M given by an immediate.
100pub fn exchange<IT: ITy, H: Host + ?Sized>(context: Ictx<'_, H, IT>) -> Result {
101    if !context.host.is_amsterdam_eip8024_enabled() {
102        return Err(InstructionResult::NotActivated);
103    }
104    let x: usize = context.interpreter.bytecode.read_u8().into();
105    if let Some((n, m)) = decode_pair(x) {
106        if !context.interpreter.stack.exchange(n, m - n) {
107            return Err(InstructionResult::StackUnderflow);
108        }
109        context.interpreter.bytecode.relative_jump(1);
110    } else {
111        return Err(InstructionResult::InvalidImmediateEncoding);
112    }
113    Ok(())
114}
115
116const fn decode_single(x: usize) -> Option<usize> {
117    if x <= 90 || x >= 128 {
118        Some((x + 145) % 256)
119    } else {
120        None
121    }
122}
123
124const fn decode_pair(x: usize) -> Option<(usize, usize)> {
125    if x > 81 && x < 128 {
126        return None;
127    }
128    let k = x ^ 143;
129    let q = k / 16;
130    let r = k % 16;
131    if q < r {
132        Some((q + 1, r + 1))
133    } else {
134        Some((r + 1, 29 - q))
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::{
141        host::DummyHost,
142        instructions::{gas_table, instruction_table},
143        interpreter::{EthInterpreter, ExtBytecode, InputsImpl, SharedMemory},
144        interpreter_types::LoopControl,
145        Interpreter,
146    };
147    use bytecode::opcode::*;
148    use bytecode::Bytecode;
149    use primitives::{hardfork::SpecId, Bytes, U256};
150
151    fn run_bytecode(code: &[u8]) -> Interpreter {
152        let bytecode = Bytecode::new_raw(Bytes::copy_from_slice(code));
153        let mut interpreter = Interpreter::<EthInterpreter>::new(
154            SharedMemory::new(),
155            ExtBytecode::new(bytecode),
156            InputsImpl::default(),
157            false,
158            SpecId::AMSTERDAM,
159            u64::MAX,
160        );
161        let table = instruction_table::<EthInterpreter, DummyHost>();
162        let gas = gas_table();
163        let mut host = DummyHost::new(SpecId::AMSTERDAM);
164        interpreter.run_plain(&table, &gas, &mut host);
165        interpreter
166    }
167
168    #[test]
169    fn test_dupn() {
170        let interpreter = run_bytecode(&[
171            PUSH1, 0x01, PUSH1, 0x00, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1,
172            DUP1, DUP1, DUP1, DUP1, DUP1, DUPN, 0x80,
173        ]);
174        assert_eq!(interpreter.stack.len(), 18);
175        assert_eq!(interpreter.stack.data()[17], U256::from(1));
176        assert_eq!(interpreter.stack.data()[0], U256::from(1));
177        for i in 1..17 {
178            assert_eq!(interpreter.stack.data()[i], U256::ZERO);
179        }
180    }
181
182    #[test]
183    fn test_swapn() {
184        let interpreter = run_bytecode(&[
185            PUSH1, 0x01, PUSH1, 0x00, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1,
186            DUP1, DUP1, DUP1, DUP1, DUP1, PUSH1, 0x02, SWAPN, 0x80,
187        ]);
188        assert_eq!(interpreter.stack.len(), 18);
189        assert_eq!(interpreter.stack.data()[17], U256::from(1));
190        assert_eq!(interpreter.stack.data()[0], U256::from(2));
191        for i in 1..17 {
192            assert_eq!(interpreter.stack.data()[i], U256::ZERO);
193        }
194    }
195
196    #[test]
197    fn test_exchange() {
198        let interpreter = run_bytecode(&[PUSH1, 0x00, PUSH1, 0x01, PUSH1, 0x02, EXCHANGE, 0x8E]);
199        assert_eq!(interpreter.stack.len(), 3);
200        assert_eq!(interpreter.stack.data()[2], U256::from(2));
201        assert_eq!(interpreter.stack.data()[1], U256::from(0));
202        assert_eq!(interpreter.stack.data()[0], U256::from(1));
203    }
204
205    #[test]
206    fn test_swapn_invalid_immediate() {
207        let mut interpreter = run_bytecode(&[SWAPN, JUMPDEST]);
208        assert!(interpreter.bytecode.instruction_result().is_none());
209    }
210
211    #[test]
212    fn test_jump_over_invalid_dupn() {
213        let interpreter = run_bytecode(&[PUSH1, 0x04, JUMP, DUPN, JUMPDEST]);
214        assert!(interpreter.bytecode.is_not_end());
215    }
216
217    #[test]
218    fn test_exchange_with_iszero() {
219        let interpreter = run_bytecode(&[
220            PUSH1, 0x00, PUSH1, 0x00, PUSH1, 0x00, EXCHANGE, 0x8E, ISZERO,
221        ]);
222        assert_eq!(interpreter.stack.len(), 3);
223        assert_eq!(interpreter.stack.data()[2], U256::from(1));
224        assert_eq!(interpreter.stack.data()[1], U256::ZERO);
225        assert_eq!(interpreter.stack.data()[0], U256::ZERO);
226    }
227}