use crate::{
abi::ethereum_types::BloomInput,
types::{Address, BlockNumber, Bloom, Log, H160, H256, U256, U64},
utils::keccak256,
};
use serde::{
de::{DeserializeOwned, MapAccess, Visitor},
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::ops::{Range, RangeFrom, RangeTo};
pub type BloomFilter = Vec<Option<Bloom>>;
pub type Topic = ValueOrArray<Option<H256>>;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum FilterBlockOption {
Range { from_block: Option<BlockNumber>, to_block: Option<BlockNumber> },
AtBlockHash(H256),
}
impl FilterBlockOption {
pub fn get_to_block(&self) -> Option<&BlockNumber> {
match self {
FilterBlockOption::Range { to_block, .. } => to_block.as_ref(),
FilterBlockOption::AtBlockHash(_) => None,
}
}
pub fn get_from_block(&self) -> Option<&BlockNumber> {
match self {
FilterBlockOption::Range { from_block, .. } => from_block.as_ref(),
FilterBlockOption::AtBlockHash(_) => None,
}
}
}
impl From<BlockNumber> for FilterBlockOption {
fn from(block: BlockNumber) -> Self {
let block = Some(block);
FilterBlockOption::Range { from_block: block, to_block: block }
}
}
impl From<U64> for FilterBlockOption {
fn from(block: U64) -> Self {
BlockNumber::from(block).into()
}
}
impl From<u64> for FilterBlockOption {
fn from(block: u64) -> Self {
BlockNumber::from(block).into()
}
}
impl<T: Into<BlockNumber>> From<Range<T>> for FilterBlockOption {
fn from(r: Range<T>) -> Self {
let from_block = Some(r.start.into());
let to_block = Some(r.end.into());
FilterBlockOption::Range { from_block, to_block }
}
}
impl<T: Into<BlockNumber>> From<RangeTo<T>> for FilterBlockOption {
fn from(r: RangeTo<T>) -> Self {
let to_block = Some(r.end.into());
FilterBlockOption::Range { from_block: Some(BlockNumber::Earliest), to_block }
}
}
impl<T: Into<BlockNumber>> From<RangeFrom<T>> for FilterBlockOption {
fn from(r: RangeFrom<T>) -> Self {
let from_block = Some(r.start.into());
FilterBlockOption::Range { from_block, to_block: Some(BlockNumber::Latest) }
}
}
impl From<H256> for FilterBlockOption {
fn from(hash: H256) -> Self {
FilterBlockOption::AtBlockHash(hash)
}
}
impl Default for FilterBlockOption {
fn default() -> Self {
FilterBlockOption::Range { from_block: None, to_block: None }
}
}
impl FilterBlockOption {
#[must_use]
pub fn set_from_block(&self, block: BlockNumber) -> Self {
let to_block =
if let FilterBlockOption::Range { to_block, .. } = self { *to_block } else { None };
FilterBlockOption::Range { from_block: Some(block), to_block }
}
#[must_use]
pub fn set_to_block(&self, block: BlockNumber) -> Self {
let from_block =
if let FilterBlockOption::Range { from_block, .. } = self { *from_block } else { None };
FilterBlockOption::Range { from_block, to_block: Some(block) }
}
#[must_use]
pub fn set_hash(&self, hash: H256) -> Self {
FilterBlockOption::AtBlockHash(hash)
}
}
#[derive(Default, Debug, PartialEq, Eq, Clone, Hash)]
pub struct Filter {
pub block_option: FilterBlockOption,
pub address: Option<ValueOrArray<Address>>,
pub topics: [Option<Topic>; 4],
}
impl Filter {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn select(mut self, filter: impl Into<FilterBlockOption>) -> Self {
self.block_option = filter.into();
self
}
#[allow(clippy::wrong_self_convention)]
#[must_use]
pub fn from_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
self.block_option = self.block_option.set_from_block(block.into());
self
}
#[allow(clippy::wrong_self_convention)]
#[must_use]
pub fn to_block<T: Into<BlockNumber>>(mut self, block: T) -> Self {
self.block_option = self.block_option.set_to_block(block.into());
self
}
#[allow(clippy::wrong_self_convention)]
#[must_use]
pub fn at_block_hash<T: Into<H256>>(mut self, hash: T) -> Self {
self.block_option = self.block_option.set_hash(hash.into());
self
}
#[must_use]
pub fn address<T: Into<ValueOrArray<Address>>>(mut self, address: T) -> Self {
self.address = Some(address.into());
self
}
#[must_use]
pub fn event(self, event_name: &str) -> Self {
let hash = H256::from(keccak256(event_name.as_bytes()));
self.topic0(hash)
}
#[must_use]
pub fn events(self, events: impl IntoIterator<Item = impl AsRef<[u8]>>) -> Self {
let events =
events.into_iter().map(|e| H256::from(keccak256(e.as_ref()))).collect::<Vec<_>>();
self.topic0(events)
}
#[must_use]
pub fn topic0<T: Into<Topic>>(mut self, topic: T) -> Self {
self.topics[0] = Some(topic.into());
self
}
#[must_use]
pub fn topic1<T: Into<Topic>>(mut self, topic: T) -> Self {
self.topics[1] = Some(topic.into());
self
}
#[must_use]
pub fn topic2<T: Into<Topic>>(mut self, topic: T) -> Self {
self.topics[2] = Some(topic.into());
self
}
#[must_use]
pub fn topic3<T: Into<Topic>>(mut self, topic: T) -> Self {
self.topics[3] = Some(topic.into());
self
}
pub fn is_paginatable(&self) -> bool {
self.get_from_block().is_some()
}
pub fn get_to_block(&self) -> Option<U64> {
self.block_option.get_to_block().and_then(|b| b.as_number())
}
pub fn get_from_block(&self) -> Option<U64> {
self.block_option.get_from_block().and_then(|b| b.as_number())
}
pub fn get_block_hash(&self) -> Option<H256> {
match self.block_option {
FilterBlockOption::AtBlockHash(hash) => Some(hash),
FilterBlockOption::Range { .. } => None,
}
}
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) => s.clone(),
}
} else {
vec![None]
};
tmp.push(v);
}
for v in cartesian(&tmp) {
out.push(ValueOrArray::Array(v));
}
out
}
pub fn topics(&self) -> impl Iterator<Item = &Topic> + '_ {
self.topics.iter().flatten()
}
pub fn has_topics(&self) -> bool {
self.topics.iter().any(|t| t.is_some())
}
}
impl Serialize for Filter {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_struct("Filter", 5)?;
match self.block_option {
FilterBlockOption::Range { from_block, to_block } => {
if let Some(ref from_block) = from_block {
s.serialize_field("fromBlock", from_block)?;
}
if let Some(ref to_block) = to_block {
s.serialize_field("toBlock", to_block)?;
}
}
FilterBlockOption::AtBlockHash(ref h) => s.serialize_field("blockHash", h)?,
}
if let Some(ref address) = self.address {
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 {
if self.topics[i + 1..].iter().any(|x| x.is_some()) {
filtered_topics.push(&None);
}
}
}
s.serialize_field("topics", &filtered_topics)?;
s.end()
}
}
impl<'de> Deserialize<'de> for Filter {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct FilterVisitor;
impl<'de> Visitor<'de> for FilterVisitor {
type Value = Filter;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Filter object")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut from_block: Option<Option<BlockNumber>> = None;
let mut to_block: Option<Option<BlockNumber>> = 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;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"fromBlock" => {
if from_block.is_some() {
return Err(serde::de::Error::duplicate_field("fromBlock"))
}
if block_hash.is_some() {
return Err(serde::de::Error::custom(
"fromBlock not allowed with blockHash",
))
}
from_block = Some(map.next_value()?)
}
"toBlock" => {
if to_block.is_some() {
return Err(serde::de::Error::duplicate_field("toBlock"))
}
if block_hash.is_some() {
return Err(serde::de::Error::custom(
"toBlock not allowed with blockHash",
))
}
to_block = Some(map.next_value()?)
}
"blockHash" => {
if block_hash.is_some() {
return Err(serde::de::Error::duplicate_field("blockHash"))
}
if from_block.is_some() || to_block.is_some() {
return Err(serde::de::Error::custom(
"fromBlock,toBlock not allowed with blockHash",
))
}
block_hash = Some(map.next_value()?)
}
"address" => {
if address.is_some() {
return Err(serde::de::Error::duplicate_field("address"))
}
address = Some(map.next_value()?)
}
"topics" => {
if topics.is_some() {
return Err(serde::de::Error::duplicate_field("topics"))
}
topics = Some(map.next_value()?)
}
key => {
return Err(serde::de::Error::unknown_field(
key,
&["fromBlock", "toBlock", "address", "topics", "blockHash"],
))
}
}
}
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 topics_vec = topics.flatten().unwrap_or_default();
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];
for (idx, topic) in topics_vec.into_iter().enumerate() {
topics[idx] = topic;
}
let block_option = if let Some(block_hash) = block_hash {
FilterBlockOption::AtBlockHash(block_hash)
} else {
FilterBlockOption::Range { from_block, to_block }
};
Ok(Filter { block_option, address, topics })
}
}
deserializer.deserialize_any(FilterVisitor)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ValueOrArray<T> {
Value(T),
Array(Vec<T>),
}
impl From<H160> for ValueOrArray<H160> {
fn from(src: H160) -> Self {
ValueOrArray::Value(src)
}
}
impl From<Vec<H160>> for ValueOrArray<H160> {
fn from(src: Vec<H160>) -> Self {
ValueOrArray::Array(src)
}
}
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 {
let mut bytes = [0; 32];
src.to_big_endian(&mut bytes);
ValueOrArray::Value(Some(H256::from(bytes)))
}
}
impl<T> Serialize for ValueOrArray<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
ValueOrArray::Value(inner) => inner.serialize(serializer),
ValueOrArray::Array(inner) => inner.serialize(serializer),
}
}
}
impl<'a, T> Deserialize<'a> for ValueOrArray<T>
where
T: DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<ValueOrArray<T>, D::Error>
where
D: Deserializer<'a>,
{
let value = serde_json::Value::deserialize(deserializer)?;
if value.is_null() {
return Ok(ValueOrArray::Array(Vec::new()))
}
#[derive(Deserialize)]
#[serde(untagged)]
enum Variadic<T> {
Value(T),
Array(Vec<T>),
}
match serde_json::from_value::<Variadic<T>>(value).map_err(|err| {
serde::de::Error::custom(format!("Invalid variadic value or array type: {err}"))
})? {
Variadic::Value(val) => Ok(ValueOrArray::Value(val)),
Variadic::Array(arr) => Ok(ValueOrArray::Array(arr)),
}
}
}
#[derive(Debug, Default)]
pub struct FilteredParams {
pub filter: Option<Filter>,
pub flat_topics: Vec<ValueOrArray<Option<H256>>>,
}
impl FilteredParams {
pub fn new(filter: Option<Filter>) -> Self {
if let Some(filter) = filter {
let flat_topics = filter.flatten();
FilteredParams { filter: Some(filter), flat_topics }
} else {
Default::default()
}
}
pub fn address_filter(address: &Option<ValueOrArray<Address>>) -> BloomFilter {
address.as_ref().map(address_to_bloom_filter).unwrap_or_default()
}
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 matches_topics(bloom: Bloom, topic_filters: &[BloomFilter]) -> bool {
if topic_filters.is_empty() {
return true
}
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
}
}
false
}
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
}
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)
}
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(BlockNumber::Number(num)) = filter.block_option.get_from_block() {
if num.as_u64() > block_number {
res = false;
}
}
if let Some(to) = filter.block_option.get_to_block() {
match to {
BlockNumber::Number(num) => {
if num.as_u64() < block_number {
res = false;
}
}
BlockNumber::Earliest => {
res = false;
}
_ => {}
}
}
res
}
pub fn filter_block_hash(&self, block_hash: H256) -> bool {
if let Some(h) = self.filter.as_ref().and_then(|f| f.get_block_hash()) {
if h != block_hash {
return false
}
}
true
}
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 {
return false
}
}
ValueOrArray::Array(x) => {
if x.is_empty() {
return true
}
if !x.contains(&log.address) {
return false
}
}
}
}
true
}
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
}
let mut new_multi = multi;
while new_multi.iter().last().unwrap_or(&Some(H256::default())).is_none() {
new_multi.pop();
}
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 = BloomInput::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 = BloomInput::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 = BloomInput::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 = BloomInput::Raw(address.as_ref()).into();
blooms.push(Some(bloom));
}
}
}
}
blooms
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::serialize;
use serde_json::json;
#[test]
fn can_serde_value_or_array() {
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
struct Item {
value: ValueOrArray<U256>,
}
let item = Item { value: ValueOrArray::Value(U256::one()) };
let json = serde_json::to_value(item.clone()).unwrap();
let deserialized: Item = serde_json::from_value(json).unwrap();
assert_eq!(item, deserialized);
let item = Item { value: ValueOrArray::Array(vec![U256::one(), U256::zero()]) };
let json = serde_json::to_value(item.clone()).unwrap();
let deserialized: Item = serde_json::from_value(json).unwrap();
assert_eq!(item, deserialized);
}
#[test]
fn filter_serialization_test() {
let t1 = "9729a6fbefefc8f6005933898b13dc45c3a2c8b7".parse::<Address>().unwrap();
let t2 = H256::from([0; 32]);
let t3 = U256::from(123);
let t1_padded = H256::from(t1);
let t3_padded = H256::from({
let mut x = [0; 32];
x[31] = 123;
x
});
let event = "ValueChanged(address,string,string)";
let t0 = H256::from(keccak256(event.as_bytes()));
let addr: Address = "f817796F60D268A36a57b8D2dF1B97B14C0D0E1d".parse().unwrap();
let filter = Filter::new();
let ser = serialize(&filter);
assert_eq!(ser, json!({ "topics": [] }));
let filter = filter.address(ValueOrArray::Value(addr));
let ser = serialize(&filter);
assert_eq!(ser, json!({"address" : addr, "topics": []}));
let filter = filter.event(event);
let ser = serialize(&filter);
assert_eq!(ser, json!({ "address" : addr, "topics": [t0]}));
let ser = serialize(&filter.clone().topic1(t1));
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded]}));
let ser = serialize(&filter.clone().topic2(t2));
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, t2]}));
let ser = serialize(&filter.clone().topic3(t3));
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, null, t3_padded]}));
let ser = serialize(&filter.clone().topic1(t1).topic2(t2));
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2]}));
let ser = serialize(&filter.clone().topic1(t1).topic3(t3));
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, null, t3_padded]}));
let ser = serialize(&filter.clone().topic2(t2).topic3(t3));
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, t2, t3_padded]}));
let ser = serialize(&filter.topic1(t1).topic2(t2).topic3(t3));
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2, t3_padded]}));
}
fn build_bloom(address: Address, topic1: H256, topic2: H256) -> 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: H256,
topic2: H256,
topic3: H256,
) -> (Filter, Option<Vec<ValueOrArray<Option<H256>>>>) {
let filter = Filter {
block_option: Default::default(),
address: None,
topics: [
Some(ValueOrArray::Value(Some(topic1))),
Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])),
None,
None,
],
};
let filtered_params = FilteredParams::new(Some(filter.clone()));
(filter, Some(filtered_params.flat_topics))
}
#[test]
fn can_detect_different_topics() {
let topic1 = H256::random();
let topic2 = H256::random();
let topic3 = H256::random();
let (_, topics) = topic_filter(topic1, topic2, topic3);
let topics_bloom = FilteredParams::topics_filter(&topics);
assert!(!FilteredParams::matches_topics(
build_bloom(Address::random(), H256::random(), H256::random()),
&topics_bloom
));
}
#[test]
fn can_match_topic() {
let topic1 = H256::random();
let topic2 = H256::random();
let topic3 = H256::random();
let (_, topics) = topic_filter(topic1, topic2, topic3);
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
));
}
#[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 topics_bloom = FilteredParams::topics_filter(&topics);
assert!(FilteredParams::matches_topics(
build_bloom(Address::random(), H256::random(), H256::random()),
&topics_bloom
));
}
#[test]
fn can_match_address_and_topics() {
let rng_address = Address::random();
let topic1 = H256::random();
let topic2 = H256::random();
let topic3 = H256::random();
let filter = Filter {
block_option: Default::default(),
address: Some(ValueOrArray::Value(rng_address)),
topics: [
Some(ValueOrArray::Value(Some(topic1))),
Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])),
None,
None,
],
};
let filtered_params = FilteredParams::new(Some(filter.clone()));
let topics = Some(filtered_params.flat_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
)
);
}
#[test]
fn can_match_topics_wildcard() {
let topic1 = H256::random();
let topic2 = H256::random();
let topic3 = H256::random();
let filter = Filter {
block_option: Default::default(),
address: None,
topics: [None, Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])), None, None],
};
let filtered_params = FilteredParams::new(Some(filter));
let topics = Some(filtered_params.flat_topics);
let topics_bloom = FilteredParams::topics_filter(&topics);
assert!(FilteredParams::matches_topics(
build_bloom(Address::random(), topic1, topic2),
&topics_bloom
));
}
#[test]
fn can_match_topics_wildcard_mismatch() {
let filter = Filter {
block_option: Default::default(),
address: None,
topics: [
None,
Some(ValueOrArray::Array(vec![Some(H256::random()), Some(H256::random())])),
None,
None,
],
};
let filtered_params = FilteredParams::new(Some(filter));
let topics_input = Some(filtered_params.flat_topics);
let topics_bloom = FilteredParams::topics_filter(&topics_input);
assert!(!FilteredParams::matches_topics(
build_bloom(Address::random(), H256::random(), H256::random()),
&topics_bloom
));
}
#[test]
fn can_match_address_filter() {
let rng_address = Address::random();
let filter = Filter {
block_option: Default::default(),
address: Some(ValueOrArray::Value(rng_address)),
topics: Default::default(),
};
let address_bloom = FilteredParams::address_filter(&filter.address);
assert!(FilteredParams::matches_address(
build_bloom(rng_address, H256::random(), H256::random(),),
&address_bloom
));
}
#[test]
fn can_detect_different_address() {
let bloom_address = Address::random();
let rng_address = Address::random();
let filter = Filter {
block_option: Default::default(),
address: Some(ValueOrArray::Value(rng_address)),
topics: Default::default(),
};
let address_bloom = FilteredParams::address_filter(&filter.address);
assert!(!FilteredParams::matches_address(
build_bloom(bloom_address, H256::random(), H256::random(),),
&address_bloom
));
}
#[test]
fn can_convert_to_ethers_filter() {
let json = json!(
{
"fromBlock": "0x429d3b",
"toBlock": "0x429d3b",
"address": "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75",
"0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078"
]
}
);
let filter: Filter = serde_json::from_value(json).unwrap();
assert_eq!(
filter,
Filter {
block_option: FilterBlockOption::Range {
from_block: Some(4365627u64.into()),
to_block: Some(4365627u64.into()),
},
address: Some(ValueOrArray::Value(
"0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907".parse().unwrap()
)),
topics: [
Some(ValueOrArray::Value(Some(
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
.parse()
.unwrap(),
))),
Some(ValueOrArray::Value(Some(
"0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75"
.parse()
.unwrap(),
))),
Some(ValueOrArray::Value(Some(
"0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078"
.parse()
.unwrap(),
))),
None,
],
}
);
}
#[test]
fn can_convert_to_ethers_filter_with_null_fields() {
let json = json!(
{
"fromBlock": "0x429d3b",
"toBlock": "0x429d3b",
"address": null,
"topics": null
}
);
let filter: Filter = serde_json::from_value(json).unwrap();
assert_eq!(
filter,
Filter {
block_option: FilterBlockOption::Range {
from_block: Some(4365627u64.into()),
to_block: Some(4365627u64.into()),
},
address: None,
topics: [None, None, None, None,],
}
);
}
}