Skip to main content

revm_interpreter/instructions/
stack.rs

1use crate::{
2    interpreter_types::{Immediates, InterpreterTypes, Jumps, RuntimeFlag, StackTr},
3    InstructionResult,
4};
5use primitives::U256;
6
7use crate::InstructionContext;
8
9/// Implements the POP instruction.
10///
11/// Removes the top item from the stack.
12pub fn pop<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
13    // Can ignore return. as relative N jump is safe operation.
14    popn!([_i], context.interpreter);
15}
16
17/// EIP-3855: PUSH0 instruction
18///
19/// Introduce a new instruction which pushes the constant value 0 onto the stack.
20pub fn push0<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
21    check!(context.interpreter, SHANGHAI);
22    push!(context.interpreter, U256::ZERO);
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, WIRE: InterpreterTypes, H: ?Sized>(
29    context: InstructionContext<'_, H, WIRE>,
30) {
31    let slice = context.interpreter.bytecode.read_slice(N);
32    if !context.interpreter.stack.push_slice(slice) {
33        context.interpreter.halt(InstructionResult::StackOverflow);
34        return;
35    }
36
37    context.interpreter.bytecode.relative_jump(N as isize);
38}
39
40/// Implements the DUP1-DUP16 instructions.
41///
42/// Duplicates the Nth stack item to the top of the stack.
43pub fn dup<const N: usize, WIRE: InterpreterTypes, H: ?Sized>(
44    context: InstructionContext<'_, H, WIRE>,
45) {
46    if !context.interpreter.stack.dup(N) {
47        context.interpreter.halt(InstructionResult::StackOverflow);
48    }
49}
50
51/// Implements the SWAP1-SWAP16 instructions.
52///
53/// Swaps the top stack item with the Nth stack item.
54pub fn swap<const N: usize, WIRE: InterpreterTypes, H: ?Sized>(
55    context: InstructionContext<'_, H, WIRE>,
56) {
57    assert!(N != 0);
58    if !context.interpreter.stack.exchange(0, N) {
59        context.interpreter.halt(InstructionResult::StackUnderflow);
60    }
61}
62
63/// Implements the DUPN instruction.
64///
65/// Duplicates the Nth stack item to the top of the stack, with N given by an immediate.
66pub fn dupn<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
67    check!(context.interpreter, AMSTERDAM);
68    let x: usize = context.interpreter.bytecode.read_u8().into();
69    if let Some(n) = decode_single(x) {
70        if !context.interpreter.stack.dup(n) {
71            context.interpreter.halt(InstructionResult::StackOverflow);
72        }
73        context.interpreter.bytecode.relative_jump(1);
74    } else {
75        context
76            .interpreter
77            .halt(InstructionResult::InvalidImmediateEncoding);
78    }
79}
80
81/// Implements the SWAPN instruction.
82///
83/// Swaps the top stack item with the N+1th stack item, with N given by an immediate.
84pub fn swapn<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
85    check!(context.interpreter, AMSTERDAM);
86    let x: usize = context.interpreter.bytecode.read_u8().into();
87    if let Some(n) = decode_single(x) {
88        if !context.interpreter.stack.exchange(0, n) {
89            context.interpreter.halt(InstructionResult::StackUnderflow);
90        }
91        context.interpreter.bytecode.relative_jump(1);
92    } else {
93        context
94            .interpreter
95            .halt(InstructionResult::InvalidImmediateEncoding);
96    }
97}
98
99/// Implements the EXCHANGE instruction.
100///
101/// Swaps the N+1th stack item with the M+1th stack item, with N, M given by an immediate.
102pub fn exchange<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_, H, WIRE>) {
103    check!(context.interpreter, AMSTERDAM);
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            context.interpreter.halt(InstructionResult::StackUnderflow);
108        }
109        context.interpreter.bytecode.relative_jump(1);
110    } else {
111        context
112            .interpreter
113            .halt(InstructionResult::InvalidImmediateEncoding);
114    }
115}
116
117fn decode_single(x: usize) -> Option<usize> {
118    if x <= 90 || x >= 128 {
119        Some((x + 145) % 256)
120    } else {
121        None
122    }
123}
124
125fn decode_pair(x: usize) -> Option<(usize, usize)> {
126    if x > 81 && x < 128 {
127        return None;
128    }
129    let k = x ^ 143;
130    let q = k / 16;
131    let r = k % 16;
132    if q < r {
133        Some((q + 1, r + 1))
134    } else {
135        Some((r + 1, 29 - q))
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use crate::{
142        host::DummyHost,
143        instructions::instruction_table,
144        interpreter::{EthInterpreter, ExtBytecode, InputsImpl, SharedMemory},
145        interpreter_types::LoopControl,
146        Interpreter,
147    };
148    use bytecode::opcode::*;
149    use bytecode::Bytecode;
150    use primitives::{hardfork::SpecId, Bytes, U256};
151
152    fn run_bytecode(code: &[u8]) -> Interpreter {
153        let bytecode = Bytecode::new_raw(Bytes::copy_from_slice(code));
154        let mut interpreter = Interpreter::<EthInterpreter>::new(
155            SharedMemory::new(),
156            ExtBytecode::new(bytecode),
157            InputsImpl::default(),
158            false,
159            SpecId::AMSTERDAM,
160            u64::MAX,
161        );
162        let table = instruction_table::<EthInterpreter, DummyHost>();
163        let mut host = DummyHost::new(SpecId::AMSTERDAM);
164        interpreter.run_plain(&table, &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}