use super::*;
use crate::bloom::{BloomFilter, DEFAULT_FILTER_SIZE_BITS, DEFAULT_HASH_COUNT};
use crate::peer::ActivePeer;
use crate::protocol::FilterAnnounce;
fn inject_peer(node: &mut Node) -> NodeAddr {
let peer_identity = make_peer_identity();
let peer_addr = *peer_identity.node_addr();
let peer = ActivePeer::new(peer_identity, LinkId::new(1), 0);
node.peers.insert(peer_addr, peer);
peer_addr
}
fn encode_payload(announce: &FilterAnnounce) -> Vec<u8> {
let mut full = announce.encode().unwrap();
full.remove(0); full
}
#[tokio::test]
async fn test_m1_rejects_all_ones_filter_announce() {
let mut node = make_node();
let peer_addr = inject_peer(&mut node);
let all_ones = BloomFilter::from_bytes(
vec![0xFFu8; DEFAULT_FILTER_SIZE_BITS / 8],
DEFAULT_HASH_COUNT,
)
.unwrap();
let announce = FilterAnnounce::new(all_ones, 1);
let payload = encode_payload(&announce);
let before_fill_exceeded = node.stats().bloom.fill_exceeded;
let before_accepted = node.stats().bloom.accepted;
node.handle_filter_announce(&peer_addr, &payload).await;
let after = &node.stats().bloom;
assert_eq!(
after.fill_exceeded,
before_fill_exceeded + 1,
"fill_exceeded counter must increment on all-ones rejection"
);
assert_eq!(
after.accepted, before_accepted,
"accepted counter must NOT increment on rejection"
);
let peer = node.get_peer(&peer_addr).expect("peer still present");
assert!(
peer.inbound_filter().is_none(),
"peer must NOT have a stored filter after rejection"
);
assert_eq!(
peer.filter_sequence(),
0,
"peer filter_sequence must NOT advance on rejection"
);
}
#[tokio::test]
async fn test_m1_accepts_sub_cap_filter() {
let mut node = make_node();
let peer_addr = inject_peer(&mut node);
let mut filter = BloomFilter::new();
for i in 0..50u8 {
let mut bytes = [0u8; 16];
bytes[0] = i;
filter.insert(&NodeAddr::from_bytes(bytes));
}
let announce = FilterAnnounce::new(filter, 1);
let payload = encode_payload(&announce);
let before_fill_exceeded = node.stats().bloom.fill_exceeded;
let before_accepted = node.stats().bloom.accepted;
node.handle_filter_announce(&peer_addr, &payload).await;
let after = &node.stats().bloom;
assert_eq!(
after.fill_exceeded, before_fill_exceeded,
"fill_exceeded must NOT increment on legitimate sub-cap filter"
);
assert_eq!(
after.accepted,
before_accepted + 1,
"accepted must increment on legitimate filter"
);
let peer = node.get_peer(&peer_addr).expect("peer still present");
assert!(
peer.inbound_filter().is_some(),
"peer must have a stored filter after acceptance"
);
assert_eq!(
peer.filter_sequence(),
1,
"peer filter_sequence must advance to announce's sequence"
);
}
#[tokio::test]
async fn test_m1_sequence_not_advanced_allows_recovery() {
let mut node = make_node();
let peer_addr = inject_peer(&mut node);
let bad = BloomFilter::from_bytes(
vec![0xFFu8; DEFAULT_FILTER_SIZE_BITS / 8],
DEFAULT_HASH_COUNT,
)
.unwrap();
let bad_announce = FilterAnnounce::new(bad, 1);
node.handle_filter_announce(&peer_addr, &encode_payload(&bad_announce))
.await;
assert_eq!(
node.get_peer(&peer_addr).unwrap().filter_sequence(),
0,
"rejected announce must not advance sequence"
);
let mut good = BloomFilter::new();
for i in 0..10u8 {
let mut bytes = [0u8; 16];
bytes[0] = i;
good.insert(&NodeAddr::from_bytes(bytes));
}
let good_announce = FilterAnnounce::new(good, 1);
node.handle_filter_announce(&peer_addr, &encode_payload(&good_announce))
.await;
let peer = node.get_peer(&peer_addr).unwrap();
assert!(
peer.inbound_filter().is_some(),
"compliant announce at same seq must be accepted after rejection"
);
assert_eq!(peer.filter_sequence(), 1);
assert_eq!(node.stats().bloom.fill_exceeded, 1);
assert_eq!(node.stats().bloom.accepted, 1);
}