Skip to main content

revm_interpreter/instructions/
stack.rs

1use crate::{
2    interpreter_types::{Immediates, InterpreterTypes as ITy, Jumps, RuntimeFlag, StackTr},
3    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: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
63    check!(context.interpreter, AMSTERDAM);
64    let x: usize = context.interpreter.bytecode.read_u8().into();
65    if let Some(n) = decode_single(x) {
66        if !context.interpreter.stack.dup(n) {
67            return Err(InstructionResult::StackOverflow);
68        }
69        context.interpreter.bytecode.relative_jump(1);
70    } else {
71        return Err(InstructionResult::InvalidImmediateEncoding);
72    }
73    Ok(())
74}
75
76/// Implements the SWAPN instruction.
77///
78/// Swaps the top stack item with the N+1th stack item, with N given by an immediate.
79pub fn swapn<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
80    check!(context.interpreter, AMSTERDAM);
81    let x: usize = context.interpreter.bytecode.read_u8().into();
82    if let Some(n) = decode_single(x) {
83        if !context.interpreter.stack.exchange(0, n) {
84            return Err(InstructionResult::StackUnderflow);
85        }
86        context.interpreter.bytecode.relative_jump(1);
87    } else {
88        return Err(InstructionResult::InvalidImmediateEncoding);
89    }
90    Ok(())
91}
92
93/// Implements the EXCHANGE instruction.
94///
95/// Swaps the N+1th stack item with the M+1th stack item, with N, M given by an immediate.
96pub fn exchange<IT: ITy, H: ?Sized>(context: Ictx<'_, H, IT>) -> Result {
97    check!(context.interpreter, AMSTERDAM);
98    let x: usize = context.interpreter.bytecode.read_u8().into();
99    if let Some((n, m)) = decode_pair(x) {
100        if !context.interpreter.stack.exchange(n, m - n) {
101            return Err(InstructionResult::StackUnderflow);
102        }
103        context.interpreter.bytecode.relative_jump(1);
104    } else {
105        return Err(InstructionResult::InvalidImmediateEncoding);
106    }
107    Ok(())
108}
109
110const fn decode_single(x: usize) -> Option<usize> {
111    if x <= 90 || x >= 128 {
112        Some((x + 145) % 256)
113    } else {
114        None
115    }
116}
117
118const fn decode_pair(x: usize) -> Option<(usize, usize)> {
119    if x > 81 && x < 128 {
120        return None;
121    }
122    let k = x ^ 143;
123    let q = k / 16;
124    let r = k % 16;
125    if q < r {
126        Some((q + 1, r + 1))
127    } else {
128        Some((r + 1, 29 - q))
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use crate::{
135        host::DummyHost,
136        instructions::{gas_table, instruction_table},
137        interpreter::{EthInterpreter, ExtBytecode, InputsImpl, SharedMemory},
138        interpreter_types::LoopControl,
139        Interpreter,
140    };
141    use bytecode::opcode::*;
142    use bytecode::Bytecode;
143    use primitives::{hardfork::SpecId, Bytes, U256};
144
145    fn run_bytecode(code: &[u8]) -> Interpreter {
146        let bytecode = Bytecode::new_raw(Bytes::copy_from_slice(code));
147        let mut interpreter = Interpreter::<EthInterpreter>::new(
148            SharedMemory::new(),
149            ExtBytecode::new(bytecode),
150            InputsImpl::default(),
151            false,
152            SpecId::AMSTERDAM,
153            u64::MAX,
154        );
155        let table = instruction_table::<EthInterpreter, DummyHost>();
156        let gas = gas_table();
157        let mut host = DummyHost::new(SpecId::AMSTERDAM);
158        interpreter.run_plain(&table, &gas, &mut host);
159        interpreter
160    }
161
162    #[test]
163    fn test_dupn() {
164        let interpreter = run_bytecode(&[
165            PUSH1, 0x01, PUSH1, 0x00, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1,
166            DUP1, DUP1, DUP1, DUP1, DUP1, DUPN, 0x80,
167        ]);
168        assert_eq!(interpreter.stack.len(), 18);
169        assert_eq!(interpreter.stack.data()[17], U256::from(1));
170        assert_eq!(interpreter.stack.data()[0], U256::from(1));
171        for i in 1..17 {
172            assert_eq!(interpreter.stack.data()[i], U256::ZERO);
173        }
174    }
175
176    #[test]
177    fn test_swapn() {
178        let interpreter = run_bytecode(&[
179            PUSH1, 0x01, PUSH1, 0x00, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1,
180            DUP1, DUP1, DUP1, DUP1, DUP1, PUSH1, 0x02, SWAPN, 0x80,
181        ]);
182        assert_eq!(interpreter.stack.len(), 18);
183        assert_eq!(interpreter.stack.data()[17], U256::from(1));
184        assert_eq!(interpreter.stack.data()[0], U256::from(2));
185        for i in 1..17 {
186            assert_eq!(interpreter.stack.data()[i], U256::ZERO);
187        }
188    }
189
190    #[test]
191    fn test_exchange() {
192        let interpreter = run_bytecode(&[PUSH1, 0x00, PUSH1, 0x01, PUSH1, 0x02, EXCHANGE, 0x8E]);
193        assert_eq!(interpreter.stack.len(), 3);
194        assert_eq!(interpreter.stack.data()[2], U256::from(2));
195        assert_eq!(interpreter.stack.data()[1], U256::from(0));
196        assert_eq!(interpreter.stack.data()[0], U256::from(1));
197    }
198
199    #[test]
200    fn test_swapn_invalid_immediate() {
201        let mut interpreter = run_bytecode(&[SWAPN, JUMPDEST]);
202        assert!(interpreter.bytecode.instruction_result().is_none());
203    }
204
205    #[test]
206    fn test_jump_over_invalid_dupn() {
207        let interpreter = run_bytecode(&[PUSH1, 0x04, JUMP, DUPN, JUMPDEST]);
208        assert!(interpreter.bytecode.is_not_end());
209    }
210
211    #[test]
212    fn test_exchange_with_iszero() {
213        let interpreter = run_bytecode(&[
214            PUSH1, 0x00, PUSH1, 0x00, PUSH1, 0x00, EXCHANGE, 0x8E, ISZERO,
215        ]);
216        assert_eq!(interpreter.stack.len(), 3);
217        assert_eq!(interpreter.stack.data()[2], U256::from(1));
218        assert_eq!(interpreter.stack.data()[1], U256::ZERO);
219        assert_eq!(interpreter.stack.data()[0], U256::ZERO);
220    }
221}