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        match rlp.item_count()? {
54            3 => Ok(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 => Ok(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            _ => Err(rlp::DecoderError::RlpInvalidLength),
67        }
68    }
69}
70
71impl MallocSizeOf for LogEntry {
72    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
73        self.topics.size_of(ops) + self.data.size_of(ops)
74    }
75}
76
77impl LogEntry {
78    /// Calculates the bloom of this log entry.
79    pub fn bloom(&self) -> Bloom {
80        self.topics.iter().fold(
81            Bloom::from(BloomInput::Raw(self.address.as_bytes())),
82            |mut b, t| {
83                b.accrue(BloomInput::Raw(t.as_bytes()));
84                b
85            },
86        )
87    }
88}
89
90pub fn build_bloom(logs: &[LogEntry]) -> Bloom {
91    logs.iter()
92        .map(LogEntry::bloom)
93        .fold(Bloom::default(), |mut acc, bloom| {
94            acc.accrue_bloom(&bloom);
95            acc
96        })
97}
98
99/// Log localized in a blockchain.
100#[derive(Default, Debug, PartialEq, Clone)]
101pub struct LocalizedLogEntry {
102    /// Plain log entry.
103    pub entry: LogEntry,
104    /// Block in which this log was created.
105    pub block_hash: H256,
106    /// Epoch number.
107    pub epoch_number: BlockNumber,
108    /// Timestamp of the block in which this log was created.
109    pub block_timestamp: Option<u64>,
110    /// Hash of transaction in which this log was created.
111    pub transaction_hash: H256,
112    /// Index of transaction within block.
113    pub transaction_index: usize,
114    /// Log position in the epoch.
115    pub log_index: usize,
116    /// Log position in the transaction.
117    pub transaction_log_index: usize,
118}
119
120impl Deref for LocalizedLogEntry {
121    type Target = LogEntry;
122
123    fn deref(&self) -> &Self::Target { &self.entry }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::LogEntry;
129    use crate::bytes::Bytes;
130    use cfx_types::{Address, Bloom, Space, H256};
131    use rlp_derive::RlpEncodable;
132
133    #[derive(PartialEq, Eq, RlpEncodable)]
134    pub struct LogEntryOld {
135        pub address: Address,
136        pub topics: Vec<H256>,
137        pub data: Bytes,
138    }
139
140    #[test]
141    fn test_empty_log_bloom() {
142        let bloom = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse::<Bloom>().unwrap();
143        let address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
144            .parse::<Address>()
145            .unwrap();
146        let log = LogEntry {
147            address,
148            topics: vec![],
149            data: vec![],
150            space: Space::Native,
151        };
152        assert_eq!(log.bloom(), bloom);
153    }
154
155    #[test]
156    fn test_rlp() {
157        let address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
158            .parse::<Address>()
159            .unwrap();
160
161        // check RLP for new version
162        let log = LogEntry {
163            address,
164            topics: vec![],
165            data: vec![],
166            space: Space::Ethereum,
167        };
168
169        assert_eq!(log, rlp::decode(&rlp::encode(&log)).unwrap());
170
171        // check RLP for old version
172        let log_old = LogEntryOld {
173            address,
174            topics: vec![],
175            data: vec![],
176        };
177
178        let log_new: LogEntry = rlp::decode(&rlp::encode(&log_old)).unwrap();
179
180        assert_eq!(
181            log_new,
182            LogEntry {
183                address,
184                topics: vec![],
185                data: vec![],
186                space: Space::Native,
187            }
188        );
189    }
190}