primitives/
log_entry.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5//! Log entry type definition.
6
7use crate::{block::BlockNumber, bytes::Bytes};
8use cfx_types::{Address, Bloom, BloomInput, Space, H256};
9use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
10use rlp::RlpStream;
11use serde_derive::{Deserialize, Serialize};
12use std::ops::Deref;
13
14/// A record of execution for a `LOG` operation.
15#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub struct LogEntry {
17    /// The address of the contract executing at the point of the `LOG`
18    /// operation.
19    pub address: Address,
20    /// The topics associated with the `LOG` operation.
21    pub topics: Vec<H256>,
22    /// The data associated with the `LOG` operation.
23    pub data: Bytes,
24    /// The space associated with `address`.
25    pub space: Space,
26}
27
28impl rlp::Encodable for LogEntry {
29    fn rlp_append(&self, s: &mut RlpStream) {
30        match self.space {
31            Space::Native => {
32                s.begin_list(3);
33                s.append(&self.address);
34                s.append_list(&self.topics);
35                s.append(&self.data);
36            }
37            Space::Ethereum => {
38                s.begin_list(4);
39                s.append(&self.address);
40                s.append_list(&self.topics);
41                s.append(&self.data);
42                s.append(&self.space);
43            }
44        }
45    }
46}
47
48// We want to remain backward-compatible with pre-CIP90 entries in the DB.
49// However, rlp_derive::RlpDecodable is not backward-compatible when adding new
50// fields, so we implement backward-compatible decoding manually.
51impl rlp::Decodable for LogEntry {
52    fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
53        let log = match rlp.item_count()? {
54            3 => LogEntry {
55                address: rlp.val_at(0)?,
56                topics: rlp.list_at(1)?,
57                data: rlp.val_at(2)?,
58                space: Space::Native,
59            },
60            4 => LogEntry {
61                address: rlp.val_at(0)?,
62                topics: rlp.list_at(1)?,
63                data: rlp.val_at(2)?,
64                space: rlp.val_at(3)?,
65            },
66            _ => return Err(rlp::DecoderError::RlpInvalidLength),
67        };
68        if log.topics.len() > 4 {
69            return Err(rlp::DecoderError::Custom("LogEntry too many topics"));
70        }
71        Ok(log)
72    }
73}
74
75impl MallocSizeOf for LogEntry {
76    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
77        self.topics.size_of(ops) + self.data.size_of(ops)
78    }
79}
80
81impl LogEntry {
82    /// Calculates the bloom of this log entry.
83    pub fn bloom(&self) -> Bloom {
84        self.topics.iter().fold(
85            Bloom::from(BloomInput::Raw(self.address.as_bytes())),
86            |mut b, t| {
87                b.accrue(BloomInput::Raw(t.as_bytes()));
88                b
89            },
90        )
91    }
92}
93
94pub fn build_bloom(logs: &[LogEntry]) -> Bloom {
95    logs.iter()
96        .map(LogEntry::bloom)
97        .fold(Bloom::default(), |mut acc, bloom| {
98            acc.accrue_bloom(&bloom);
99            acc
100        })
101}
102
103/// Log localized in a blockchain.
104#[derive(Default, Debug, PartialEq, Clone)]
105pub struct LocalizedLogEntry {
106    /// Plain log entry.
107    pub entry: LogEntry,
108    /// Block in which this log was created.
109    pub block_hash: H256,
110    /// Epoch number.
111    pub epoch_number: BlockNumber,
112    /// Timestamp of the block in which this log was created.
113    pub block_timestamp: Option<u64>,
114    /// Hash of transaction in which this log was created.
115    pub transaction_hash: H256,
116    /// Index of transaction within block.
117    pub transaction_index: usize,
118    /// Log position in the epoch.
119    pub log_index: usize,
120    /// Log position in the transaction.
121    pub transaction_log_index: usize,
122}
123
124impl Deref for LocalizedLogEntry {
125    type Target = LogEntry;
126
127    fn deref(&self) -> &Self::Target { &self.entry }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::LogEntry;
133    use crate::bytes::Bytes;
134    use cfx_types::{Address, Bloom, Space, H256};
135    use rlp_derive::RlpEncodable;
136
137    #[derive(PartialEq, Eq, RlpEncodable)]
138    pub struct LogEntryOld {
139        pub address: Address,
140        pub topics: Vec<H256>,
141        pub data: Bytes,
142    }
143
144    #[test]
145    fn test_empty_log_bloom() {
146        let bloom = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse::<Bloom>().unwrap();
147        let address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
148            .parse::<Address>()
149            .unwrap();
150        let log = LogEntry {
151            address,
152            topics: vec![],
153            data: vec![],
154            space: Space::Native,
155        };
156        assert_eq!(log.bloom(), bloom);
157    }
158
159    #[test]
160    fn test_rlp() {
161        let address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
162            .parse::<Address>()
163            .unwrap();
164
165        // check RLP for new version
166        let log = LogEntry {
167            address,
168            topics: vec![],
169            data: vec![],
170            space: Space::Ethereum,
171        };
172
173        assert_eq!(log, rlp::decode(&rlp::encode(&log)).unwrap());
174
175        // check RLP for old version
176        let log_old = LogEntryOld {
177            address,
178            topics: vec![],
179            data: vec![],
180        };
181
182        let log_new: LogEntry = rlp::decode(&rlp::encode(&log_old)).unwrap();
183
184        assert_eq!(
185            log_new,
186            LogEntry {
187                address,
188                topics: vec![],
189                data: vec![],
190                space: Space::Native,
191            }
192        );
193    }
194}