rtvm_interpreter/instructions/
data.rs

1use crate::{
2    gas::{BASE, DATA_LOAD_GAS, VERYLOW},
3    instructions::utility::read_u16,
4    interpreter::Interpreter,
5    primitives::U256,
6    Host,
7};
8
9pub fn data_load<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
10    require_eof!(interpreter);
11    gas!(interpreter, DATA_LOAD_GAS);
12    pop_top!(interpreter, offset);
13
14    let offset_usize = as_usize_saturated!(offset);
15
16    let slice = interpreter
17        .contract
18        .bytecode
19        .eof()
20        .expect("eof")
21        .data_slice(offset_usize, 32);
22
23    let mut word = [0u8; 32];
24    word[..slice.len()].copy_from_slice(slice);
25
26    *offset = U256::from_be_bytes(word);
27}
28
29pub fn data_loadn<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
30    require_eof!(interpreter);
31    gas!(interpreter, VERYLOW);
32    let offset = unsafe { read_u16(interpreter.instruction_pointer) } as usize;
33
34    let slice = interpreter
35        .contract
36        .bytecode
37        .eof()
38        .expect("eof")
39        .data_slice(offset, 32);
40
41    let mut word = [0u8; 32];
42    word[..slice.len()].copy_from_slice(slice);
43
44    push_b256!(interpreter, word.into());
45
46    // add +2 to the instruction pointer to skip the offset
47    interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(2) };
48}
49
50pub fn data_size<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
51    require_eof!(interpreter);
52    gas!(interpreter, BASE);
53    let data_size = interpreter.eof().expect("eof").header.data_size;
54
55    push!(interpreter, U256::from(data_size));
56}
57
58pub fn data_copy<H: Host + ?Sized>(interpreter: &mut Interpreter, _host: &mut H) {
59    require_eof!(interpreter);
60    gas!(interpreter, VERYLOW);
61    pop!(interpreter, mem_offset, offset, size);
62
63    // sizes more than u64::MAX will spend all the gas in memmory resize.
64    let size = as_usize_or_fail!(interpreter, size);
65    // size of zero should not change the memory
66    if size == 0 {
67        return;
68    }
69    // fail if mem offset is big as it will spend all the gas
70    let mem_offset = as_usize_or_fail!(interpreter, mem_offset);
71    resize_memory!(interpreter, mem_offset, size);
72
73    let offset = as_usize_saturated!(offset);
74    let data = interpreter.contract.bytecode.eof().expect("EOF").data();
75
76    // set data from the eof to the shared memory. Padd it with zeros.
77    interpreter
78        .shared_memory
79        .set_data(mem_offset, offset, size, data);
80}
81
82#[cfg(test)]
83mod test {
84    use rtvm_primitives::{b256, bytes, Bytecode, Bytes, Eof, PragueSpec};
85
86    use super::*;
87    use crate::{
88        opcode::{make_instruction_table, DATACOPY, DATALOAD, DATALOADN, DATASIZE},
89        DummyHost, Gas, Interpreter,
90    };
91
92    fn dummy_eof(code_bytes: Bytes) -> Bytecode {
93        let bytes = bytes!("ef000101000402000100010400000000800000fe");
94        let mut eof = Eof::decode(bytes).unwrap();
95
96        eof.body.data_section =
97            bytes!("000000000000000000000000000000000000000000000000000000000000000102030405");
98        eof.header.data_size = eof.body.data_section.len() as u16;
99
100        eof.header.code_sizes[0] = code_bytes.len() as u16;
101        eof.body.code_section[0] = code_bytes;
102        Bytecode::Eof(eof)
103    }
104
105    #[test]
106    fn dataload_dataloadn() {
107        let table = make_instruction_table::<_, PragueSpec>();
108        let mut host = DummyHost::default();
109        let eof = dummy_eof(Bytes::from([
110            DATALOAD, DATALOADN, 0x00, 0x00, DATALOAD, DATALOADN, 0x00, 35, DATALOAD, DATALOADN,
111            0x00, 36, DATASIZE,
112        ]));
113
114        let mut interp = Interpreter::new_bytecode(eof);
115        interp.gas = Gas::new(10000);
116
117        // DATALOAD
118        interp.stack.push(U256::from(0)).unwrap();
119        interp.step(&table, &mut host);
120        assert_eq!(interp.stack.data(), &vec![U256::from(0x01)]);
121        interp.stack.pop().unwrap();
122
123        // DATALOADN
124        interp.step(&table, &mut host);
125        assert_eq!(interp.stack.data(), &vec![U256::from(0x01)]);
126        interp.stack.pop().unwrap();
127
128        // DATALOAD (padding)
129        interp.stack.push(U256::from(35)).unwrap();
130        interp.step(&table, &mut host);
131        assert_eq!(
132            interp.stack.data(),
133            &vec![b256!("0500000000000000000000000000000000000000000000000000000000000000").into()]
134        );
135        interp.stack.pop().unwrap();
136
137        // DATALOADN (padding)
138        interp.step(&table, &mut host);
139        assert_eq!(
140            interp.stack.data(),
141            &vec![b256!("0500000000000000000000000000000000000000000000000000000000000000").into()]
142        );
143        interp.stack.pop().unwrap();
144
145        // DATALOAD (out of bounds)
146        interp.stack.push(U256::from(36)).unwrap();
147        interp.step(&table, &mut host);
148        assert_eq!(interp.stack.data(), &vec![U256::ZERO]);
149        interp.stack.pop().unwrap();
150
151        // DATALOADN (out of bounds)
152        interp.step(&table, &mut host);
153        assert_eq!(interp.stack.data(), &vec![U256::ZERO]);
154        interp.stack.pop().unwrap();
155
156        // DATA SIZE
157        interp.step(&table, &mut host);
158        assert_eq!(interp.stack.data(), &vec![U256::from(36)]);
159    }
160
161    #[test]
162    fn data_copy() {
163        let table = make_instruction_table::<_, PragueSpec>();
164        let mut host = DummyHost::default();
165        let eof = dummy_eof(Bytes::from([DATACOPY, DATACOPY, DATACOPY, DATACOPY]));
166
167        let mut interp = Interpreter::new_bytecode(eof);
168        interp.gas = Gas::new(10000);
169
170        // Data copy
171        // size, offset mem_offset,
172        interp.stack.push(U256::from(32)).unwrap();
173        interp.stack.push(U256::from(0)).unwrap();
174        interp.stack.push(U256::from(0)).unwrap();
175        interp.step(&table, &mut host);
176        assert_eq!(
177            interp.shared_memory.context_memory(),
178            &bytes!("0000000000000000000000000000000000000000000000000000000000000001")
179        );
180
181        // Data copy (Padding)
182        // size, offset mem_offset,
183        interp.stack.push(U256::from(2)).unwrap();
184        interp.stack.push(U256::from(35)).unwrap();
185        interp.stack.push(U256::from(1)).unwrap();
186        interp.step(&table, &mut host);
187        assert_eq!(
188            interp.shared_memory.context_memory(),
189            &bytes!("0005000000000000000000000000000000000000000000000000000000000001")
190        );
191
192        // Data copy (Out of bounds)
193        // size, offset mem_offset,
194        interp.stack.push(U256::from(2)).unwrap();
195        interp.stack.push(U256::from(37)).unwrap();
196        interp.stack.push(U256::from(1)).unwrap();
197        interp.step(&table, &mut host);
198        assert_eq!(
199            interp.shared_memory.context_memory(),
200            &bytes!("0000000000000000000000000000000000000000000000000000000000000001")
201        );
202
203        // Data copy (Size == 0)
204        // mem_offset, offset, size
205        interp.stack.push(U256::from(0)).unwrap();
206        interp.stack.push(U256::from(37)).unwrap();
207        interp.stack.push(U256::from(1)).unwrap();
208        interp.step(&table, &mut host);
209        assert_eq!(
210            interp.shared_memory.context_memory(),
211            &bytes!("0000000000000000000000000000000000000000000000000000000000000001")
212        );
213    }
214}