zingen/visitor/
log.rs

1//! System instructions
2
3use crate::{masm::MemoryInfo, wasm::ToLSBytes, Error, Function, Result};
4
5impl Function {
6    /// Parse log data from the bytecode.
7    ///
8    /// WASM example:
9    /// ```
10    /// i32.const 1048576   ;; offset
11    /// i32.const 4         ;; 4 bytes
12    /// ```
13    fn data(&mut self) -> Result<(i32, i32)> {
14        let buffer: Vec<u8> = self.masm.buffer().into();
15
16        // Pop offset and size from the bytecode.
17        //
18        // TODO: backtrace should cross the whole codegen,
19        // embed stack operations. (#155)
20        let data_len = self.backtrace.popn(2).concat().len();
21        self.masm.decrement_sp(2)?;
22
23        let data = &buffer[(buffer.len() - data_len)..];
24        *self.masm.buffer_mut() = buffer[..(buffer.len() - data_len)].into();
25
26        // Parse offset.
27        //
28        // PUSH0 0x5e
29        // ..
30        // PUSH32 0x8f
31        if !(0x5e..0x8f).contains(&data[0]) {
32            return Err(Error::InvalidDataOffset(data[0].into()));
33        }
34
35        let offset_len = (data[0] - 0x5f) as usize;
36        tracing::trace!("offset len: {offset_len}");
37        let offset = {
38            let mut bytes = [0; 4];
39            bytes[(4 - offset_len)..].copy_from_slice(&data[1..(offset_len + 1)]);
40            bytes.reverse();
41            i32::from_le_bytes(bytes)
42        };
43        tracing::debug!("log offset: {:?}", offset);
44
45        // Parse size.
46        if !(0x5e..0x8f).contains(&data[offset_len + 1]) {
47            return Err(Error::InvalidDataOffset(data[offset_len + 1].into()));
48        }
49        let size = {
50            // TODO: from ls bytes as offset
51            let mut bytes = [0; 4];
52            let size_bytes = &data[(offset_len + 2)..];
53            bytes[..size_bytes.len()].copy_from_slice(size_bytes);
54            i32::from_le_bytes(bytes)
55        };
56
57        tracing::debug!("log size: {:?}", size);
58        Ok((offset, size))
59    }
60
61    /// Log a message with topics.
62    pub fn log(&mut self, count: usize) -> Result<()> {
63        let (offset, size) = self.data()?;
64        let data = self.env.data.load(offset, size as usize)?;
65
66        // 1. write data to memory
67        let MemoryInfo { offset, size } = self.masm.memory_write_bytes(&data)?;
68
69        // 3. prepare the offset and size of the data.
70        self.masm.push(&size.to_ls_bytes())?;
71        self.masm.push(&offset)?;
72
73        // 4. run log for the data
74        match count {
75            0 => self.masm._log0(),
76            1 => self.masm._log1(),
77            2 => self.masm._log2(),
78            3 => self.masm._log3(),
79            4 => self.masm._log4(),
80            _ => unreachable!("invalid topics"),
81        }?;
82
83        Ok(())
84    }
85
86    /// Revert with message.
87    pub fn revert(&mut self, count: usize) -> Result<()> {
88        let mut message = Vec::<Vec<u8>>::default();
89        for slot in 0..count {
90            let (offset, size) = self.data()?;
91            let size = size as usize;
92            let data = self.env.data.load(offset, size)?;
93
94            self.masm.push(&data)?;
95            if slot == 0 {
96                self.masm._push0()?;
97            } else {
98                self.masm.push(&slot.to_ls_bytes())?;
99            }
100            self.masm._mstore()?;
101            message.push(data);
102        }
103
104        tracing::debug!(
105            "revert message: {}",
106            String::from_utf8_lossy(&message.into_iter().flatten().collect::<Vec<u8>>())
107        );
108
109        self.masm.push(&(count * 32).to_ls_bytes())?;
110        self.masm._push0()?;
111
112        // 3. run log for the data
113        self.masm._revert()?;
114        Ok(())
115    }
116}