diff --git a/crates/consensus/src/receipt/receipts.rs b/crates/consensus/src/receipt/receipts.rs index 1446e4ede47..9c3a2e1ac01 100644 --- a/crates/consensus/src/receipt/receipts.rs +++ b/crates/consensus/src/receipt/receipts.rs @@ -40,14 +40,14 @@ impl Receipt where T: AsRef, { - /// Calculates [`Log`]'s bloom filter. this is slow operation and [ReceiptWithBloom] can - /// be used to cache this value. + /// Calculates [`Log`]'s bloom filter. This is slow operation and + /// [`ReceiptWithBloom`] can be used to cache this value. pub fn bloom_slow(&self) -> Bloom { self.logs.iter().map(AsRef::as_ref).collect() } - /// Calculates the bloom filter for the receipt and returns the [ReceiptWithBloom] container - /// type. + /// Calculates the bloom filter for the receipt and returns the + /// [`ReceiptWithBloom`] container type. pub fn with_bloom(self) -> ReceiptWithBloom { ReceiptWithBloom { logs_bloom: self.bloom_slow(), receipt: self } } diff --git a/crates/rpc-types-eth/src/block.rs b/crates/rpc-types-eth/src/block.rs index abb9c90d4a5..a4dc31753f1 100644 --- a/crates/rpc-types-eth/src/block.rs +++ b/crates/rpc-types-eth/src/block.rs @@ -1209,6 +1209,7 @@ mod tests { // #[test] + #[cfg(feature = "serde")] fn deserde_tenderly_block() { let s = include_str!("../testdata/tenderly.sepolia.json"); let _block: Block = serde_json::from_str(s).unwrap(); diff --git a/crates/rpc-types-eth/src/fee.rs b/crates/rpc-types-eth/src/fee.rs index f0046c32b4c..b43b7a48840 100644 --- a/crates/rpc-types-eth/src/fee.rs +++ b/crates/rpc-types-eth/src/fee.rs @@ -151,6 +151,7 @@ mod tests { } #[test] + #[cfg(feature = "serde")] fn test_fee_hist_null_gas_used_ratio() { let json = r#"{"oldestBlock": "0x0", "gasUsedRatio": null}"#; let _actual = serde_json::from_str::(json).unwrap(); diff --git a/crates/rpc-types-eth/src/filter.rs b/crates/rpc-types-eth/src/filter.rs index 3bca51199e2..4360e86ecc9 100644 --- a/crates/rpc-types-eth/src/filter.rs +++ b/crates/rpc-types-eth/src/filter.rs @@ -1,9 +1,10 @@ use crate::{BlockNumberOrTag, Log as RpcLog, Transaction}; -use alloc::{string::String, vec::Vec}; +use alloc::{borrow::Cow, string::String, vec::Vec}; +use alloy_eips::BlockNumHash; use alloy_primitives::{ keccak256, map::{hash_set, HashSet}, - Address, BlockHash, Bloom, BloomInput, B256, U256, U64, + Address, BlockHash, Bloom, BloomInput, Log, LogData, B256, U256, U64, }; use core::{ hash::Hash, @@ -14,47 +15,51 @@ use itertools::{ Itertools, }; -/// Helper type to represent a bloom filter used for matching logs. -#[derive(Debug, Default)] -pub struct BloomFilter(Vec); +/// FilterSet is a set of values that will be used to filter logs. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(from = "HashSet"))] +pub struct FilterSet { + set: HashSet, -impl From> for BloomFilter { - fn from(src: Vec) -> Self { - Self(src) - } + #[cfg(feature = "std")] + #[cfg_attr(feature = "serde", serde(skip, default))] + bloom: std::sync::OnceLock, } -impl BloomFilter { - /// Returns whether the given bloom matches the list of Blooms in the current filter. - /// If the filter is empty (the list is empty), then any bloom matches - /// Otherwise, there must be at least one match for the BloomFilter to match. - pub fn matches(&self, bloom: Bloom) -> bool { - self.0.is_empty() || self.0.iter().any(|a| bloom.contains(a)) +impl Default for FilterSet { + fn default() -> Self { + Self { + set: Default::default(), + #[cfg(feature = "std")] + bloom: Default::default(), + } } } -/// FilterSet is a set of values that will be used to filter logs. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize))] -pub struct FilterSet(HashSet); - impl From for FilterSet { fn from(src: T) -> Self { - Self(core::iter::once(src).collect()) + Self { set: core::iter::once(src).collect(), ..Default::default() } } } impl Hash for FilterSet { fn hash(&self, state: &mut H) { - for value in &self.0 { + for value in &self.set { value.hash(state); } } } +impl From> for FilterSet { + fn from(src: HashSet) -> Self { + Self { set: src, ..Default::default() } + } +} + impl From> for FilterSet { fn from(src: Vec) -> Self { - Self(src.into_iter().collect()) + src.into_iter().collect::>().into() } } @@ -70,14 +75,14 @@ impl From> for FilterSet { impl From>> for FilterSet { fn from(src: ValueOrArray>) -> Self { match src { - ValueOrArray::Value(None) => Self(Default::default()), + ValueOrArray::Value(None) => Default::default(), ValueOrArray::Value(Some(val)) => val.into(), ValueOrArray::Array(arr) => { // If the array contains at least one `null` (ie. None), as it's considered // a "wildcard" value, the whole filter should be treated as matching everything, // thus is empty. if arr.iter().contains(&None) { - Self(Default::default()) + Default::default() } else { // Otherwise, we flatten the array, knowing there are no `None` values arr.into_iter().flatten().collect::>().into() @@ -92,39 +97,94 @@ impl IntoIterator for FilterSet { type IntoIter = hash_set::IntoIter; fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + self.set.into_iter() } } impl FromIterator for FilterSet { fn from_iter>(iter: I) -> Self { - Self(HashSet::from_iter(iter)) + HashSet::from_iter(iter).into() } } impl FilterSet { /// Returns whether the filter is empty pub fn is_empty(&self) -> bool { - self.0.is_empty() + self.set.is_empty() + } + + /// Returns the number of values in the filter + pub fn len(&self) -> usize { + self.set.len() } /// Returns whether the given value matches the filter. If the filter is empty /// any value matches. Otherwise, the filter must include the value pub fn matches(&self, value: &T) -> bool { - self.is_empty() || self.0.contains(value) + self.is_empty() || self.set.contains(value) } /// Returns an iterator over the underlying HashSet. Values are visited /// in an arbitrary order. pub fn iter(&self) -> hash_set::Iter<'_, T> { - self.0.iter() + self.set.iter() + } + + /// Check if the filter contains the given value + pub fn contains(&self, value: &T) -> bool { + self.set.contains(value) + } + + /// Drop the bloom filter if it exists. This should be invoked by any + /// method taking `&mut self`. + fn unseal(&mut self) { + #[cfg(feature = "std")] + self.bloom.take(); + } + + /// Insert a value into the filter + pub fn insert(&mut self, value: T) -> bool { + self.unseal(); + self.set.insert(value) + } + + /// Remove a value from the filter (if present) + pub fn remove(&mut self, value: &T) -> bool { + if self.contains(value) { + self.unseal(); + self.set.remove(value) + } else { + false + } } } impl + Eq + Hash> FilterSet { + /// Create a bloom filter from the set of values. + fn make_bloom(&self) -> Bloom { + self.set.iter().fold(Bloom::default(), |mut acc, set| { + acc.accrue(BloomInput::Raw(set.as_ref())); + acc + }) + } + + /// Get a reference to the BloomFilter. + #[cfg(feature = "std")] + pub fn bloom_ref(&self) -> &Bloom { + self.bloom.get_or_init(|| self.make_bloom()) + } + /// Returns a list of Bloom (BloomFilter) corresponding to the filter's values - pub fn to_bloom_filter(&self) -> BloomFilter { - self.0.iter().map(|a| BloomInput::Raw(a.as_ref()).into()).collect::>().into() + pub fn bloom(&self) -> Cow<'_, Bloom> { + #[cfg(feature = "std")] + { + Cow::Borrowed(self.bloom_ref()) + } + + #[cfg(not(feature = "std"))] + { + Cow::Owned(self.make_bloom()) + } } } @@ -134,7 +194,7 @@ impl FilterSet { /// - If the filter has only 1 value, it returns the single value /// - Otherwise it returns an array of values pub fn to_value_or_array(&self) -> Option> { - let mut values = self.0.iter().cloned().collect::>(); + let mut values = self.set.iter().cloned().collect::>(); match values.len() { 0 => None, 1 => Some(ValueOrArray::Value(values.pop().expect("values length is one"))), @@ -149,7 +209,8 @@ pub type Topic = FilterSet; impl Topic { /// Extends the topic with a value that can be converted into a Topic pub fn extend>(mut self, value: T) -> Self { - self.0.extend(value.into().0); + self.unseal(); + self.set.extend(value.into()); self } } @@ -573,10 +634,172 @@ impl Filter { } } - /// Returns true if at least one topic is set + /// Returns `true` if at least one topic is set pub fn has_topics(&self) -> bool { self.topics.iter().any(|t| !t.is_empty()) } + + /// Create the [`Bloom`] for the addresses. + pub fn address_bloom(&self) -> Cow<'_, Bloom> { + self.address.bloom() + } + + /// Create a [`Bloom`] for each topic filter. + pub fn topics_bloom(&self) -> [Cow<'_, Bloom>; 4] { + self.topics.each_ref().map(|t| t.bloom()) + } + + /// Check whether the provided bloom contains all topics and the address we + /// wish to filter on. + pub fn matches_bloom(&self, bloom: Bloom) -> bool { + bloom.contains(&self.address_bloom()) + && self.topics_bloom().iter().all(|topic_bloom| bloom.contains(topic_bloom)) + } + + /// Returns `true` if the filter matches the given topics. + pub fn matches_topics(&self, topics: &[B256]) -> bool { + self.topics.iter().zip_longest(topics.iter()).all(|topic| match topic { + Both(filter, log) => filter.matches(log), + Left(filter) => filter.is_empty(), + Right(_) => false, + }) + } + + /// Returns `true` if the filter matches the given address. + pub fn matches_address(&self, address: Address) -> bool { + self.address.matches(&address) + } + + /// Returns `true` if the block matches the filter. + pub fn matches_block_range(&self, block_number: u64) -> bool { + let mut res = true; + + if let Some(BlockNumberOrTag::Number(num)) = self.block_option.get_from_block() { + if *num > block_number { + res = false; + } + } + + if let Some(to) = self.block_option.get_to_block() { + match to { + BlockNumberOrTag::Number(num) => { + if *num < block_number { + res = false; + } + } + BlockNumberOrTag::Earliest => { + res = false; + } + _ => {} + } + } + res + } + + /// Returns `true` if the filter matches the given block hash. + pub fn matches_block_hash(&self, block_hash: B256) -> bool { + match self.block_option { + FilterBlockOption::AtBlockHash(hash) => hash == block_hash, + FilterBlockOption::Range { .. } => false, + } + } + + /// Returns `true` if the filter matches the given block. Checks both the + /// block number and hash. + pub fn matches_block(&self, block: &BlockNumHash) -> bool { + self.matches_block_range(block.number) || self.matches_block_hash(block.hash) + } + + /// Returns `true` if either of the following is true: + /// - the filter and log are both pending + /// - the filter matches the block in the log. I.e. [`Self::matches_block`] returns true when + /// called with the block number and hash from the log. + pub fn matches_log_block(&self, log: &crate::Log) -> bool { + if self.is_pending_block_filter() { + // We skip checking block_hash, as a mismatch here would indicate + // invalid log data + return log.block_number.is_none(); + } + + // If the data is invalid, we'll shortcut return false + let Some(number) = log.block_number else { return false }; + let Some(hash) = log.block_hash else { return false }; + let num_hash = BlockNumHash { number, hash }; + + self.matches_block(&num_hash) + } + + /// Check if a [`Log`] matches the filter. This will check topics and + /// address. + /// + /// This checks [`Log`], the raw, primitive type carrying un-parsed + /// [`LogData`]. + /// + /// - For un-parsed RPC logs [`crate::Log`], see [`Self::rpc_matches`] and + /// [`Self::rpc_matches_parsed`]. + /// - For parsed [`Log`]s (e.g. those returned by a contract), see [`Self::matches_parsed`]. + pub fn matches(&self, log: &Log) -> bool { + if !self.matches_address(log.address) { + return false; + } + + self.matches_topics(log.topics()) + } + + /// Check if a [`crate::Log`] matches the filter. This will check topics, + /// address, and block option. + /// + /// This function checks [`crate::Log`], the RPC type carrying + /// un-parsed [`LogData`]. + /// + /// - For parsed [`Log`]s (e.g. those returned by a contract), see [`Self::matches_parsed`]. + /// - For parsed [`crate::Log`]s (e.g. those returned by a contract), see + /// [`Self::rpc_matches`]. + pub fn rpc_matches(&self, log: &crate::Log) -> bool { + self.matches_log_block(log) && self.matches(&log.inner) + } + + /// Check if a parsed [`Log`] matches the filter. This will check + /// topics and address. + /// + /// This function checks [`Log`], the primitive `Log` type carrying + /// some parsed `T`, usually implementing [`SolEvent`]. + /// + /// - For un-parsed [`Log`] see [`Self::matches`]. + /// - For un-parsed RPC logs [`crate::Log`] see [`Self::rpc_matches`]. + /// - For parsed RPC [`crate::Log`]s (e.g. those returned by a contract), see + /// [`Self::rpc_matches_parsed`]. + /// + /// [`SolEvent`]: alloy_sol_types::SolEvent + pub fn matches_parsed(&self, log: &T) -> bool + where + T: AsRef>, + for<'a> &'a U: Into, + { + let log = log.as_ref().reserialize(); + self.matches(&log) + } + + /// Check if a parsed rpc log [`crate::Log`] matches the filter. This + /// will check topics, address, and block option. + /// + /// If the RPC log block hash or number is `None` (indicating an uncled + /// block), this function will return `false`. + /// + /// This function checks [`crate::Log`], the RPC type carrying some + /// parsed `T`, usually implementing [`SolEvent`]. + /// + /// - For un-parsed [`Log`] see [`Self::matches`]. + /// - For parsed [`Log`]s (e.g. those returned by a contract), see [`Self::matches_parsed`]. + /// - For un-parsed RPC logs [`crate::Log`] see [`Self::rpc_matches`]. + /// + /// [`SolEvent`]: alloy_sol_types::SolEvent + pub fn rpc_matches_parsed(&self, log: &crate::Log) -> bool + where + for<'a> &'a U: Into, + { + self.matches_log_block(log) && self.matches_parsed(log) + } } #[cfg(feature = "serde")] @@ -735,7 +958,8 @@ impl<'de> serde::Deserialize<'de> for Filter { } } -/// Union type for representing a single value or a vector of values inside a filter +/// Union type for representing a single value or a vector of values inside a +/// filter. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum ValueOrArray { /// A single value @@ -839,135 +1063,6 @@ where } } -/// Support for matching [Filter]s -#[derive(Debug, Default)] -pub struct FilteredParams { - /// The original filter, if any - pub filter: Option, -} - -impl FilteredParams { - /// Creates a new wrapper type for a [Filter], if any with flattened topics, that can be used - /// for matching - pub fn new(filter: Option) -> Self { - filter.map_or_else(Default::default, |filter| Self { filter: Some(filter) }) - } - - /// Returns the [BloomFilter] for the given address - pub fn address_filter(address: &FilterSet
) -> BloomFilter { - address.to_bloom_filter() - } - - /// Returns the [BloomFilter] for the given topics - pub fn topics_filter(topics: &[FilterSet]) -> Vec { - topics.iter().map(|t| t.to_bloom_filter()).collect() - } - - /// Returns `true` if the bloom matches the topics - pub fn matches_topics(bloom: Bloom, topic_filters: &[BloomFilter]) -> bool { - if topic_filters.is_empty() { - return true; - } - - // for each filter, iterate through the list of filter blooms. for each set of filter - // (each BloomFilter), the given `bloom` must match at least one of them, unless the list is - // empty (no filters). - for filter in topic_filters { - if !filter.matches(bloom) { - return false; - } - } - true - } - - /// Returns `true` if the bloom contains one of the address blooms, or the address blooms - /// list is empty (thus, no filters) - pub fn matches_address(bloom: Bloom, address_filter: &BloomFilter) -> bool { - address_filter.matches(bloom) - } - - /// Returns true if the filter matches the given block number - pub fn filter_block_range(&self, block_number: u64) -> bool { - if self.filter.is_none() { - return true; - } - let filter = self.filter.as_ref().unwrap(); - let mut res = true; - - if let Some(BlockNumberOrTag::Number(num)) = filter.block_option.get_from_block() { - if *num > block_number { - res = false; - } - } - - if let Some(to) = filter.block_option.get_to_block() { - match to { - BlockNumberOrTag::Number(num) => { - if *num < block_number { - res = false; - } - } - BlockNumberOrTag::Earliest => { - res = false; - } - _ => {} - } - } - res - } - - /// Returns `true` if the filter matches the given block hash. - pub fn filter_block_hash(&self, block_hash: B256) -> bool { - if let Some(h) = self.filter.as_ref().and_then(|f| f.get_block_hash()) { - if h != block_hash { - return false; - } - } - true - } - - /// Return `true` if the filter configured to match pending block. - /// This means that both from_block and to_block are set to the pending tag. - /// It calls [`Filter::is_pending_block_filter`] undercover. - pub fn is_pending_block_filter(&self) -> bool { - self.filter.as_ref().is_some_and(|f| f.is_pending_block_filter()) - } - - /// Returns `true` if the filter matches the given address. - pub fn filter_address(&self, address: &Address) -> bool { - self.filter.as_ref().map(|f| f.address.matches(address)).unwrap_or(true) - } - - /// Returns `true` if the log matches the given topics - pub fn filter_topics(&self, log_topics: &[B256]) -> bool { - let topics = match self.filter.as_ref() { - None => return true, - Some(f) => &f.topics, - }; - for topic_tuple in topics.iter().zip_longest(log_topics.iter()) { - match topic_tuple { - // We exhausted the `log.topics`, so if there's a filter set for - // this topic index, there is no match. Otherwise (empty filter), continue. - Left(filter_topic) => { - if !filter_topic.is_empty() { - return false; - } - } - // We exhausted the filter topics, therefore any subsequent log topic - // will match. - Right(_) => return true, - // Check that `log_topic` is included in `filter_topic` - Both(filter_topic, log_topic) => { - if !filter_topic.matches(log_topic) { - return false; - } - } - } - } - true - } -} - /// Response of the `eth_getFilterChanges` RPC. #[derive(Default, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] @@ -1152,6 +1247,7 @@ impl From> for FilterId { } } } + /// Specifies the kind of information you wish to receive from the `eth_newPendingTransactionFilter` /// RPC endpoint. /// @@ -1203,6 +1299,8 @@ impl<'a> serde::Deserialize<'a> for PendingTransactionFilterKind { #[cfg(test)] mod tests { use super::*; + use alloy_primitives::LogData; + #[cfg(feature = "serde")] use serde_json::json; use similar_asserts::assert_eq; @@ -1376,14 +1474,6 @@ mod tests { assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2, t3_padded]})); } - fn build_bloom(address: Address, topic1: B256, topic2: B256) -> Bloom { - let mut block_bloom = Bloom::default(); - block_bloom.accrue(BloomInput::Raw(&address[..])); - block_bloom.accrue(BloomInput::Raw(&topic1[..])); - block_bloom.accrue(BloomInput::Raw(&topic2[..])); - block_bloom - } - fn topic_filter(topic1: B256, topic2: B256, topic3: B256) -> Filter { Filter { block_option: Default::default(), @@ -1399,32 +1489,13 @@ mod tests { #[test] fn can_detect_different_topics() { - let topic1 = B256::random(); - let topic2 = B256::random(); - let topic3 = B256::random(); - - let topics = topic_filter(topic1, topic2, topic3).topics; - let topics_bloom = FilteredParams::topics_filter(&topics); - assert!(!FilteredParams::matches_topics( - build_bloom(Address::random(), B256::random(), B256::random()), - &topics_bloom - )); - } + let topic1 = B256::with_last_byte(1); + let topic2 = B256::with_last_byte(2); + let topic3 = B256::with_last_byte(3); - #[test] - fn can_match_topic() { - let topic1 = B256::random(); - let topic2 = B256::random(); - let topic3 = B256::random(); - - let topics = topic_filter(topic1, topic2, topic3).topics; - let _topics_bloom = FilteredParams::topics_filter(&topics); - - let topics_bloom = FilteredParams::topics_filter(&topics); - assert!(FilteredParams::matches_topics( - build_bloom(Address::random(), topic1, topic2), - &topics_bloom - )); + let filter = topic_filter(topic1, topic2, topic3); + + assert!(filter.matches_topics(&[topic1, topic2, topic3])); } #[test] @@ -1434,25 +1505,25 @@ mod tests { address: Default::default(), topics: Default::default(), }; - let topics = filter.topics; - let topics_bloom = FilteredParams::topics_filter(&topics); - assert!(FilteredParams::matches_topics( - build_bloom(Address::random(), B256::random(), B256::random()), - &topics_bloom - )); + assert!(filter.matches_topics(&[ + B256::with_last_byte(1), + B256::with_last_byte(2), + B256::with_last_byte(3), + B256::with_last_byte(4), + ])); } #[test] fn can_match_address_and_topics() { - let rng_address = Address::random(); - let topic1 = B256::random(); - let topic2 = B256::random(); - let topic3 = B256::random(); + let address = Address::with_last_byte(1); + let topic1 = B256::with_last_byte(2); + let topic2 = B256::with_last_byte(3); + let topic3 = B256::with_last_byte(4); let filter = Filter { block_option: Default::default(), - address: rng_address.into(), + address: address.into(), topics: [ topic1.into(), vec![topic2, topic3].into(), @@ -1460,26 +1531,19 @@ mod tests { Default::default(), ], }; - let topics = filter.topics; - - let address_filter = FilteredParams::address_filter(&filter.address); - let topics_filter = FilteredParams::topics_filter(&topics); - assert!( - FilteredParams::matches_address( - build_bloom(rng_address, topic1, topic2), - &address_filter - ) && FilteredParams::matches_topics( - build_bloom(rng_address, topic1, topic2), - &topics_filter - ) - ); + + let log = + Log { address, data: LogData::new_unchecked(vec![topic1, topic2], Default::default()) }; + + assert!(filter.matches(&log)); } #[test] fn can_match_topics_wildcard() { - let topic1 = B256::random(); - let topic2 = B256::random(); - let topic3 = B256::random(); + let address = Address::with_last_byte(1); + let topic1 = B256::with_last_byte(2); + let topic2 = B256::with_last_byte(3); + let topic3 = B256::with_last_byte(4); let filter = Filter { block_option: Default::default(), @@ -1491,65 +1555,62 @@ mod tests { Default::default(), ], }; - let topics = filter.topics; - let topics_bloom = FilteredParams::topics_filter(&topics); - assert!(FilteredParams::matches_topics( - build_bloom(Address::random(), topic1, topic2), - &topics_bloom - )); + let log = + Log { address, data: LogData::new_unchecked(vec![topic1, topic2], Default::default()) }; + + assert!(filter.matches(&log)); } #[test] fn can_match_topics_wildcard_mismatch() { + let address = Address::with_last_byte(1); + let topic1 = B256::with_last_byte(2); + let topic2 = B256::with_last_byte(3); + let bad_topic = B256::with_last_byte(4); + let filter = Filter { block_option: Default::default(), address: Default::default(), topics: [ Default::default(), - vec![B256::random(), B256::random()].into(), + vec![topic1, topic2].into(), Default::default(), Default::default(), ], }; - let topics_input = filter.topics; - let topics_bloom = FilteredParams::topics_filter(&topics_input); - assert!(!FilteredParams::matches_topics( - build_bloom(Address::random(), B256::random(), B256::random()), - &topics_bloom - )); + let log = Log { + address, + data: LogData::new_unchecked(vec![bad_topic, bad_topic], Default::default()), + }; + + assert!(!filter.matches(&log)); } #[test] fn can_match_address_filter() { - let rng_address = Address::random(); + let address = Address::with_last_byte(1); let filter = Filter { block_option: Default::default(), - address: rng_address.into(), + address: address.into(), topics: Default::default(), }; - let address_bloom = FilteredParams::address_filter(&filter.address); - assert!(FilteredParams::matches_address( - build_bloom(rng_address, B256::random(), B256::random(),), - &address_bloom - )); + + assert!(filter.matches_address(address)); } #[test] fn can_detect_different_address() { - let bloom_address = Address::random(); - let rng_address = Address::random(); + let address = Address::with_last_byte(1); + let bad_address = Address::with_last_byte(2); let filter = Filter { block_option: Default::default(), - address: rng_address.into(), + address: address.into(), topics: Default::default(), }; - let address_bloom = FilteredParams::address_filter(&filter.address); - assert!(!FilteredParams::matches_address( - build_bloom(bloom_address, B256::random(), B256::random(),), - &address_bloom - )); + + assert!(!filter.matches_address(bad_address)); } #[test] @@ -1737,8 +1798,6 @@ mod tests { ], }; assert!(filter.is_pending_block_filter()); - let filter_params = FilteredParams::new(Some(filter)); - assert!(filter_params.is_pending_block_filter()); let filter = Filter { block_option: FilterBlockOption::Range { @@ -1766,8 +1825,6 @@ mod tests { ], }; assert!(!filter.is_pending_block_filter()); - let filter_params = FilteredParams::new(Some(filter)); - assert!(!filter_params.is_pending_block_filter()); } #[test] @@ -1778,12 +1835,12 @@ mod tests { topic = topic.extend(U256::from(123)).extend(Address::random()).extend(true).extend([0u8; 32]); - assert_eq!(topic.0.len(), 4); + assert_eq!(topic.set.len(), 4); topic = topic.extend(U256::from(123)); - assert_eq!(topic.0.len(), 4); + assert_eq!(topic.set.len(), 4); topic = topic.extend(U256::from(456)); - assert_eq!(topic.0.len(), 5); + assert_eq!(topic.set.len(), 5); } } diff --git a/crates/rpc-types-eth/src/index.rs b/crates/rpc-types-eth/src/index.rs index 7135856367e..f5ed27e0bba 100644 --- a/crates/rpc-types-eth/src/index.rs +++ b/crates/rpc-types-eth/src/index.rs @@ -84,6 +84,7 @@ impl<'a> serde::Deserialize<'a> for Index { mod tests { use super::*; use rand::{thread_rng, Rng}; + #[cfg(feature = "serde")] use serde_json::json; use similar_asserts::assert_eq; diff --git a/crates/rpc-types-eth/src/simulate.rs b/crates/rpc-types-eth/src/simulate.rs index 19d33bca844..510d6bbebe4 100644 --- a/crates/rpc-types-eth/src/simulate.rs +++ b/crates/rpc-types-eth/src/simulate.rs @@ -156,6 +156,7 @@ pub struct SimulateError { mod tests { use super::*; use alloy_primitives::{Address, TxKind}; + #[cfg(feature = "serde")] use serde_json::json; use similar_asserts::assert_eq; diff --git a/crates/rpc-types-eth/src/syncing.rs b/crates/rpc-types-eth/src/syncing.rs index 2baa8a26413..62a909f1e29 100644 --- a/crates/rpc-types-eth/src/syncing.rs +++ b/crates/rpc-types-eth/src/syncing.rs @@ -183,6 +183,7 @@ mod tests { use super::*; #[test] + #[cfg(feature = "serde")] fn test_sync_info_serialization() { let sync_info = SyncInfo { starting_block: U256::from(0x3cbed5), @@ -204,6 +205,7 @@ mod tests { } #[test] + #[cfg(feature = "serde")] fn test_peer_info_serialization() { let peer_info = PeerInfo { id: Some("peer_id_123".to_string()), @@ -231,6 +233,7 @@ mod tests { } #[test] + #[cfg(feature = "serde")] fn test_sync_status_serialization() { let sync_status = SyncStatus::Info(Box::new(SyncInfo { starting_block: U256::from(0x3cbed5), diff --git a/crates/rpc-types-eth/src/transaction/mod.rs b/crates/rpc-types-eth/src/transaction/mod.rs index 8e368df3c05..82880ddae16 100644 --- a/crates/rpc-types-eth/src/transaction/mod.rs +++ b/crates/rpc-types-eth/src/transaction/mod.rs @@ -538,6 +538,7 @@ mod tests { } #[test] + #[cfg(feature = "serde")] fn serde_tx_from_contract_mod() { let rpc_tx = r#"{"hash":"0x018b2331d461a4aeedf6a1f9cc37463377578244e6a35216057a8370714e798f","nonce":"0x1","blockHash":"0x6e4e53d1de650d5a5ebed19b38321db369ef1dc357904284ecf4d89b8834969c","blockNumber":"0x2","transactionIndex":"0x0","from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0x5fbdb2315678afecb367f032d93f642f64180aa3","value":"0x0","gasPrice":"0x3a29f0f8","gas":"0x1c9c380","maxFeePerGas":"0xba43b7400","maxPriorityFeePerGas":"0x5f5e100","input":"0xd09de08a","r":"0xd309309a59a49021281cb6bb41d164c96eab4e50f0c1bd24c03ca336e7bc2bb7","s":"0x28a7f089143d0a1355ebeb2a1b9f0e5ad9eca4303021c1400d61bc23c9ac5319","v":"0x0","yParity":"0x0","chainId":"0x7a69","accessList":[],"type":"0x2"}"#; @@ -546,6 +547,7 @@ mod tests { } #[test] + #[cfg(feature = "serde")] fn test_gas_price_present() { let blob_rpc_tx = r#"{"blockHash":"0x1732a5fe86d54098c431fa4fea34387b650e41dbff65ca554370028172fcdb6a","blockNumber":"0x3","from":"0x7435ed30a8b4aeb0877cef0c6e8cffe834eb865f","gas":"0x186a0","gasPrice":"0x281d620e","maxFeePerGas":"0x281d620e","maxPriorityFeePerGas":"0x1","maxFeePerBlobGas":"0x20000","hash":"0xb0ebf0d8fca6724d5111d0be9ac61f0e7bf174208e0fafcb653f337c72465b83","input":"0xdc4c8669df128318656d6974","nonce":"0x8","to":"0x7dcd17433742f4c0ca53122ab541d0ba67fc27df","transactionIndex":"0x0","value":"0x3","type":"0x3","accessList":[{"address":"0x7dcd17433742f4c0ca53122ab541d0ba67fc27df","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000","0x462708a3c1cd03b21605715d090136df64e227f7e7792f74bb1bd7a8288f8801"]}],"chainId":"0xc72dd9d5e883e","blobVersionedHashes":["0x015a4cab4911426699ed34483de6640cf55a568afc5c5edffdcbd8bcd4452f68"],"v":"0x0","r":"0x478385a47075dd6ba56300b623038052a6e4bb03f8cfc53f367712f1c1d3e7de","s":"0x2f79ed9b154b0af2c97ddfc1f4f76e6c17725713b6d44ea922ca4c6bbc20775c","yParity":"0x0"}"#; let legacy_rpc_tx = r#"{"blockHash":"0x7e5d03caac4eb2b613ae9c919ef3afcc8ed0e384f31ee746381d3c8739475d2a","blockNumber":"0x4","from":"0x7435ed30a8b4aeb0877cef0c6e8cffe834eb865f","gas":"0x5208","gasPrice":"0x23237dee","hash":"0x3f38cdc805c02e152bfed34471a3a13a786fed436b3aec0c3eca35d23e2cdd2c","input":"0x","nonce":"0xc","to":"0x4dde844b71bcdf95512fb4dc94e84fb67b512ed8","transactionIndex":"0x0","value":"0x1","type":"0x0","chainId":"0xc72dd9d5e883e","v":"0x18e5bb3abd10a0","r":"0x3d61f5d7e93eecd0669a31eb640ab3349e9e5868a44c2be1337c90a893b51990","s":"0xc55f44ba123af37d0e73ed75e578647c3f473805349936f64ea902ea9e03bc7"}"#; @@ -561,6 +563,7 @@ mod tests { // #[test] + #[cfg(feature = "serde")] fn deserialize_7702_v() { let raw = r#"{"blockHash":"0xb14eac260f0cb7c3bbf4c9ff56034defa4f566780ed3e44b7a79b6365d02887c","blockNumber":"0xb022","from":"0x6d2d4e1c2326a069f36f5d6337470dc26adb7156","gas":"0xf8ac","gasPrice":"0xe07899f","maxFeePerGas":"0xe0789a0","maxPriorityFeePerGas":"0xe078998","hash":"0xadc3f24d05f05f1065debccb1c4b033eaa35917b69b343d88d9062cdf8ecad83","input":"0x","nonce":"0x1a","to":"0x6d2d4e1c2326a069f36f5d6337470dc26adb7156","transactionIndex":"0x0","value":"0x0","type":"0x4","accessList":[],"chainId":"0x1a5ee289c","authorizationList":[{"chainId":"0x1a5ee289c","address":"0x529f773125642b12a44bd543005650989eceaa2a","nonce":"0x1a","v":"0x0","r":"0x9b3de20cf8bd07f3c5c55c38c920c146f081bc5ab4580d0c87786b256cdab3c2","s":"0x74841956f4832bace3c02aed34b8f0a2812450da3728752edbb5b5e1da04497"}],"v":"0x1","r":"0xb3bf7d6877864913bba04d6f93d98009a5af16ee9c12295cd634962a2346b67c","s":"0x31ca4a874afa964ec7643e58c6b56b35b1bcc7698eb1b5e15e61e78b353bd42d","yParity":"0x1"}"#; let tx = serde_json::from_str::(raw).unwrap(); diff --git a/crates/rpc-types-eth/src/transaction/request.rs b/crates/rpc-types-eth/src/transaction/request.rs index 27b263e35db..72322262e01 100644 --- a/crates/rpc-types-eth/src/transaction/request.rs +++ b/crates/rpc-types-eth/src/transaction/request.rs @@ -1228,6 +1228,7 @@ pub struct BuildTransactionErr { mod tests { use super::*; use alloy_primitives::b256; + #[cfg(feature = "serde")] use alloy_serde::WithOtherFields; use assert_matches::assert_matches; use similar_asserts::assert_eq;