use crate::{block::BlockNumber, bytes::Bytes};
use cfx_types::{Address, Bloom, BloomInput, Space, H256};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use rlp::RlpStream;
use serde_derive::{Deserialize, Serialize};
use std::ops::Deref;
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LogEntry {
pub address: Address,
pub topics: Vec<H256>,
pub data: Bytes,
pub space: Space,
}
impl rlp::Encodable for LogEntry {
fn rlp_append(&self, s: &mut RlpStream) {
match self.space {
Space::Native => {
s.begin_list(3);
s.append(&self.address);
s.append_list(&self.topics);
s.append(&self.data);
}
Space::Ethereum => {
s.begin_list(4);
s.append(&self.address);
s.append_list(&self.topics);
s.append(&self.data);
s.append(&self.space);
}
}
}
}
impl rlp::Decodable for LogEntry {
fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
match rlp.item_count()? {
3 => Ok(LogEntry {
address: rlp.val_at(0)?,
topics: rlp.list_at(1)?,
data: rlp.val_at(2)?,
space: Space::Native,
}),
4 => Ok(LogEntry {
address: rlp.val_at(0)?,
topics: rlp.list_at(1)?,
data: rlp.val_at(2)?,
space: rlp.val_at(3)?,
}),
_ => Err(rlp::DecoderError::RlpInvalidLength),
}
}
}
impl MallocSizeOf for LogEntry {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.topics.size_of(ops) + self.data.size_of(ops)
}
}
impl LogEntry {
pub fn bloom(&self) -> Bloom {
self.topics.iter().fold(
Bloom::from(BloomInput::Raw(self.address.as_bytes())),
|mut b, t| {
b.accrue(BloomInput::Raw(t.as_bytes()));
b
},
)
}
}
pub fn build_bloom(logs: &[LogEntry]) -> Bloom {
logs.iter()
.map(LogEntry::bloom)
.fold(Bloom::default(), |mut acc, bloom| {
acc.accrue_bloom(&bloom);
acc
})
}
#[derive(Default, Debug, PartialEq, Clone)]
pub struct LocalizedLogEntry {
pub entry: LogEntry,
pub block_hash: H256,
pub epoch_number: BlockNumber,
pub transaction_hash: H256,
pub transaction_index: usize,
pub log_index: usize,
pub transaction_log_index: usize,
}
impl Deref for LocalizedLogEntry {
type Target = LogEntry;
fn deref(&self) -> &Self::Target { &self.entry }
}
#[cfg(test)]
mod tests {
use super::LogEntry;
use crate::bytes::Bytes;
use cfx_types::{Address, Bloom, Space, H256};
use rlp_derive::RlpEncodable;
#[derive(PartialEq, Eq, RlpEncodable)]
pub struct LogEntryOld {
pub address: Address,
pub topics: Vec<H256>,
pub data: Bytes,
}
#[test]
fn test_empty_log_bloom() {
let bloom = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse::<Bloom>().unwrap();
let address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
.parse::<Address>()
.unwrap();
let log = LogEntry {
address,
topics: vec![],
data: vec![],
space: Space::Native,
};
assert_eq!(log.bloom(), bloom);
}
#[test]
fn test_rlp() {
let address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6"
.parse::<Address>()
.unwrap();
let log = LogEntry {
address,
topics: vec![],
data: vec![],
space: Space::Ethereum,
};
assert_eq!(log, rlp::decode(&rlp::encode(&log)).unwrap());
let log_old = LogEntryOld {
address,
topics: vec![],
data: vec![],
};
let log_new: LogEntry = rlp::decode(&rlp::encode(&log_old)).unwrap();
assert_eq!(
log_new,
LogEntry {
address,
topics: vec![],
data: vec![],
space: Space::Native,
}
);
}
}