refactor(rpc): simplify the inner definitions of topics & address filters (#3876)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Nicolas Gotchac
2023-07-28 13:00:37 +02:00
committed by GitHub
parent d2cdd10ed2
commit 0892833842
7 changed files with 276 additions and 381 deletions

1
Cargo.lock generated
View File

@ -5792,6 +5792,7 @@ dependencies = [
name = "reth-rpc-types"
version = "0.1.0-alpha.4"
dependencies = [
"itertools 0.10.5",
"jsonrpsee-types",
"rand 0.8.5",
"reth-primitives",

View File

@ -22,7 +22,7 @@ crunchy = { version = "0.2.2", default-features = false, features = ["limit_256"
ruint = { version = "1.9.0", features = ["primitive-types", "rlp"] }
# Bloom
fixed-hash = { version = "0.8", default-features = false, features = ["rustc-hex"] }
fixed-hash = { version = "0.8", default-features = false, features = ["rustc-hex"] }
# crypto
secp256k1 = { workspace = true, default-features = false, features = [

View File

@ -19,6 +19,7 @@ reth-rlp.workspace = true
thiserror.workspace = true
# misc
itertools = "0.10"
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
jsonrpsee-types = { version = "0.18" }

View File

@ -1,4 +1,5 @@
use crate::Log as RpcLog;
use itertools::{EitherOrBoth::*, Itertools};
use jsonrpsee_types::SubscriptionId;
use reth_primitives::{
bloom::{Bloom, Input},
@ -9,13 +10,120 @@ use serde::{
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::ops::{Range, RangeFrom, RangeTo};
use std::{
collections::HashSet,
hash::Hash,
ops::{Range, RangeFrom, RangeTo},
};
/// Helper type to represent a bloom filter used for matching logs.
pub type BloomFilter = Vec<Option<Bloom>>;
#[derive(Default, Debug)]
pub struct BloomFilter(Vec<Bloom>);
impl From<Vec<Bloom>> for BloomFilter {
fn from(src: Vec<Bloom>) -> Self {
BloomFilter(src)
}
}
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 matche for the BloomFilter to match.
pub fn matches(&self, bloom: Bloom) -> bool {
self.0.is_empty() || self.0.iter().any(|a| bloom.contains_bloom(a))
}
}
#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize)]
/// FilterSet is a set of values that will be used to filter logs
pub struct FilterSet<T: Eq + Hash>(HashSet<T>);
impl<T: Eq + Hash> From<T> for FilterSet<T> {
fn from(src: T) -> Self {
FilterSet(HashSet::from([src]))
}
}
impl<T: Eq + Hash> From<Vec<T>> for FilterSet<T> {
fn from(src: Vec<T>) -> Self {
FilterSet(HashSet::from_iter(src.into_iter().map(Into::into)))
}
}
impl<T: Eq + Hash> From<ValueOrArray<T>> for FilterSet<T> {
fn from(src: ValueOrArray<T>) -> Self {
match src {
ValueOrArray::Value(val) => val.into(),
ValueOrArray::Array(arr) => arr.into(),
}
}
}
impl<T: Eq + Hash> From<ValueOrArray<Option<T>>> for FilterSet<T> {
fn from(src: ValueOrArray<Option<T>>) -> Self {
match src {
ValueOrArray::Value(None) => FilterSet(HashSet::new()),
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) {
FilterSet(HashSet::new())
} else {
// Otherwise, we flatten the array, knowing there are no `None` values
arr.into_iter().flatten().collect::<Vec<T>>().into()
}
}
}
}
}
impl<T: Eq + Hash> FilterSet<T> {
/// Returns wheter the filter is empty
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Returns whether the given value matches the filter. It 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)
}
}
impl<T: AsRef<[u8]> + Eq + Hash> FilterSet<T> {
/// Returns a list of Bloom (BloomFilter) corresponding to the filter's values
pub fn to_bloom_filter(&self) -> BloomFilter {
self.0.iter().map(|a| Input::Raw(a.as_ref()).into()).collect::<Vec<Bloom>>().into()
}
}
impl<T: Clone + Eq + Hash> FilterSet<T> {
/// Returns a ValueOrArray inside an Option, so that:
/// - If the filter is empty, it returns None
/// - If the filter has only 1 value, it returns the single value
/// - Otherwise it returns an array of values
/// This should be useful for serialization
pub fn to_value_or_array(&self) -> Option<ValueOrArray<T>> {
let mut values = self.0.iter().cloned().collect::<Vec<T>>();
match values.len() {
0 => None,
1 => Some(ValueOrArray::Value(values.pop().expect("values length is one"))),
_ => Some(ValueOrArray::Array(values)),
}
}
}
/// A single topic
pub type Topic = ValueOrArray<Option<H256>>;
pub type Topic = FilterSet<H256>;
impl From<U256> for Topic {
fn from(src: U256) -> Self {
Into::<H256>::into(src).into()
}
}
/// Represents the target range of blocks for the filter
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
@ -141,16 +249,16 @@ impl FilterBlockOption {
}
/// Filter for
#[derive(Default, Debug, PartialEq, Eq, Clone, Hash)]
#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct Filter {
/// Filter block options, specifying on which blocks the filter should
/// match.
// https://eips.ethereum.org/EIPS/eip-234
pub block_option: FilterBlockOption,
/// Address
pub address: Option<ValueOrArray<Address>>,
pub address: FilterSet<Address>,
/// Topics (maxmimum of 4)
pub topics: [Option<Topic>; 4],
pub topics: [Topic; 4],
}
impl Filter {
@ -279,7 +387,7 @@ impl Filter {
/// ```
#[must_use]
pub fn address<T: Into<ValueOrArray<Address>>>(mut self, address: T) -> Self {
self.address = Some(address.into());
self.address = address.into().into();
self
}
@ -300,28 +408,28 @@ impl Filter {
/// Sets topic0 (the event name for non-anonymous events)
#[must_use]
pub fn topic0<T: Into<Topic>>(mut self, topic: T) -> Self {
self.topics[0] = Some(topic.into());
self.topics[0] = topic.into();
self
}
/// Sets the 1st indexed topic
#[must_use]
pub fn topic1<T: Into<Topic>>(mut self, topic: T) -> Self {
self.topics[1] = Some(topic.into());
self.topics[1] = topic.into();
self
}
/// Sets the 2nd indexed topic
#[must_use]
pub fn topic2<T: Into<Topic>>(mut self, topic: T) -> Self {
self.topics[2] = Some(topic.into());
self.topics[2] = topic.into();
self
}
/// Sets the 3rd indexed topic
#[must_use]
pub fn topic3<T: Into<Topic>>(mut self, topic: T) -> Self {
self.topics[3] = Some(topic.into());
self.topics[3] = topic.into();
self
}
@ -348,64 +456,9 @@ impl Filter {
}
}
/// Flattens the topics using the cartesian product
fn flatten(&self) -> Vec<ValueOrArray<Option<H256>>> {
fn cartesian(lists: &[Vec<Option<H256>>]) -> Vec<Vec<Option<H256>>> {
let mut res = Vec::new();
let mut list_iter = lists.iter();
if let Some(first_list) = list_iter.next() {
for &i in first_list {
res.push(vec![i]);
}
}
for l in list_iter {
let mut tmp = Vec::new();
for r in res {
for &el in l {
let mut tmp_el = r.clone();
tmp_el.push(el);
tmp.push(tmp_el);
}
}
res = tmp;
}
res
}
let mut out = Vec::new();
let mut tmp = Vec::new();
for v in self.topics.iter() {
let v = if let Some(v) = v {
match v {
ValueOrArray::Value(s) => {
vec![*s]
}
ValueOrArray::Array(s) => {
if s.is_empty() {
vec![None]
} else {
s.clone()
}
}
}
} else {
vec![None]
};
tmp.push(v);
}
for v in cartesian(&tmp) {
out.push(ValueOrArray::Array(v));
}
out
}
/// Returns an iterator over all existing topics
pub fn topics(&self) -> impl Iterator<Item = &Topic> + '_ {
self.topics.iter().flatten()
}
/// Returns true if at least one topic is set
pub fn has_topics(&self) -> bool {
self.topics.iter().any(|t| t.is_some())
self.topics.iter().any(|t| !t.is_empty())
}
}
@ -429,27 +482,28 @@ impl Serialize for Filter {
FilterBlockOption::AtBlockHash(ref h) => s.serialize_field("blockHash", h)?,
}
if let Some(ref address) = self.address {
s.serialize_field("address", address)?;
if let Some(address) = self.address.to_value_or_array() {
s.serialize_field("address", &address)?;
}
let mut filtered_topics = Vec::new();
for i in 0..4 {
if self.topics[i].is_some() {
filtered_topics.push(&self.topics[i]);
} else {
// TODO: This can be optimized
if self.topics[i + 1..].iter().any(|x| x.is_some()) {
filtered_topics.push(&None);
}
let mut filtered_topics_len = 0;
for (i, topic) in self.topics.iter().enumerate() {
if !topic.is_empty() {
filtered_topics_len = i + 1;
}
filtered_topics.push(topic.to_value_or_array());
}
filtered_topics.truncate(filtered_topics_len);
s.serialize_field("topics", &filtered_topics)?;
s.end()
}
}
type RawAddressFilter = ValueOrArray<Option<Address>>;
type RawTopicsFilter = Vec<Option<ValueOrArray<Option<H256>>>>;
impl<'de> Deserialize<'de> for Filter {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@ -471,8 +525,8 @@ impl<'de> Deserialize<'de> for Filter {
let mut from_block: Option<Option<BlockNumberOrTag>> = None;
let mut to_block: Option<Option<BlockNumberOrTag>> = None;
let mut block_hash: Option<Option<H256>> = None;
let mut address: Option<Option<ValueOrArray<Address>>> = None;
let mut topics: Option<Option<Vec<Option<Topic>>>> = None;
let mut address: Option<Option<RawAddressFilter>> = None;
let mut topics: Option<Option<RawTopicsFilter>> = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
@ -534,16 +588,21 @@ impl<'de> Deserialize<'de> for Filter {
let from_block = from_block.unwrap_or_default();
let to_block = to_block.unwrap_or_default();
let block_hash = block_hash.unwrap_or_default();
let address = address.unwrap_or_default();
let address = address.flatten().map(|a| a.into()).unwrap_or_default();
let topics_vec = topics.flatten().unwrap_or_default();
// maximum allowed filter len
if topics_vec.len() > 4 {
return Err(serde::de::Error::custom("exceeded maximum topics len"))
}
let mut topics: [Option<Topic>; 4] = [None, None, None, None];
let mut topics: [Topic; 4] = [
Default::default(),
Default::default(),
Default::default(),
Default::default(),
];
for (idx, topic) in topics_vec.into_iter().enumerate() {
topics[idx] = topic;
topics[idx] = topic.map(|t| t.into()).unwrap_or_default();
}
let block_option = if let Some(block_hash) = block_hash {
@ -581,47 +640,12 @@ impl From<Vec<H160>> for ValueOrArray<H160> {
}
}
impl From<H256> for Topic {
fn from(src: H256) -> Self {
ValueOrArray::Value(Some(src))
}
}
impl From<Vec<H256>> for ValueOrArray<H256> {
fn from(src: Vec<H256>) -> Self {
ValueOrArray::Array(src)
}
}
impl From<ValueOrArray<H256>> for Topic {
fn from(src: ValueOrArray<H256>) -> Self {
match src {
ValueOrArray::Value(val) => ValueOrArray::Value(Some(val)),
ValueOrArray::Array(arr) => arr.into(),
}
}
}
impl<I: Into<H256>> From<Vec<I>> for Topic {
fn from(src: Vec<I>) -> Self {
ValueOrArray::Array(src.into_iter().map(Into::into).map(Some).collect())
}
}
impl From<Address> for Topic {
fn from(src: Address) -> Self {
let mut bytes = [0; 32];
bytes[12..32].copy_from_slice(src.as_bytes());
ValueOrArray::Value(Some(H256::from(bytes)))
}
}
impl From<U256> for Topic {
fn from(src: U256) -> Self {
ValueOrArray::Value(Some(src.into()))
}
}
impl<T> Serialize for ValueOrArray<T>
where
T: Serialize,
@ -672,8 +696,6 @@ where
pub struct FilteredParams {
/// The original filter, if any
pub filter: Option<Filter>,
/// Flattened topics of the `filter` used to determine if the the filter matches a log.
pub flat_topics: Vec<ValueOrArray<Option<H256>>>,
}
impl FilteredParams {
@ -681,86 +703,43 @@ impl FilteredParams {
/// for matching
pub fn new(filter: Option<Filter>) -> Self {
if let Some(filter) = filter {
let flat_topics = filter.flatten();
FilteredParams { filter: Some(filter), flat_topics }
FilteredParams { filter: Some(filter) }
} else {
Default::default()
}
}
/// Returns the [BloomFilter] for the given address
pub fn address_filter(address: &Option<ValueOrArray<Address>>) -> BloomFilter {
address.as_ref().map(address_to_bloom_filter).unwrap_or_default()
pub fn address_filter(address: &FilterSet<Address>) -> BloomFilter {
address.to_bloom_filter()
}
/// Returns the [BloomFilter] for the given topics
pub fn topics_filter(topics: &Option<Vec<ValueOrArray<Option<H256>>>>) -> Vec<BloomFilter> {
let mut output = Vec::new();
if let Some(topics) = topics {
output.extend(topics.iter().map(topics_to_bloom_filter));
}
output
pub fn topics_filter(topics: &[FilterSet<H256>]) -> Vec<BloomFilter> {
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 {
pub fn matches_topics(bloom: Bloom, topic_filters: &Vec<BloomFilter>) -> bool {
if topic_filters.is_empty() {
return true
}
// returns true if a filter matches
// 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.iter() {
let mut is_match = false;
for maybe_bloom in filter {
is_match = maybe_bloom.as_ref().map(|b| bloom.contains_bloom(b)).unwrap_or(true);
if !is_match {
break
}
}
if is_match {
return true
if !filter.matches(bloom) {
return false
}
}
false
true
}
/// Returns `true` if the bloom contains the address
/// 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 {
if address_filter.is_empty() {
return true
} else {
for maybe_bloom in address_filter {
if maybe_bloom.as_ref().map(|b| bloom.contains_bloom(b)).unwrap_or(true) {
return true
}
}
}
false
}
/// Replace None values - aka wildcards - for the log input value in that position.
pub fn replace(&self, log: &Log, topic: Topic) -> Option<Vec<H256>> {
let mut out: Vec<H256> = Vec::new();
match topic {
ValueOrArray::Value(value) => {
if let Some(value) = value {
out.push(value);
}
}
ValueOrArray::Array(value) => {
for (k, v) in value.into_iter().enumerate() {
if let Some(v) = v {
out.push(v);
} else {
out.push(log.topics[k]);
}
}
}
};
if out.is_empty() {
return None
}
Some(out)
address_filter.matches(bloom)
}
/// Returns true if the filter matches the given block number
@ -805,18 +784,30 @@ impl FilteredParams {
/// Returns `true` if the filter matches the given log.
pub fn filter_address(&self, log: &Log) -> bool {
if let Some(input_address) = &self.filter.as_ref().and_then(|f| f.address.clone()) {
match input_address {
ValueOrArray::Value(x) => {
if log.address != *x {
self.filter.as_ref().map(|f| f.address.matches(&log.address)).unwrap_or(true)
}
/// Returns `true` if the log matches the filter's topics
pub fn filter_topics(&self, log: &Log) -> 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
}
}
ValueOrArray::Array(x) => {
if x.is_empty() {
return true
}
if !x.contains(&log.address) {
// 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
}
}
@ -824,98 +815,6 @@ impl FilteredParams {
}
true
}
/// Returns `true` if the log matches any topic
pub fn filter_topics(&self, log: &Log) -> bool {
let mut out: bool = true;
for topic in self.flat_topics.iter().cloned() {
match topic {
ValueOrArray::Value(single) => {
if let Some(single) = single {
if !log.topics.starts_with(&[single]) {
out = false;
}
}
}
ValueOrArray::Array(multi) => {
if multi.is_empty() {
out = true;
continue
}
// Shrink the topics until the last item is Some.
let mut new_multi = multi;
while new_multi.iter().last().unwrap_or(&Some(H256::default())).is_none() {
new_multi.pop();
}
// We can discard right away any logs with lesser topics than the filter.
if new_multi.len() > log.topics.len() {
out = false;
break
}
let replaced: Option<Vec<H256>> =
self.replace(log, ValueOrArray::Array(new_multi));
if let Some(replaced) = replaced {
out = false;
if log.topics.starts_with(&replaced[..]) {
out = true;
break
}
}
}
}
}
out
}
}
fn topics_to_bloom_filter(topics: &ValueOrArray<Option<H256>>) -> BloomFilter {
let mut blooms = BloomFilter::new();
match topics {
ValueOrArray::Value(topic) => {
if let Some(topic) = topic {
let bloom: Bloom = Input::Raw(topic.as_ref()).into();
blooms.push(Some(bloom));
} else {
blooms.push(None);
}
}
ValueOrArray::Array(topics) => {
if topics.is_empty() {
blooms.push(None);
} else {
for topic in topics.iter() {
if let Some(topic) = topic {
let bloom: Bloom = Input::Raw(topic.as_ref()).into();
blooms.push(Some(bloom));
} else {
blooms.push(None);
}
}
}
}
}
blooms
}
fn address_to_bloom_filter(address: &ValueOrArray<Address>) -> BloomFilter {
let mut blooms = BloomFilter::new();
match address {
ValueOrArray::Value(address) => {
let bloom: Bloom = Input::Raw(address.as_ref()).into();
blooms.push(Some(bloom))
}
ValueOrArray::Array(addresses) => {
if addresses.is_empty() {
blooms.push(None);
} else {
for address in addresses.iter() {
let bloom: Bloom = Input::Raw(address.as_ref()).into();
blooms.push(Some(bloom));
}
}
}
}
blooms
}
/// Response of the `eth_getFilterChanges` RPC.
@ -1007,6 +906,7 @@ impl From<SubscriptionId<'_>> for FilterId {
#[cfg(test)]
mod tests {
use super::*;
use reth_primitives::U256;
use serde_json::json;
fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
@ -1020,40 +920,36 @@ mod tests {
similar_asserts::assert_eq!(
filter.topics,
[
Some(ValueOrArray::Array(vec![Some(
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"
.parse()
.unwrap()
),])),
Some(ValueOrArray::Array(vec![])),
Some(ValueOrArray::Array(vec![Some(
"0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"
.parse()
.unwrap()
)])),
None
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"
.parse::<H256>()
.unwrap()
.into(),
Default::default(),
"0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"
.parse::<H256>()
.unwrap()
.into(),
Default::default(),
]
);
}
let filtered_params = FilteredParams::new(Some(filter));
let topics = filtered_params.flat_topics;
assert_eq!(
topics,
vec![ValueOrArray::Array(vec![
Some(
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"
.parse()
.unwrap()
),
None,
Some(
"0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"
.parse()
.unwrap()
),
None
])]
)
#[test]
fn test_filter_topics_middle_wildcard() {
let s = r#"{"fromBlock": "0xfc359e", "toBlock": "0xfc359e", "topics": [["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"], [], [null, "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"]]}"#;
let filter = serde_json::from_str::<Filter>(s).unwrap();
similar_asserts::assert_eq!(
filter.topics,
[
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"
.parse::<H256>()
.unwrap()
.into(),
Default::default(),
Default::default(),
Default::default(),
]
);
}
#[test]
@ -1076,11 +972,13 @@ mod tests {
#[test]
fn filter_serialization_test() {
let t1 = "9729a6fbefefc8f6005933898b13dc45c3a2c8b7".parse::<Address>().unwrap();
let t1 = "0000000000000000000000009729a6fbefefc8f6005933898b13dc45c3a2c8b7"
.parse::<H256>()
.unwrap();
let t2 = H256::from([0; 32]);
let t3 = U256::from(123);
let t1_padded = H256::from(t1);
let t1_padded = t1;
let t3_padded = H256::from({
let mut x = [0; 32];
x[31] = 123;
@ -1143,24 +1041,17 @@ mod tests {
block_bloom
}
fn topic_filter(
topic1: H256,
topic2: H256,
topic3: H256,
) -> (Filter, Option<Vec<ValueOrArray<Option<H256>>>>) {
let filter = Filter {
fn topic_filter(topic1: H256, topic2: H256, topic3: H256) -> Filter {
Filter {
block_option: Default::default(),
address: None,
address: Default::default(),
topics: [
Some(ValueOrArray::Value(Some(topic1))),
Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])),
None,
None,
topic1.into(),
vec![topic2, topic3].into(),
Default::default(),
Default::default(),
],
};
let filtered_params = FilteredParams::new(Some(filter.clone()));
(filter, Some(filtered_params.flat_topics))
}
}
#[test]
@ -1169,7 +1060,7 @@ mod tests {
let topic2 = H256::random();
let topic3 = H256::random();
let (_, topics) = topic_filter(topic1, topic2, topic3);
let topics = topic_filter(topic1, topic2, topic3).topics;
let topics_bloom = FilteredParams::topics_filter(&topics);
assert!(!FilteredParams::matches_topics(
build_bloom(Address::random(), H256::random(), H256::random()),
@ -1183,7 +1074,7 @@ mod tests {
let topic2 = H256::random();
let topic3 = H256::random();
let (_, topics) = topic_filter(topic1, topic2, topic3);
let topics = topic_filter(topic1, topic2, topic3).topics;
let _topics_bloom = FilteredParams::topics_filter(&topics);
let topics_bloom = FilteredParams::topics_filter(&topics);
@ -1195,11 +1086,12 @@ mod tests {
#[test]
fn can_match_empty_topics() {
let filter =
Filter { block_option: Default::default(), address: None, topics: Default::default() };
let filtered_params = FilteredParams::new(Some(filter));
let topics = Some(filtered_params.flat_topics);
let filter = Filter {
block_option: Default::default(),
address: Default::default(),
topics: Default::default(),
};
let topics = filter.topics;
let topics_bloom = FilteredParams::topics_filter(&topics);
assert!(FilteredParams::matches_topics(
@ -1217,16 +1109,16 @@ mod tests {
let filter = Filter {
block_option: Default::default(),
address: Some(ValueOrArray::Value(rng_address)),
address: rng_address.into(),
topics: [
Some(ValueOrArray::Value(Some(topic1))),
Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])),
None,
None,
topic1.into(),
vec![topic2, topic3].into(),
Default::default(),
Default::default(),
],
};
let filtered_params = FilteredParams::new(Some(filter.clone()));
let topics = Some(filtered_params.flat_topics);
let topics = filter.topics;
let address_filter = FilteredParams::address_filter(&filter.address);
let topics_filter = FilteredParams::topics_filter(&topics);
assert!(
@ -1248,11 +1140,16 @@ mod tests {
let filter = Filter {
block_option: Default::default(),
address: None,
topics: [None, Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])), None, None],
address: Default::default(),
topics: [
Default::default(),
vec![topic2, topic3].into(),
Default::default(),
Default::default(),
],
};
let filtered_params = FilteredParams::new(Some(filter));
let topics = Some(filtered_params.flat_topics);
let topics = filter.topics;
let topics_bloom = FilteredParams::topics_filter(&topics);
assert!(FilteredParams::matches_topics(
build_bloom(Address::random(), topic1, topic2),
@ -1264,16 +1161,16 @@ mod tests {
fn can_match_topics_wildcard_mismatch() {
let filter = Filter {
block_option: Default::default(),
address: None,
address: Default::default(),
topics: [
None,
Some(ValueOrArray::Array(vec![Some(H256::random()), Some(H256::random())])),
None,
None,
Default::default(),
vec![H256::random(), H256::random()].into(),
Default::default(),
Default::default(),
],
};
let filtered_params = FilteredParams::new(Some(filter));
let topics_input = Some(filtered_params.flat_topics);
let topics_input = filter.topics;
let topics_bloom = FilteredParams::topics_filter(&topics_input);
assert!(!FilteredParams::matches_topics(
build_bloom(Address::random(), H256::random(), H256::random()),
@ -1286,7 +1183,7 @@ mod tests {
let rng_address = Address::random();
let filter = Filter {
block_option: Default::default(),
address: Some(ValueOrArray::Value(rng_address)),
address: rng_address.into(),
topics: Default::default(),
};
let address_bloom = FilteredParams::address_filter(&filter.address);
@ -1302,7 +1199,7 @@ mod tests {
let rng_address = Address::random();
let filter = Filter {
block_option: Default::default(),
address: Some(ValueOrArray::Value(rng_address)),
address: rng_address.into(),
topics: Default::default(),
};
let address_bloom = FilteredParams::address_filter(&filter.address);
@ -1335,26 +1232,24 @@ mod tests {
from_block: Some(4365627u64.into()),
to_block: Some(4365627u64.into()),
},
address: Some(ValueOrArray::Value(
"0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907".parse().unwrap()
)),
address: "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907"
.parse::<Address>()
.unwrap()
.into(),
topics: [
Some(ValueOrArray::Value(Some(
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
.parse()
.unwrap(),
))),
Some(ValueOrArray::Value(Some(
"0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75"
.parse()
.unwrap(),
))),
Some(ValueOrArray::Value(Some(
"0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078"
.parse()
.unwrap(),
))),
None,
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
.parse::<H256>()
.unwrap()
.into(),
"0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75"
.parse::<H256>()
.unwrap()
.into(),
"0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078"
.parse::<H256>()
.unwrap()
.into(),
Default::default(),
],
}
);
@ -1379,8 +1274,8 @@ mod tests {
from_block: Some(4365627u64.into()),
to_block: Some(4365627u64.into()),
},
address: None,
topics: [None, None, None, None,],
address: Default::default(),
topics: Default::default(),
}
);
}

View File

@ -97,7 +97,7 @@ pub enum SubscriptionKind {
}
/// Any additional parameters for a subscription.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum Params {
/// No parameters passed.
#[default]

View File

@ -363,11 +363,9 @@ where
let mut all_logs = Vec::new();
let filter_params = FilteredParams::new(Some(filter.clone()));
let topics = filter.has_topics().then(|| filter_params.flat_topics.clone());
// derive bloom filters from filter input
let address_filter = FilteredParams::address_filter(&filter.address);
let topics_filter = FilteredParams::topics_filter(&topics);
let topics_filter = FilteredParams::topics_filter(&filter.topics);
let is_multi_block_range = from_block != to_block;

View File

@ -186,8 +186,8 @@ fn receipts_provider_example<T: ReceiptProvider + TransactionsProvider + HeaderP
// topics. This API is a bit clunky and not obvious to use at the moemnt.
let filter = Filter::new().address(addr).topic0(topic);
let filter_params = FilteredParams::new(Some(filter));
let address_filter = FilteredParams::address_filter(&Some(addr.into()));
let topics_filter = FilteredParams::topics_filter(&Some(vec![topic.into()]));
let address_filter = FilteredParams::address_filter(&addr.into());
let topics_filter = FilteredParams::topics_filter(&[topic.into()]);
// 3. If the address & topics filters match do something. We use the outer check against the
// bloom filter stored in the header to avoid having to query the receipts table when there