zingen/visitor/
log.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! System instructions

use crate::{masm::MemoryInfo, wasm::ToLSBytes, Error, Function, Result};

impl Function {
    /// Parse log data from the bytecode.
    ///
    /// WASM example:
    /// ```
    /// i32.const 1048576   ;; offset
    /// i32.const 4         ;; 4 bytes
    /// ```
    fn data(&mut self) -> Result<(i32, i32)> {
        let buffer: Vec<u8> = self.masm.buffer().into();

        // Pop offset and size from the bytecode.
        //
        // TODO: backtrace should cross the whole codegen,
        // embed stack operations. (#155)
        let data_len = self.backtrace.popn(2).concat().len();
        self.masm.decrement_sp(2)?;

        let data = &buffer[(buffer.len() - data_len)..];
        *self.masm.buffer_mut() = buffer[..(buffer.len() - data_len)].into();

        // Parse offset.
        //
        // PUSH0 0x5e
        // ..
        // PUSH32 0x8f
        if !(0x5e..0x8f).contains(&data[0]) {
            return Err(Error::InvalidDataOffset(data[0].into()));
        }

        let offset_len = (data[0] - 0x5f) as usize;
        tracing::trace!("offset len: {offset_len}");
        let offset = {
            let mut bytes = [0; 4];
            bytes[(4 - offset_len)..].copy_from_slice(&data[1..(offset_len + 1)]);
            bytes.reverse();
            i32::from_le_bytes(bytes)
        };
        tracing::debug!("log offset: {:?}", offset);

        // Parse size.
        if !(0x5e..0x8f).contains(&data[offset_len + 1]) {
            return Err(Error::InvalidDataOffset(data[offset_len + 1].into()));
        }
        let size = {
            // TODO: from ls bytes as offset
            let mut bytes = [0; 4];
            let size_bytes = &data[(offset_len + 2)..];
            bytes[..size_bytes.len()].copy_from_slice(size_bytes);
            i32::from_le_bytes(bytes)
        };

        tracing::debug!("log size: {:?}", size);
        Ok((offset, size))
    }

    /// Log a message with topics.
    pub fn log(&mut self, count: usize) -> Result<()> {
        let mut topics = Vec::<Vec<u8>>::default();
        for topic in (1..=count).rev() {
            let (offset, size) = self.data()?;
            let size = size as usize;
            let data = self.env.data.load(offset, size)?;

            tracing::debug!("log{count} topic{topic}: {:?}", data);
            topics.push(data);
        }

        let name = {
            let (offset, size) = self.data()?;
            let size = size as usize;
            let data = self.env.data.load(offset, size)?;

            tracing::debug!("log1 name: {:?}", data);
            data
        };

        for topic in topics {
            self.masm.push(&topic)?;
        }

        // 1. write data to memory
        let MemoryInfo { offset, size } = self.masm.memory_write_bytes(&name)?;

        // 3. prepare the offset and size of the data.
        self.masm.push(&size.to_ls_bytes())?;
        self.masm.push(&offset)?;

        // 4. run log for the data
        match count {
            0 => self.masm._log0(),
            1 => self.masm._log1(),
            2 => self.masm._log2(),
            3 => self.masm._log3(),
            4 => self.masm._log4(),
            _ => unreachable!("invalid topics"),
        }?;

        Ok(())
    }

    /// Revert with message.
    pub fn revert(&mut self, count: usize) -> Result<()> {
        let mut message = Vec::<Vec<u8>>::default();
        for slot in 0..count {
            let (offset, size) = self.data()?;
            let size = size as usize;
            let data = self.env.data.load(offset, size)?;

            self.masm.push(&data)?;
            if slot == 0 {
                self.masm._push0()?;
            } else {
                self.masm.push(&slot.to_ls_bytes())?;
            }
            self.masm._mstore()?;
            message.push(data);
        }

        tracing::debug!(
            "revert message: {}",
            String::from_utf8_lossy(&message.into_iter().flatten().collect::<Vec<u8>>())
        );

        self.masm.push(&(count * 32).to_ls_bytes())?;
        self.masm._push0()?;

        // 3. run log for the data
        self.masm._revert()?;
        Ok(())
    }
}