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::StackOverflow);
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::StackOverflow);
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::StackOverflow);
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 {
119        Some(x + 17)
120    } else if x >= 128 {
121        Some(x - 20)
122    } else {
123        None
124    }
125}
126
127fn decode_pair(x: usize) -> Option<(usize, usize)> {
128    let k = if x <= 79 {
129        x
130    } else if x >= 128 {
131        x - 48
132    } else {
133        return None;
134    };
135    let q = k / 16;
136    let r = k % 16;
137    if q < r {
138        Some((q + 1, r + 1))
139    } else {
140        Some((r + 1, 29 - q))
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use crate::{
147        host::DummyHost,
148        instructions::instruction_table,
149        interpreter::{EthInterpreter, ExtBytecode, InputsImpl, SharedMemory},
150        interpreter_types::LoopControl,
151        Interpreter,
152    };
153    use bytecode::opcode::*;
154    use bytecode::Bytecode;
155    use primitives::{hardfork::SpecId, Bytes, U256};
156
157    fn run_bytecode(code: &[u8]) -> Interpreter {
158        let bytecode = Bytecode::new_raw(Bytes::copy_from_slice(code));
159        let mut interpreter = Interpreter::<EthInterpreter>::new(
160            SharedMemory::new(),
161            ExtBytecode::new(bytecode),
162            InputsImpl::default(),
163            false,
164            SpecId::AMSTERDAM,
165            u64::MAX,
166        );
167        let table = instruction_table::<EthInterpreter, DummyHost>();
168        let mut host = DummyHost::new(SpecId::AMSTERDAM);
169        interpreter.run_plain(&table, &mut host);
170        interpreter
171    }
172
173    #[test]
174    fn test_dupn() {
175        let interpreter = run_bytecode(&[
176            PUSH1, 0x01, PUSH1, 0x00, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1,
177            DUP1, DUP1, DUP1, DUP1, DUP1, DUPN, 0x00,
178        ]);
179        assert_eq!(interpreter.stack.len(), 18);
180        assert_eq!(interpreter.stack.data()[17], U256::from(1));
181        assert_eq!(interpreter.stack.data()[0], U256::from(1));
182        for i in 1..17 {
183            assert_eq!(interpreter.stack.data()[i], U256::ZERO);
184        }
185    }
186
187    #[test]
188    fn test_swapn() {
189        let interpreter = run_bytecode(&[
190            PUSH1, 0x01, PUSH1, 0x00, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1, DUP1,
191            DUP1, DUP1, DUP1, DUP1, DUP1, PUSH1, 0x02, SWAPN, 0x00,
192        ]);
193        assert_eq!(interpreter.stack.len(), 18);
194        assert_eq!(interpreter.stack.data()[17], U256::from(1));
195        assert_eq!(interpreter.stack.data()[0], U256::from(2));
196        for i in 1..17 {
197            assert_eq!(interpreter.stack.data()[i], U256::ZERO);
198        }
199    }
200
201    #[test]
202    fn test_exchange() {
203        let interpreter = run_bytecode(&[PUSH1, 0x00, PUSH1, 0x01, PUSH1, 0x02, EXCHANGE, 0x01]);
204        assert_eq!(interpreter.stack.len(), 3);
205        assert_eq!(interpreter.stack.data()[2], U256::from(2));
206        assert_eq!(interpreter.stack.data()[1], U256::from(0));
207        assert_eq!(interpreter.stack.data()[0], U256::from(1));
208    }
209
210    #[test]
211    fn test_swapn_invalid_immediate() {
212        let mut interpreter = run_bytecode(&[SWAPN, JUMPDEST]);
213        assert!(interpreter.bytecode.instruction_result().is_none());
214    }
215
216    #[test]
217    fn test_jump_over_invalid_dupn() {
218        let interpreter = run_bytecode(&[PUSH1, 0x04, JUMP, DUPN, JUMPDEST]);
219        assert!(interpreter.bytecode.is_not_end());
220    }
221
222    #[test]
223    fn test_exchange_with_iszero() {
224        let interpreter = run_bytecode(&[
225            PUSH1, 0x00, PUSH1, 0x00, PUSH1, 0x00, EXCHANGE, 0x01, ISZERO,
226        ]);
227        assert_eq!(interpreter.stack.len(), 3);
228        assert_eq!(interpreter.stack.data()[2], U256::from(1));
229        assert_eq!(interpreter.stack.data()[1], U256::ZERO);
230        assert_eq!(interpreter.stack.data()[0], U256::ZERO);
231    }
232}