rate_limits.rs (22343B)
1 #![forbid(unsafe_code)] 2 3 use crate::errors::BaseRelayError; 4 use std::{ 5 collections::BTreeMap, 6 net::IpAddr, 7 sync::{Arc, Mutex}, 8 }; 9 use tangle_groups::GroupId; 10 use tangle_protocol::{Kind, PublicKeyHex, UnixTimestamp}; 11 12 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 13 pub enum TangleRateLimitScope { 14 Auth, 15 Event, 16 GroupWrite, 17 Req, 18 Count, 19 } 20 21 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 22 pub enum TangleRateLimitKey { 23 Ip { 24 scope: TangleRateLimitScope, 25 ip: IpAddr, 26 }, 27 Pubkey { 28 scope: TangleRateLimitScope, 29 pubkey: PublicKeyHex, 30 }, 31 Group { 32 scope: TangleRateLimitScope, 33 group_id: GroupId, 34 }, 35 Kind { 36 scope: TangleRateLimitScope, 37 kind: Kind, 38 }, 39 AuthFailure { 40 ip: Option<IpAddr>, 41 pubkey: Option<PublicKeyHex>, 42 }, 43 JoinFlowIp { 44 group_id: GroupId, 45 ip: IpAddr, 46 }, 47 JoinFlow { 48 group_id: GroupId, 49 pubkey: PublicKeyHex, 50 }, 51 Connection { 52 scope: TangleRateLimitScope, 53 connection_id: u64, 54 }, 55 QueryClass { 56 scope: TangleRateLimitScope, 57 class: TangleRateLimitQueryClass, 58 }, 59 } 60 61 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 62 pub enum TangleRateLimitQueryClass { 63 Broad, 64 } 65 66 impl TangleRateLimitKey { 67 pub fn ip(scope: TangleRateLimitScope, ip: IpAddr) -> Self { 68 Self::Ip { scope, ip } 69 } 70 71 pub fn pubkey(scope: TangleRateLimitScope, pubkey: PublicKeyHex) -> Self { 72 Self::Pubkey { scope, pubkey } 73 } 74 75 pub fn group(scope: TangleRateLimitScope, group_id: GroupId) -> Self { 76 Self::Group { scope, group_id } 77 } 78 79 pub fn kind(scope: TangleRateLimitScope, kind: Kind) -> Self { 80 Self::Kind { scope, kind } 81 } 82 83 pub fn auth_failure(ip: Option<IpAddr>, pubkey: Option<PublicKeyHex>) -> Self { 84 Self::AuthFailure { ip, pubkey } 85 } 86 87 pub fn join_flow(group_id: GroupId, pubkey: PublicKeyHex) -> Self { 88 Self::JoinFlow { group_id, pubkey } 89 } 90 91 pub fn join_flow_ip(group_id: GroupId, ip: IpAddr) -> Self { 92 Self::JoinFlowIp { group_id, ip } 93 } 94 95 pub fn connection(scope: TangleRateLimitScope, connection_id: u64) -> Self { 96 Self::Connection { 97 scope, 98 connection_id, 99 } 100 } 101 102 pub fn query_class(scope: TangleRateLimitScope, class: TangleRateLimitQueryClass) -> Self { 103 Self::QueryClass { scope, class } 104 } 105 } 106 107 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 108 pub struct TangleRateLimitConfig { 109 auth: TangleAuthRateLimitConfig, 110 event: TangleEventRateLimitConfig, 111 group: TangleGroupRateLimitConfig, 112 req: TangleQueryRateLimitConfig, 113 count: TangleQueryRateLimitConfig, 114 } 115 116 impl TangleRateLimitConfig { 117 pub fn new( 118 auth: TangleAuthRateLimitConfig, 119 event: TangleEventRateLimitConfig, 120 group: TangleGroupRateLimitConfig, 121 req: TangleQueryRateLimitConfig, 122 count: TangleQueryRateLimitConfig, 123 ) -> Self { 124 Self { 125 auth, 126 event, 127 group, 128 req, 129 count, 130 } 131 } 132 133 pub fn auth(self) -> TangleAuthRateLimitConfig { 134 self.auth 135 } 136 137 pub fn event(self) -> TangleEventRateLimitConfig { 138 self.event 139 } 140 141 pub fn group(self) -> TangleGroupRateLimitConfig { 142 self.group 143 } 144 145 pub fn req(self) -> TangleQueryRateLimitConfig { 146 self.req 147 } 148 149 pub fn count(self) -> TangleQueryRateLimitConfig { 150 self.count 151 } 152 } 153 154 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 155 pub struct TangleAuthRateLimitConfig { 156 per_ip: TangleRateLimitRule, 157 per_pubkey: TangleRateLimitRule, 158 failures: TangleRateLimitRule, 159 failures_per_ip: TangleRateLimitRule, 160 } 161 162 impl TangleAuthRateLimitConfig { 163 pub fn new( 164 per_ip: TangleRateLimitRule, 165 per_pubkey: TangleRateLimitRule, 166 failures: TangleRateLimitRule, 167 failures_per_ip: TangleRateLimitRule, 168 ) -> Self { 169 Self { 170 per_ip, 171 per_pubkey, 172 failures, 173 failures_per_ip, 174 } 175 } 176 177 pub fn per_ip(self) -> TangleRateLimitRule { 178 self.per_ip 179 } 180 181 pub fn per_pubkey(self) -> TangleRateLimitRule { 182 self.per_pubkey 183 } 184 185 pub fn failures(self) -> TangleRateLimitRule { 186 self.failures 187 } 188 189 pub fn failures_per_ip(self) -> TangleRateLimitRule { 190 self.failures_per_ip 191 } 192 } 193 194 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 195 pub struct TangleEventRateLimitConfig { 196 per_ip: TangleRateLimitRule, 197 per_pubkey: TangleRateLimitRule, 198 per_kind: TangleRateLimitRule, 199 } 200 201 impl TangleEventRateLimitConfig { 202 pub fn new( 203 per_ip: TangleRateLimitRule, 204 per_pubkey: TangleRateLimitRule, 205 per_kind: TangleRateLimitRule, 206 ) -> Self { 207 Self { 208 per_ip, 209 per_pubkey, 210 per_kind, 211 } 212 } 213 214 pub fn per_ip(self) -> TangleRateLimitRule { 215 self.per_ip 216 } 217 218 pub fn per_pubkey(self) -> TangleRateLimitRule { 219 self.per_pubkey 220 } 221 222 pub fn per_kind(self) -> TangleRateLimitRule { 223 self.per_kind 224 } 225 } 226 227 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 228 pub struct TangleGroupRateLimitConfig { 229 write_per_ip: TangleRateLimitRule, 230 write_per_pubkey: TangleRateLimitRule, 231 write_per_group: TangleRateLimitRule, 232 write_per_kind: TangleRateLimitRule, 233 join_flow: TangleRateLimitRule, 234 join_flow_per_ip: TangleRateLimitRule, 235 } 236 237 impl TangleGroupRateLimitConfig { 238 pub fn new( 239 write_per_ip: TangleRateLimitRule, 240 write_per_pubkey: TangleRateLimitRule, 241 write_per_group: TangleRateLimitRule, 242 write_per_kind: TangleRateLimitRule, 243 join_flow: TangleRateLimitRule, 244 join_flow_per_ip: TangleRateLimitRule, 245 ) -> Self { 246 Self { 247 write_per_ip, 248 write_per_pubkey, 249 write_per_group, 250 write_per_kind, 251 join_flow, 252 join_flow_per_ip, 253 } 254 } 255 256 pub fn write_per_ip(self) -> TangleRateLimitRule { 257 self.write_per_ip 258 } 259 260 pub fn write_per_pubkey(self) -> TangleRateLimitRule { 261 self.write_per_pubkey 262 } 263 264 pub fn write_per_group(self) -> TangleRateLimitRule { 265 self.write_per_group 266 } 267 268 pub fn write_per_kind(self) -> TangleRateLimitRule { 269 self.write_per_kind 270 } 271 272 pub fn join_flow(self) -> TangleRateLimitRule { 273 self.join_flow 274 } 275 276 pub fn join_flow_per_ip(self) -> TangleRateLimitRule { 277 self.join_flow_per_ip 278 } 279 } 280 281 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 282 pub struct TangleQueryRateLimitConfig { 283 per_ip: TangleRateLimitRule, 284 per_connection: TangleRateLimitRule, 285 per_pubkey: TangleRateLimitRule, 286 per_group: TangleRateLimitRule, 287 per_kind: TangleRateLimitRule, 288 broad: TangleRateLimitRule, 289 } 290 291 impl TangleQueryRateLimitConfig { 292 pub fn new( 293 per_ip: TangleRateLimitRule, 294 per_connection: TangleRateLimitRule, 295 per_pubkey: TangleRateLimitRule, 296 per_group: TangleRateLimitRule, 297 per_kind: TangleRateLimitRule, 298 broad: TangleRateLimitRule, 299 ) -> Self { 300 Self { 301 per_ip, 302 per_connection, 303 per_pubkey, 304 per_group, 305 per_kind, 306 broad, 307 } 308 } 309 310 pub fn per_ip(self) -> TangleRateLimitRule { 311 self.per_ip 312 } 313 314 pub fn per_connection(self) -> TangleRateLimitRule { 315 self.per_connection 316 } 317 318 pub fn per_pubkey(self) -> TangleRateLimitRule { 319 self.per_pubkey 320 } 321 322 pub fn per_group(self) -> TangleRateLimitRule { 323 self.per_group 324 } 325 326 pub fn per_kind(self) -> TangleRateLimitRule { 327 self.per_kind 328 } 329 330 pub fn broad(self) -> TangleRateLimitRule { 331 self.broad 332 } 333 } 334 335 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 336 pub struct TangleRateLimitRule { 337 window_seconds: u64, 338 max_hits: u64, 339 } 340 341 impl TangleRateLimitRule { 342 pub fn new(window_seconds: u64, max_hits: u64) -> Result<Self, BaseRelayError> { 343 if window_seconds == 0 { 344 return Err(BaseRelayError::invalid( 345 "rate limit window seconds must be greater than zero", 346 )); 347 } 348 if max_hits == 0 { 349 return Err(BaseRelayError::invalid( 350 "rate limit max hits must be greater than zero", 351 )); 352 } 353 Ok(Self { 354 window_seconds, 355 max_hits, 356 }) 357 } 358 359 pub fn window_seconds(self) -> u64 { 360 self.window_seconds 361 } 362 363 pub fn max_hits(self) -> u64 { 364 self.max_hits 365 } 366 } 367 368 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 369 pub enum TangleRateLimitDecision { 370 Allowed { 371 remaining: u64, 372 reset_at: UnixTimestamp, 373 }, 374 Rejected { 375 reset_at: UnixTimestamp, 376 }, 377 } 378 379 impl TangleRateLimitDecision { 380 pub fn is_allowed(self) -> bool { 381 matches!(self, Self::Allowed { .. }) 382 } 383 384 pub fn reset_at(self) -> UnixTimestamp { 385 match self { 386 Self::Allowed { reset_at, .. } | Self::Rejected { reset_at } => reset_at, 387 } 388 } 389 390 pub fn remaining(self) -> u64 { 391 match self { 392 Self::Allowed { remaining, .. } => remaining, 393 Self::Rejected { .. } => 0, 394 } 395 } 396 } 397 398 #[derive(Debug, Clone, Default)] 399 pub struct TangleRateLimiter { 400 entries: Arc<Mutex<BTreeMap<TangleRateLimitKey, TangleRateLimitEntry>>>, 401 } 402 403 impl TangleRateLimiter { 404 pub fn new() -> Self { 405 Self::default() 406 } 407 408 pub fn record( 409 &self, 410 key: TangleRateLimitKey, 411 rule: TangleRateLimitRule, 412 now: UnixTimestamp, 413 ) -> TangleRateLimitDecision { 414 let mut entries = self 415 .entries 416 .lock() 417 .unwrap_or_else(|error| error.into_inner()); 418 let entry = entries 419 .entry(key) 420 .and_modify(|entry| entry.reset_if_expired(rule, now)) 421 .or_insert_with(|| TangleRateLimitEntry::new(rule, now)); 422 if entry.hits >= rule.max_hits() { 423 return TangleRateLimitDecision::Rejected { 424 reset_at: entry.reset_at, 425 }; 426 } 427 entry.hits = entry.hits.saturating_add(1); 428 TangleRateLimitDecision::Allowed { 429 remaining: rule.max_hits().saturating_sub(entry.hits), 430 reset_at: entry.reset_at, 431 } 432 } 433 434 pub fn hits(&self, key: &TangleRateLimitKey) -> u64 { 435 self.entries 436 .lock() 437 .unwrap_or_else(|error| error.into_inner()) 438 .get(key) 439 .map(|entry| entry.hits) 440 .unwrap_or(0) 441 } 442 443 pub fn retain_active(&self, now: UnixTimestamp) { 444 self.entries 445 .lock() 446 .unwrap_or_else(|error| error.into_inner()) 447 .retain(|_, entry| now.as_u64() < entry.reset_at.as_u64()); 448 } 449 450 pub fn tracked_key_count(&self) -> usize { 451 self.entries 452 .lock() 453 .unwrap_or_else(|error| error.into_inner()) 454 .len() 455 } 456 } 457 458 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 459 struct TangleRateLimitEntry { 460 reset_at: UnixTimestamp, 461 hits: u64, 462 } 463 464 impl TangleRateLimitEntry { 465 fn new(rule: TangleRateLimitRule, now: UnixTimestamp) -> Self { 466 Self { 467 reset_at: reset_at(rule, now), 468 hits: 0, 469 } 470 } 471 472 fn reset_if_expired(&mut self, rule: TangleRateLimitRule, now: UnixTimestamp) { 473 if now.as_u64() >= self.reset_at.as_u64() { 474 *self = Self::new(rule, now); 475 } 476 } 477 } 478 479 fn reset_at(rule: TangleRateLimitRule, now: UnixTimestamp) -> UnixTimestamp { 480 UnixTimestamp::from(now.as_u64().saturating_add(rule.window_seconds())) 481 } 482 483 #[cfg(test)] 484 mod tests { 485 use super::{ 486 TangleAuthRateLimitConfig, TangleEventRateLimitConfig, TangleGroupRateLimitConfig, 487 TangleQueryRateLimitConfig, TangleRateLimitConfig, TangleRateLimitDecision, 488 TangleRateLimitKey, TangleRateLimitQueryClass, TangleRateLimitRule, TangleRateLimitScope, 489 TangleRateLimiter, 490 }; 491 use std::net::{IpAddr, Ipv4Addr}; 492 use tangle_groups::GroupId; 493 use tangle_protocol::{Kind, PublicKeyHex, UnixTimestamp}; 494 495 #[test] 496 fn rate_limit_rules_reject_zero_values() { 497 assert_eq!( 498 TangleRateLimitRule::new(0, 1) 499 .expect_err("window") 500 .prefixed_message(), 501 "invalid: rate limit window seconds must be greater than zero" 502 ); 503 assert_eq!( 504 TangleRateLimitRule::new(1, 0) 505 .expect_err("max hits") 506 .prefixed_message(), 507 "invalid: rate limit max hits must be greater than zero" 508 ); 509 } 510 511 #[test] 512 fn rate_limiter_tracks_required_key_dimensions() { 513 let limiter = TangleRateLimiter::new(); 514 let rule = TangleRateLimitRule::new(60, 1).expect("rule"); 515 let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); 516 let pubkey = PublicKeyHex::new(&"1".repeat(64)).expect("pubkey"); 517 let group_id = GroupId::new("farm").expect("group"); 518 let kind = Kind::new(1).expect("kind"); 519 let keys = [ 520 TangleRateLimitKey::ip(TangleRateLimitScope::Event, ip), 521 TangleRateLimitKey::pubkey(TangleRateLimitScope::Event, pubkey.clone()), 522 TangleRateLimitKey::group(TangleRateLimitScope::GroupWrite, group_id.clone()), 523 TangleRateLimitKey::kind(TangleRateLimitScope::Event, kind), 524 TangleRateLimitKey::auth_failure(Some(ip), Some(pubkey.clone())), 525 TangleRateLimitKey::join_flow_ip(group_id.clone(), ip), 526 TangleRateLimitKey::join_flow(group_id, pubkey), 527 TangleRateLimitKey::connection(TangleRateLimitScope::Req, 42), 528 TangleRateLimitKey::query_class( 529 TangleRateLimitScope::Count, 530 TangleRateLimitQueryClass::Broad, 531 ), 532 ]; 533 534 for key in keys { 535 assert!( 536 limiter 537 .record(key.clone(), rule, UnixTimestamp::from(1)) 538 .is_allowed() 539 ); 540 assert_eq!( 541 limiter.record(key.clone(), rule, UnixTimestamp::from(2)), 542 TangleRateLimitDecision::Rejected { 543 reset_at: UnixTimestamp::from(61) 544 } 545 ); 546 assert_eq!(limiter.hits(&key), 1); 547 } 548 assert_eq!(limiter.tracked_key_count(), 9); 549 } 550 551 #[test] 552 fn rate_limiter_counts_per_key_and_resets_after_window() { 553 let limiter = TangleRateLimiter::new(); 554 let rule = TangleRateLimitRule::new(10, 2).expect("rule"); 555 let key = TangleRateLimitKey::ip( 556 TangleRateLimitScope::Event, 557 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 558 ); 559 560 let first = limiter.record(key.clone(), rule, UnixTimestamp::from(100)); 561 let second = limiter.record(key.clone(), rule, UnixTimestamp::from(101)); 562 let third = limiter.record(key.clone(), rule, UnixTimestamp::from(102)); 563 let after_window = limiter.record(key, rule, UnixTimestamp::from(110)); 564 565 assert_eq!( 566 first, 567 TangleRateLimitDecision::Allowed { 568 remaining: 1, 569 reset_at: UnixTimestamp::from(110) 570 } 571 ); 572 assert_eq!( 573 second, 574 TangleRateLimitDecision::Allowed { 575 remaining: 0, 576 reset_at: UnixTimestamp::from(110) 577 } 578 ); 579 assert_eq!( 580 third, 581 TangleRateLimitDecision::Rejected { 582 reset_at: UnixTimestamp::from(110) 583 } 584 ); 585 assert_eq!( 586 after_window, 587 TangleRateLimitDecision::Allowed { 588 remaining: 1, 589 reset_at: UnixTimestamp::from(120) 590 } 591 ); 592 } 593 594 #[test] 595 fn rate_limiter_drops_expired_key_state() { 596 let limiter = TangleRateLimiter::new(); 597 let rule = TangleRateLimitRule::new(5, 1).expect("rule"); 598 limiter.record( 599 TangleRateLimitKey::ip( 600 TangleRateLimitScope::Event, 601 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 602 ), 603 rule, 604 UnixTimestamp::from(1), 605 ); 606 limiter.record( 607 TangleRateLimitKey::ip( 608 TangleRateLimitScope::Event, 609 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 610 ), 611 rule, 612 UnixTimestamp::from(5), 613 ); 614 615 limiter.retain_active(UnixTimestamp::from(6)); 616 617 assert_eq!(limiter.tracked_key_count(), 1); 618 } 619 620 #[test] 621 fn scoped_pubkey_keys_do_not_share_buckets() { 622 let limiter = TangleRateLimiter::new(); 623 let rule = TangleRateLimitRule::new(60, 1).expect("rule"); 624 let pubkey = PublicKeyHex::new(&"1".repeat(64)).expect("pubkey"); 625 let auth_key = TangleRateLimitKey::pubkey(TangleRateLimitScope::Auth, pubkey.clone()); 626 let event_key = TangleRateLimitKey::pubkey(TangleRateLimitScope::Event, pubkey); 627 628 assert!( 629 limiter 630 .record(auth_key.clone(), rule, UnixTimestamp::from(1)) 631 .is_allowed() 632 ); 633 assert!( 634 limiter 635 .record(event_key.clone(), rule, UnixTimestamp::from(1)) 636 .is_allowed() 637 ); 638 assert!( 639 !limiter 640 .record(auth_key, rule, UnixTimestamp::from(2)) 641 .is_allowed() 642 ); 643 assert!( 644 !limiter 645 .record(event_key, rule, UnixTimestamp::from(2)) 646 .is_allowed() 647 ); 648 } 649 650 #[test] 651 fn rate_limit_config_exposes_auth_and_event_rules() { 652 let auth_pubkey = TangleRateLimitRule::new(60, 2).expect("auth pubkey"); 653 let auth_failures = TangleRateLimitRule::new(300, 3).expect("auth failures"); 654 let auth_ip = TangleRateLimitRule::new(60, 4).expect("auth ip"); 655 let auth_failures_ip = TangleRateLimitRule::new(300, 5).expect("auth failures ip"); 656 let event_ip = TangleRateLimitRule::new(60, 6).expect("event ip"); 657 let event_pubkey = TangleRateLimitRule::new(60, 7).expect("event pubkey"); 658 let event_kind = TangleRateLimitRule::new(60, 8).expect("event kind"); 659 let group_ip = TangleRateLimitRule::new(60, 9).expect("group ip"); 660 let group_pubkey = TangleRateLimitRule::new(60, 10).expect("group pubkey"); 661 let group_write = TangleRateLimitRule::new(60, 11).expect("group write"); 662 let group_kind = TangleRateLimitRule::new(60, 12).expect("group kind"); 663 let group_join = TangleRateLimitRule::new(300, 13).expect("group join"); 664 let group_join_ip = TangleRateLimitRule::new(300, 14).expect("group join ip"); 665 let req_ip = TangleRateLimitRule::new(60, 15).expect("req ip"); 666 let req_connection = TangleRateLimitRule::new(60, 16).expect("req connection"); 667 let req_pubkey = TangleRateLimitRule::new(60, 17).expect("req pubkey"); 668 let req_group = TangleRateLimitRule::new(60, 18).expect("req group"); 669 let req_kind = TangleRateLimitRule::new(60, 19).expect("req kind"); 670 let req_broad = TangleRateLimitRule::new(60, 20).expect("req broad"); 671 let count_ip = TangleRateLimitRule::new(60, 21).expect("count ip"); 672 let count_connection = TangleRateLimitRule::new(60, 22).expect("count connection"); 673 let count_pubkey = TangleRateLimitRule::new(60, 23).expect("count pubkey"); 674 let count_group = TangleRateLimitRule::new(60, 24).expect("count group"); 675 let count_kind = TangleRateLimitRule::new(60, 25).expect("count kind"); 676 let count_broad = TangleRateLimitRule::new(60, 26).expect("count broad"); 677 let config = TangleRateLimitConfig::new( 678 TangleAuthRateLimitConfig::new(auth_ip, auth_pubkey, auth_failures, auth_failures_ip), 679 TangleEventRateLimitConfig::new(event_ip, event_pubkey, event_kind), 680 TangleGroupRateLimitConfig::new( 681 group_ip, 682 group_pubkey, 683 group_write, 684 group_kind, 685 group_join, 686 group_join_ip, 687 ), 688 TangleQueryRateLimitConfig::new( 689 req_ip, 690 req_connection, 691 req_pubkey, 692 req_group, 693 req_kind, 694 req_broad, 695 ), 696 TangleQueryRateLimitConfig::new( 697 count_ip, 698 count_connection, 699 count_pubkey, 700 count_group, 701 count_kind, 702 count_broad, 703 ), 704 ); 705 706 assert_eq!(config.auth().per_ip(), auth_ip); 707 assert_eq!(config.auth().per_pubkey(), auth_pubkey); 708 assert_eq!(config.auth().failures(), auth_failures); 709 assert_eq!(config.auth().failures_per_ip(), auth_failures_ip); 710 assert_eq!(config.event().per_ip(), event_ip); 711 assert_eq!(config.event().per_pubkey(), event_pubkey); 712 assert_eq!(config.event().per_kind(), event_kind); 713 assert_eq!(config.group().write_per_ip(), group_ip); 714 assert_eq!(config.group().write_per_pubkey(), group_pubkey); 715 assert_eq!(config.group().write_per_group(), group_write); 716 assert_eq!(config.group().write_per_kind(), group_kind); 717 assert_eq!(config.group().join_flow(), group_join); 718 assert_eq!(config.group().join_flow_per_ip(), group_join_ip); 719 assert_eq!(config.req().per_ip(), req_ip); 720 assert_eq!(config.req().per_connection(), req_connection); 721 assert_eq!(config.req().per_pubkey(), req_pubkey); 722 assert_eq!(config.req().per_group(), req_group); 723 assert_eq!(config.req().per_kind(), req_kind); 724 assert_eq!(config.req().broad(), req_broad); 725 assert_eq!(config.count().per_ip(), count_ip); 726 assert_eq!(config.count().per_connection(), count_connection); 727 assert_eq!(config.count().per_pubkey(), count_pubkey); 728 assert_eq!(config.count().per_group(), count_group); 729 assert_eq!(config.count().per_kind(), count_kind); 730 assert_eq!(config.count().broad(), count_broad); 731 } 732 }