1use 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#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub struct LogEntry {
17 pub address: Address,
20 pub topics: Vec<H256>,
22 pub data: Bytes,
24 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
48impl 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 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#[derive(Default, Debug, PartialEq, Clone)]
105pub struct LocalizedLogEntry {
106 pub entry: LogEntry,
108 pub block_hash: H256,
110 pub epoch_number: BlockNumber,
112 pub block_timestamp: Option<u64>,
114 pub transaction_hash: H256,
116 pub transaction_index: usize,
118 pub log_index: usize,
120 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 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 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}