├── .github ├── dependabot.yml └── workflows │ └── ci_checks.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src ├── gossip.rs ├── layer ├── cyclon.rs ├── mod.rs ├── rings.rs └── vicinity.rs ├── lib.rs ├── priority_map.rs ├── profile.rs ├── profiles.rs ├── topic.rs └── topology.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/ci_checks.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Health Check 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | ci: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | rust: 18 | - stable 19 | - 1.51.0 # Minimum supported rust version 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - uses: actions-rs/toolchain@v1 25 | with: 26 | profile: minimal 27 | toolchain: ${{ matrix.rust }} 28 | override: true 29 | components: rustfmt, clippy 30 | 31 | - uses: actions-rs/cargo@v1 32 | with: 33 | command: build 34 | 35 | - uses: actions-rs/cargo@v1 36 | with: 37 | command: test 38 | 39 | - uses: actions-rs/cargo@v1 40 | with: 41 | command: fmt 42 | args: --all -- --check 43 | 44 | - uses: actions-rs/cargo@v1 45 | with: 46 | command: clippy 47 | args: --all --all-targets --all-features -- --deny "clippy::all" 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .idea/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "poldercast" 3 | version = "1.2.1" 4 | license = "MIT OR Apache-2.0" 5 | authors = ["Nicolas Di Prima "] 6 | edition = "2018" 7 | readme = "README.md" 8 | repository = "https://github.com/primetype/poldercast" 9 | homepage = "https://github.com/primetype/poldercast#README.md" 10 | keywords = [ "P2P", "Topology" ] 11 | description = """ 12 | Peer to Peer topology management 13 | """ 14 | 15 | [dependencies] 16 | keynesis = { version = "1.4" } 17 | thiserror = "1.0" 18 | hex = "0.4" 19 | lru = "0.7" 20 | 21 | [dev-dependencies] 22 | quickcheck = "1.0.3" 23 | quickcheck_macros = "1.0.0" 24 | 25 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2021 Prime Type Ltd 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2021 Prime Type Ltd 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Poldercast's P2P topology organization 2 | 3 | This crate implements the Poldercast's Peer to Peer (P2P) topology construction. 4 | The idea is to allow the node to participate actively into building the decentralized 5 | topology of the p2p network. 6 | 7 | This is done through gossiping. This is the process of sharing with others topology 8 | information: who is on the network, how to reach them and what are they interested 9 | about. 10 | 11 | In the poldercast paper there are 3 different modules implementing 3 different strategies 12 | to select nodes to gossip to and to select the gossiping data: 13 | 14 | * `Cyclon`: this module is responsible to add a bit of randomness in the gossiping 15 | strategy. It also prevent nodes to be left behind, favouring contacting Nodes 16 | we have the least used; 17 | * `Vicinity`: this module helps with building an interest-induced links between the 18 | nodes of the topology. Making sure that nodes that have common interests are often 19 | in touch. 20 | * `Rings`: this module create an oriented list of nodes. It is an arbitrary way to 21 | link the nodes in the network. For each topics, the node will select a set of _close_ 22 | nodes (see documentation in the implementation for more details about this). 23 | 24 | # Papers of reference 25 | 26 | This crate is a concrete implementation of the Poldercast paper: 27 | 28 | * [PolderCast: Fast, Robust, and Scalable Architecture for P2P Topic-Based Pub/Sub](https://hal.inria.fr/hal-01555561/document) 29 | * [CYCLON: Inexpensive Membership Management for Unstructured P2P Overlays](http://www.csl.mtu.edu/cs6461/www/Reading/Voulgaris-jnsm05.pdf) 30 | * [Vicinity: Epidemic-Based Self-Organization in Peer-To-Peer Systems](https://www.cs.vu.nl/~spyros/papers/Thesis-Voulgaris.pdf) 31 | 32 | # Customization 33 | 34 | Now this crate allows room for different kind of management of the modules. 35 | It is possible to add the Poldercast default modules (the default). It is 36 | possible to setup a custom topology strategy utilizing part or all 37 | of the poldercast's modules or with new custom modules. 38 | 39 | # License 40 | 41 | This project is licensed under either of the following licenses: 42 | 43 | * Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 44 | * MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) 45 | 46 | Please choose the licence you want to use. 47 | -------------------------------------------------------------------------------- /src/gossip.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Subscription, SubscriptionError, SubscriptionSlice, Subscriptions, SubscriptionsSlice, 3 | }; 4 | use keynesis::{key::ed25519, passport::block::Time}; 5 | use std::{ 6 | convert::TryInto as _, 7 | fmt::{self, Formatter}, 8 | net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, 9 | }; 10 | use thiserror::Error; 11 | 12 | const INFO_INDEX: usize = 0; 13 | const INFO_END: usize = INFO_INDEX + GossipInfo::SIZE; 14 | const ID_INDEX: usize = INFO_END; 15 | const ID_END: usize = ID_INDEX + ed25519::PublicKey::SIZE; 16 | const TIME_INDEX: usize = ID_END; 17 | const TIME_END: usize = TIME_INDEX + Time::SIZE; 18 | 19 | const IPV4_INDEX: usize = TIME_END; 20 | const IPV4_END: usize = IPV4_INDEX + 4; 21 | 22 | const IPV6_INDEX: usize = TIME_END; 23 | const IPV6_END: usize = IPV6_INDEX + 16; 24 | 25 | #[derive(Copy, Clone, Eq, PartialEq, Hash)] 26 | struct GossipInfo(u16); 27 | 28 | #[derive(Clone, Eq, PartialEq, Hash)] 29 | pub struct Gossip(Vec); 30 | 31 | #[derive(Copy, Clone, Eq, PartialEq, Hash)] 32 | pub struct GossipSlice<'a>(&'a [u8]); 33 | 34 | #[derive(Debug, Error)] 35 | pub enum GossipError { 36 | #[error("Invalid gossip size, expected at least {min}")] 37 | InvalidSize { min: usize, max: Option }, 38 | 39 | #[error("The signature does not match the public key and the content")] 40 | InvalidSignature, 41 | 42 | #[error("Invalid subscription ({index}): {error}")] 43 | InvalidSubscription { 44 | index: usize, 45 | error: SubscriptionError, 46 | }, 47 | } 48 | 49 | impl GossipInfo { 50 | const SIZE: usize = std::mem::size_of::(); 51 | 52 | fn try_from_slice(slice: &[u8]) -> Result { 53 | if slice.len() < Self::SIZE { 54 | return Err(GossipError::InvalidSize { 55 | min: Self::SIZE, 56 | max: None, 57 | }); 58 | } 59 | 60 | let sub = u16::from_be_bytes( 61 | slice[..Self::SIZE] 62 | .try_into() 63 | .expect("valid 2 bytes on the slice"), 64 | ); 65 | Ok(Self(sub)) 66 | } 67 | 68 | fn set_num_subscriptions(&mut self, num: usize) { 69 | let num = num & Subscriptions::MAX_NUM_SUBSCRIPTIONS; 70 | self.0 &= !(Subscriptions::MAX_NUM_SUBSCRIPTIONS as u16); 71 | self.0 |= num as u16; 72 | } 73 | 74 | fn set_ipv4(&mut self) { 75 | self.0 |= 0b1000_0000_0000_0000; 76 | } 77 | 78 | fn set_ipv6(&mut self) { 79 | self.0 &= 0b0111_1111_1111_1111; 80 | } 81 | 82 | #[inline(always)] 83 | fn is_ipv4(&self) -> bool { 84 | self.0 & 0b1000_0000_0000_0000 == 0b1000_0000_0000_0000 85 | } 86 | 87 | #[inline(always)] 88 | fn is_ipv6(&self) -> bool { 89 | !self.is_ipv4() 90 | } 91 | 92 | #[inline(always)] 93 | fn num_subscriptions(&self) -> usize { 94 | (self.0 & 0b0000_0011_1111_1111) as usize 95 | } 96 | 97 | #[inline(always)] 98 | const fn ip_start(&self) -> usize { 99 | TIME_END 100 | } 101 | 102 | #[inline(always)] 103 | fn ip_end(&self) -> usize { 104 | let length = if self.is_ipv4() { 4 } else { 16 }; 105 | 106 | self.ip_start() + length 107 | } 108 | 109 | #[inline(always)] 110 | fn port_start(&self) -> usize { 111 | self.ip_end() 112 | } 113 | 114 | #[inline(always)] 115 | fn port_end(&self) -> usize { 116 | self.port_start() + 2 117 | } 118 | 119 | #[inline(always)] 120 | fn subscription_start(&self) -> usize { 121 | self.port_end() 122 | } 123 | 124 | #[inline(always)] 125 | fn subscription_end(&self) -> usize { 126 | self.subscription_start() + (self.num_subscriptions() * Subscription::SIZE) 127 | } 128 | 129 | #[inline(always)] 130 | fn signature_start(&self) -> usize { 131 | self.subscription_end() 132 | } 133 | 134 | #[inline(always)] 135 | fn signature_end(&self) -> usize { 136 | self.signature_start() + ed25519::Signature::SIZE 137 | } 138 | } 139 | 140 | impl Gossip { 141 | pub const MAX_NUM_SUBSCRIPTIONS: usize = Subscriptions::MAX_NUM_SUBSCRIPTIONS; 142 | pub const MIN_SIZE: usize = 143 | IPV4_END + ed25519::Signature::SIZE + Self::MAX_NUM_SUBSCRIPTIONS * Subscription::SIZE; 144 | pub const MAX_SIZE: usize = 145 | IPV6_END + ed25519::Signature::SIZE + Self::MAX_NUM_SUBSCRIPTIONS * Subscription::SIZE; 146 | 147 | /// prepare a gossip without our address and public key 148 | pub fn new( 149 | address: SocketAddr, 150 | id: &ed25519::SecretKey, 151 | subscriptions: SubscriptionsSlice<'_>, 152 | ) -> Self { 153 | let mut info = GossipInfo(0); 154 | info.set_num_subscriptions(subscriptions.number_subscriptions()); 155 | if address.is_ipv4() { 156 | info.set_ipv4() 157 | } else if address.is_ipv6() { 158 | info.set_ipv6() 159 | } 160 | 161 | let signature_start = info.signature_start(); 162 | let signature_end = info.signature_end(); 163 | 164 | let mut bytes = vec![0; signature_end]; 165 | 166 | bytes[INFO_INDEX..INFO_END].copy_from_slice(&info.0.to_be_bytes()); 167 | bytes[ID_INDEX..ID_END].copy_from_slice(id.public_key().as_ref()); 168 | bytes[TIME_INDEX..TIME_END].copy_from_slice(&Time::now().to_be_bytes()); 169 | 170 | match address.ip() { 171 | IpAddr::V4(v4) => { 172 | let ip = v4.octets(); 173 | bytes[IPV4_INDEX..IPV4_END].copy_from_slice(&ip); 174 | } 175 | IpAddr::V6(v6) => { 176 | let ip = v6.octets(); 177 | bytes[IPV6_INDEX..IPV6_END].copy_from_slice(&ip); 178 | } 179 | }; 180 | bytes[info.port_start()..info.port_end()].copy_from_slice(&address.port().to_be_bytes()); 181 | bytes[info.subscription_start()..info.subscription_end()] 182 | .copy_from_slice(subscriptions.as_ref()); 183 | 184 | let signature = id.sign(&bytes[..signature_start]); 185 | bytes[signature_start..signature_end].copy_from_slice(signature.as_ref()); 186 | 187 | Self(bytes) 188 | } 189 | 190 | pub fn as_slice(&self) -> GossipSlice<'_> { 191 | GossipSlice(&self.0) 192 | } 193 | 194 | pub fn id(&self) -> ed25519::PublicKey { 195 | self.as_slice().id() 196 | } 197 | 198 | pub fn time(&self) -> Time { 199 | self.as_slice().time() 200 | } 201 | 202 | pub fn address(&self) -> SocketAddr { 203 | self.as_slice().address() 204 | } 205 | 206 | pub fn subscriptions(&self) -> SubscriptionsSlice<'_> { 207 | self.as_slice().subscriptions() 208 | } 209 | 210 | pub fn signature(&self) -> ed25519::Signature { 211 | self.as_slice().signature() 212 | } 213 | } 214 | 215 | impl<'a> GossipSlice<'a> { 216 | pub fn try_from_slice(slice: &'a [u8]) -> Result { 217 | let info = GossipInfo::try_from_slice(slice)?; 218 | 219 | if info.signature_end() != slice.len() { 220 | return Err(GossipError::InvalidSize { 221 | min: info.signature_end(), 222 | max: Some(info.signature_end()), 223 | }); 224 | } 225 | 226 | let gossip = Self::from_slice_unchecked(slice); 227 | 228 | for (index, sub) in gossip.subscriptions().iter().enumerate() { 229 | let slice = sub.as_ref(); 230 | let _ = SubscriptionSlice::try_from_slice(slice) 231 | .map_err(|error| GossipError::InvalidSubscription { index, error })?; 232 | } 233 | 234 | let pk = gossip.id(); 235 | let signature = gossip.signature(); 236 | let signed_data = gossip.signed_data(); 237 | 238 | if !pk.verify(signed_data, &signature) { 239 | Err(GossipError::InvalidSignature) 240 | } else { 241 | Ok(Self(slice)) 242 | } 243 | } 244 | 245 | pub fn from_slice_unchecked(slice: &'a [u8]) -> Self { 246 | #[cfg(debug_assertions)] 247 | { 248 | let info = 249 | GossipInfo::try_from_slice(slice).expect("should have the gossip info slice"); 250 | debug_assert_eq!(info.signature_end(), slice.len()); 251 | } 252 | 253 | Self(slice) 254 | } 255 | 256 | pub fn to_owned(self) -> Gossip { 257 | Gossip(self.0.to_owned()) 258 | } 259 | 260 | fn info(&self) -> GossipInfo { 261 | let sub = u16::from_be_bytes( 262 | self.0[INFO_INDEX..INFO_END] 263 | .try_into() 264 | .expect("valid 2 bytes on the slice"), 265 | ); 266 | GossipInfo(sub) 267 | } 268 | 269 | pub fn id(&self) -> ed25519::PublicKey { 270 | let pk: [u8; ed25519::PublicKey::SIZE] = self.0[ID_INDEX..ID_END] 271 | .try_into() 272 | .expect("valid public key"); 273 | ed25519::PublicKey::from(pk) 274 | } 275 | 276 | pub fn time(&self) -> Time { 277 | let time = u32::from_be_bytes( 278 | self.0[TIME_INDEX..TIME_END] 279 | .try_into() 280 | .expect("Valid 4 bytes on the gossip"), 281 | ); 282 | Time::from(time) 283 | } 284 | 285 | fn ipv4(&self) -> Ipv4Addr { 286 | let ip: [u8; 4] = self.0[IPV4_INDEX..IPV4_END] 287 | .try_into() 288 | .expect("bytes of the ipv4 address"); 289 | Ipv4Addr::from(ip) 290 | } 291 | 292 | fn ipv6(&self) -> Ipv6Addr { 293 | let ip: [u8; 16] = self.0[IPV6_INDEX..IPV6_END] 294 | .try_into() 295 | .expect("bytes of the ipv6 address"); 296 | Ipv6Addr::from(ip) 297 | } 298 | 299 | pub fn address(&self) -> SocketAddr { 300 | let info = self.info(); 301 | 302 | let (ip, port_index, port_end) = if info.is_ipv4() { 303 | let ipv4 = self.ipv4(); 304 | (IpAddr::V4(ipv4), IPV4_END, IPV4_END + 2) 305 | } else if info.is_ipv6() { 306 | let ipv6 = self.ipv6(); 307 | (IpAddr::V6(ipv6), IPV6_END, IPV6_END + 2) 308 | } else { 309 | unreachable!("It should be either an IPv6 or IPv4") 310 | }; 311 | let port = u16::from_be_bytes( 312 | self.0[port_index..port_end] 313 | .try_into() 314 | .expect("valid 2 bytes on the slice"), 315 | ); 316 | SocketAddr::new(ip, port) 317 | } 318 | 319 | pub fn subscriptions(&self) -> SubscriptionsSlice<'a> { 320 | let info = self.info(); 321 | 322 | let start_index = if info.is_ipv4() { 323 | IPV4_END + 2 324 | } else if info.is_ipv6() { 325 | IPV6_END + 2 326 | } else { 327 | unreachable!("It should be either an IPv6 or IPv4") 328 | }; 329 | let slice = 330 | &self.0[start_index..start_index + (info.num_subscriptions() * Subscription::SIZE)]; 331 | SubscriptionsSlice::from_slice_unchecked(slice) 332 | } 333 | 334 | fn signed_data(&self) -> &[u8] { 335 | let info = self.info(); 336 | &self.0[..info.signature_start()] 337 | } 338 | 339 | pub fn signature(&self) -> ed25519::Signature { 340 | let info = self.info(); 341 | let signature: [u8; ed25519::Signature::SIZE] = self.0 342 | [info.signature_start()..info.signature_end()] 343 | .try_into() 344 | .expect("64 bytes of the signature"); 345 | signature.into() 346 | } 347 | } 348 | 349 | /* AsRef ******************************************************************* */ 350 | 351 | impl<'a> AsRef<[u8]> for GossipSlice<'a> { 352 | fn as_ref(&self) -> &[u8] { 353 | self.0 354 | } 355 | } 356 | 357 | impl AsRef<[u8]> for Gossip { 358 | fn as_ref(&self) -> &[u8] { 359 | self.0.as_ref() 360 | } 361 | } 362 | 363 | /* Formatter *************************************************************** */ 364 | 365 | impl<'a> fmt::Debug for GossipSlice<'a> { 366 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 367 | f.debug_struct("Gossip") 368 | .field("id", &self.id()) 369 | .field("time", &self.time()) 370 | .field("address", &self.address()) 371 | .field("subscriptions", &self.subscriptions()) 372 | .field("signature", &self.signature()) 373 | .finish() 374 | } 375 | } 376 | 377 | impl fmt::Debug for Gossip { 378 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 379 | self.as_slice().fmt(f) 380 | } 381 | } 382 | 383 | #[cfg(test)] 384 | mod tests { 385 | use super::*; 386 | use keynesis::Seed; 387 | use quickcheck::{Arbitrary, Gen}; 388 | 389 | impl Arbitrary for Gossip { 390 | fn arbitrary(g: &mut Gen) -> Self { 391 | let mut seed = [0; Seed::SIZE]; 392 | seed.iter_mut().for_each(|byte| { 393 | *byte = u8::arbitrary(g); 394 | }); 395 | let mut rng = Seed::from(seed).into_rand_chacha(); 396 | 397 | let address = SocketAddr::arbitrary(g); 398 | let id = ed25519::SecretKey::new(&mut rng); 399 | let subscriptions = Subscriptions::arbitrary(g); 400 | 401 | Self::new(address, &id, subscriptions.as_slice()) 402 | } 403 | } 404 | 405 | #[test] 406 | fn simple_ipv4() { 407 | let mut rng = Seed::from([0; Seed::SIZE]).into_rand_chacha(); 408 | let id = ed25519::SecretKey::new(&mut rng); 409 | 410 | let address: SocketAddr = "127.0.0.1:9876".parse().unwrap(); 411 | let subscriptions = Subscriptions::new(); 412 | 413 | let gossip = Gossip::new(address, &id, subscriptions.as_slice()); 414 | 415 | let slice = gossip.as_slice(); 416 | let decoded = GossipSlice::try_from_slice(slice.as_ref()) 417 | .unwrap() 418 | .to_owned(); 419 | 420 | assert_eq!(gossip.0, decoded.0); 421 | assert_eq!(decoded.address(), address); 422 | } 423 | 424 | #[test] 425 | fn simple_ipv6() { 426 | let mut rng = Seed::from([0; Seed::SIZE]).into_rand_chacha(); 427 | let id = ed25519::SecretKey::new(&mut rng); 428 | 429 | let address: SocketAddr = "[::1]:9876".parse().unwrap(); 430 | let subscriptions = Subscriptions::new(); 431 | 432 | let gossip = Gossip::new(address, &id, subscriptions.as_slice()); 433 | 434 | let slice = gossip.as_slice(); 435 | let decoded = GossipSlice::try_from_slice(slice.as_ref()) 436 | .unwrap() 437 | .to_owned(); 438 | 439 | assert_eq!(gossip.0, decoded.0); 440 | assert_eq!(decoded.address(), address); 441 | } 442 | 443 | #[quickcheck] 444 | fn parse_valid_gossip(gossip: Gossip) -> bool { 445 | let slice = gossip.as_slice(); 446 | let decoded = GossipSlice::try_from_slice(slice.as_ref()) 447 | .unwrap() 448 | .to_owned(); 449 | 450 | assert_eq!(gossip.0, decoded.0); 451 | true 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /src/layer/cyclon.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | layer::{Layer, ViewBuilder}, 3 | InterestLevel, PriorityMap, Profile, Topic, 4 | }; 5 | use keynesis::key::ed25519; 6 | 7 | pub struct Cyclon { 8 | nodes: lru::LruCache, 9 | } 10 | 11 | impl Cyclon { 12 | pub fn new(length: usize) -> Self { 13 | Self { 14 | nodes: lru::LruCache::new(length), 15 | } 16 | } 17 | } 18 | 19 | impl Layer for Cyclon { 20 | fn name(&self) -> &'static str { 21 | "poldercast::cyclon" 22 | } 23 | 24 | fn view(&mut self, builder: &mut ViewBuilder) { 25 | self.nodes.iter().for_each(|(k, _)| builder.add(k)); 26 | } 27 | 28 | fn remove(&mut self, id: &ed25519::PublicKey) { 29 | self.nodes.pop(id); 30 | } 31 | fn reset(&mut self) { 32 | self.nodes.clear(); 33 | } 34 | 35 | fn populate(&mut self, _our_profile: &Profile, new_profile: &Profile) { 36 | self.nodes.put(new_profile.id(), ()); 37 | } 38 | 39 | fn subscribe(&mut self, _topic: Topic) {} 40 | 41 | fn unsubscribe(&mut self, _topic: &Topic) {} 42 | 43 | fn subscriptions(&self, _output: &mut PriorityMap) {} 44 | } 45 | -------------------------------------------------------------------------------- /src/layer/mod.rs: -------------------------------------------------------------------------------- 1 | mod cyclon; 2 | mod rings; 3 | mod vicinity; 4 | 5 | pub use self::{cyclon::Cyclon, rings::Rings, vicinity::Vicinity}; 6 | use crate::{InterestLevel, PriorityMap, Profile, Topic}; 7 | use keynesis::key::ed25519; 8 | use std::collections::HashSet; 9 | 10 | pub trait Layer: Send { 11 | fn name(&self) -> &'static str; 12 | 13 | fn view(&mut self, builder: &mut ViewBuilder); 14 | 15 | fn remove(&mut self, id: &ed25519::PublicKey); 16 | fn reset(&mut self); 17 | 18 | fn subscribe(&mut self, topic: Topic); 19 | fn unsubscribe(&mut self, topic: &Topic); 20 | fn subscriptions(&self, output: &mut PriorityMap); 21 | 22 | fn populate(&mut self, our_profile: &Profile, new_profile: &Profile); 23 | } 24 | 25 | pub trait LayerBuilder { 26 | fn build_for_view(&self) -> Vec>; 27 | fn build_for_gossip(&self) -> Vec>; 28 | } 29 | 30 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 31 | pub enum Selection { 32 | Topic { topic: Topic }, 33 | Any, 34 | } 35 | 36 | #[doc(hidden)] 37 | pub struct ViewBuilder { 38 | event_origin: Option, 39 | 40 | selection: Selection, 41 | 42 | view: HashSet, 43 | } 44 | 45 | impl ViewBuilder { 46 | pub fn new(selection: Selection) -> Self { 47 | Self { 48 | event_origin: None, 49 | selection, 50 | view: HashSet::new(), 51 | } 52 | } 53 | 54 | pub fn with_origin(&mut self, origin: ed25519::PublicKey) -> &Self { 55 | self.event_origin = Some(origin); 56 | self 57 | } 58 | 59 | pub fn origin(&self) -> Option<&ed25519::PublicKey> { 60 | self.event_origin.as_ref() 61 | } 62 | 63 | pub fn selection(&self) -> Selection { 64 | self.selection 65 | } 66 | 67 | pub fn add(&mut self, node: &ed25519::PublicKey) { 68 | self.view.insert(*node); 69 | } 70 | 71 | pub(crate) fn build(self) -> HashSet { 72 | self.view 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/layer/rings.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | layer::{Layer, Selection, ViewBuilder}, 3 | InterestLevel, PriorityMap, Profile, Subscription, Subscriptions, Topic, 4 | }; 5 | use keynesis::key::ed25519; 6 | use std::cmp::Ordering; 7 | 8 | struct Ring { 9 | length: u8, 10 | predecessors: lru::LruCache, 11 | successors: lru::LruCache, 12 | 13 | current_low: Option, 14 | current_max: Option, 15 | } 16 | 17 | pub struct Rings { 18 | length: u8, 19 | links: lru::LruCache, 20 | } 21 | 22 | impl Ring { 23 | fn new(length: u8) -> Self { 24 | Self { 25 | length, 26 | predecessors: lru::LruCache::new(length as usize / 2), 27 | successors: lru::LruCache::new(length as usize / 2), 28 | current_low: None, 29 | current_max: None, 30 | } 31 | } 32 | 33 | pub fn remove(&mut self, id: &ed25519::PublicKey) { 34 | if self.predecessors.pop(id).is_some() { 35 | self.current_low = self.predecessors.iter().map(|(k, _)| k).min().copied(); 36 | } 37 | if self.successors.pop(id).is_some() { 38 | self.current_max = self.successors.iter().map(|(k, _)| k).max().copied(); 39 | } 40 | } 41 | 42 | pub fn interest_level(&self) -> InterestLevel { 43 | let max = self.length; 44 | let size = (self.predecessors.len() as u8).wrapping_add(self.successors.len() as u8); 45 | 46 | let multiplier = u8::MAX.wrapping_div_euclid(max); 47 | let level = max.wrapping_sub(size).wrapping_mul(multiplier); 48 | 49 | InterestLevel::new(level) 50 | } 51 | 52 | pub fn recipients(&mut self, builder: &mut ViewBuilder) { 53 | let (predecessor, successor) = if let Some(from) = builder.origin() { 54 | ( 55 | !self.predecessors.contains(from), 56 | !self.successors.contains(from), 57 | ) 58 | } else { 59 | (true, true) 60 | }; 61 | 62 | if predecessor { 63 | if let Some((key, ())) = self.predecessors.pop_lru() { 64 | builder.add(&key); 65 | self.predecessors.put(key, ()); 66 | } 67 | } 68 | 69 | if successor { 70 | if let Some((key, ())) = self.successors.pop_lru() { 71 | builder.add(&key); 72 | self.successors.put(key, ()); 73 | } 74 | } 75 | } 76 | 77 | pub fn receive_gossips(&mut self, our_id: &ed25519::PublicKey, their_id: &ed25519::PublicKey) { 78 | match our_id.cmp(their_id) { 79 | Ordering::Equal => { 80 | // same id, we can assume this is ourselves... even though we expect 81 | // ourselves to be filtered out already 82 | } 83 | Ordering::Less => { 84 | let new_low = if let Some(low) = self.current_low.as_ref() { 85 | let r = low < their_id; 86 | 87 | if r { 88 | self.predecessors.pop(low); 89 | } 90 | 91 | r 92 | } else { 93 | true 94 | }; 95 | 96 | if new_low { 97 | self.current_low = Some(*their_id); 98 | self.predecessors.put(*their_id, ()); 99 | } 100 | } 101 | Ordering::Greater => { 102 | let new_high = if let Some(high) = self.current_max.as_ref() { 103 | let r = high > their_id; 104 | 105 | if r { 106 | self.successors.pop(high); 107 | } 108 | 109 | r 110 | } else { 111 | true 112 | }; 113 | 114 | if new_high { 115 | self.current_max = Some(*their_id); 116 | self.successors.put(*their_id, ()); 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | impl Rings { 124 | pub fn new(length: u8) -> Self { 125 | Self { 126 | length, 127 | links: lru::LruCache::new(Subscriptions::MAX_NUM_SUBSCRIPTIONS), 128 | } 129 | } 130 | 131 | pub fn subscriptions(&self) -> Subscriptions { 132 | let mut subscriptions = Subscriptions::new(); 133 | 134 | for (topic, ring) in self.links.iter() { 135 | let interest_level = ring.interest_level(); 136 | 137 | if interest_level.no_interest() { 138 | // there there are no interests, just ignore it 139 | // and move on to the next item 140 | continue; 141 | } 142 | 143 | let subscription = Subscription::new(*topic, interest_level); 144 | if subscriptions.push(subscription.as_slice()).is_err() { 145 | // if we have reached the limit fo the subscriptions content 146 | // we stop there 147 | break; 148 | } 149 | } 150 | 151 | subscriptions 152 | } 153 | 154 | fn recipients_for_event(&mut self, topic: &Topic, builder: &mut ViewBuilder) { 155 | if let Some(ring) = self.links.get_mut(topic) { 156 | ring.recipients(builder); 157 | } 158 | } 159 | 160 | fn recipients_for_all(&mut self, builder: &mut ViewBuilder) { 161 | for (_, ring) in self.links.iter_mut() { 162 | ring.recipients(builder); 163 | } 164 | } 165 | 166 | pub fn receive_gossip( 167 | &mut self, 168 | our_id: &ed25519::PublicKey, 169 | their_id: &ed25519::PublicKey, 170 | topics: impl Iterator, 171 | ) { 172 | for topic in topics { 173 | if let Some(ring) = self.links.get_mut(&topic) { 174 | ring.receive_gossips(our_id, their_id); 175 | } 176 | } 177 | } 178 | } 179 | 180 | impl Layer for Rings { 181 | fn name(&self) -> &'static str { 182 | "poldercast::rings" 183 | } 184 | 185 | fn view(&mut self, builder: &mut ViewBuilder) { 186 | match builder.selection() { 187 | Selection::Any => { 188 | self.recipients_for_all(builder); 189 | } 190 | Selection::Topic { topic } => { 191 | self.recipients_for_event(&topic, builder); 192 | } 193 | } 194 | } 195 | 196 | fn remove(&mut self, id: &ed25519::PublicKey) { 197 | for (_, ring) in self.links.iter_mut() { 198 | ring.remove(id) 199 | } 200 | } 201 | fn reset(&mut self) { 202 | self.links.clear(); 203 | } 204 | 205 | fn populate(&mut self, our_profile: &Profile, new_profile: &Profile) { 206 | self.receive_gossip( 207 | &our_profile.id(), 208 | &new_profile.id(), 209 | new_profile.subscriptions().iter().map(|sub| sub.topic()), 210 | ) 211 | } 212 | 213 | fn subscribe(&mut self, topic: Topic) { 214 | if !self.links.contains(&topic) { 215 | self.links.put(topic, Ring::new(self.length)); 216 | } 217 | } 218 | fn unsubscribe(&mut self, topic: &Topic) { 219 | self.links.pop(topic); 220 | } 221 | fn subscriptions(&self, output: &mut PriorityMap) { 222 | for (topic, ring) in self.links.iter() { 223 | let interest_level = ring.interest_level(); 224 | 225 | if interest_level.no_interest() { 226 | // there there are no interests, just ignore it 227 | // and move on to the next item 228 | continue; 229 | } 230 | 231 | output.put(interest_level, *topic); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/layer/vicinity.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | layer::{Layer, ViewBuilder}, 3 | profile::Proximity, 4 | InterestLevel, PriorityMap, Profile, Topic, 5 | }; 6 | use keynesis::key::ed25519; 7 | 8 | pub struct Vicinity { 9 | nodes: PriorityMap, 10 | } 11 | 12 | impl Vicinity { 13 | pub fn new(length: usize) -> Self { 14 | Self { 15 | nodes: PriorityMap::new(length), 16 | } 17 | } 18 | } 19 | 20 | impl Layer for Vicinity { 21 | fn name(&self) -> &'static str { 22 | "poldercast::vicinity" 23 | } 24 | 25 | fn view(&mut self, builder: &mut ViewBuilder) { 26 | self.nodes.iter().for_each(|(_, v)| builder.add(v)); 27 | } 28 | 29 | fn remove(&mut self, id: &ed25519::PublicKey) { 30 | self.nodes.remove(id); 31 | } 32 | fn reset(&mut self) { 33 | self.nodes.clear(); 34 | } 35 | 36 | fn populate(&mut self, our_profile: &Profile, new_profile: &Profile) { 37 | let proximity = our_profile.proximity_to(new_profile); 38 | self.nodes.put(proximity, new_profile.id()); 39 | } 40 | 41 | fn subscribe(&mut self, _: Topic) {} 42 | 43 | fn unsubscribe(&mut self, _: &Topic) {} 44 | 45 | fn subscriptions(&self, _output: &mut PriorityMap) {} 46 | } 47 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | #[macro_use(quickcheck)] 3 | extern crate quickcheck_macros; 4 | 5 | mod gossip; 6 | pub mod layer; 7 | mod priority_map; 8 | mod profile; 9 | mod profiles; 10 | mod topic; 11 | mod topology; 12 | 13 | pub use self::{ 14 | gossip::{Gossip, GossipError, GossipSlice}, 15 | priority_map::PriorityMap, 16 | profile::Profile, 17 | profiles::Profiles, 18 | topic::{ 19 | InterestLevel, Subscription, SubscriptionError, SubscriptionIter, SubscriptionSlice, 20 | Subscriptions, SubscriptionsSlice, Topic, 21 | }, 22 | topology::Topology, 23 | }; 24 | -------------------------------------------------------------------------------- /src/priority_map.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | borrow::Borrow, 3 | collections::{btree_map, hash_map::RandomState, BTreeMap, HashMap}, 4 | hash::{BuildHasher, Hash}, 5 | ptr::NonNull, 6 | rc::Rc, 7 | }; 8 | 9 | #[derive(Debug)] 10 | struct Entry { 11 | key: Rc, 12 | value: Rc, 13 | } 14 | 15 | type PriorityGroup = lru::LruCache, NonNull>>; 16 | 17 | pub struct PriorityMap { 18 | by_value: HashMap, Box>, H>, 19 | by_priority: BTreeMap, PriorityGroup>, 20 | cap: usize, 21 | } 22 | 23 | impl Entry { 24 | fn new(key: K, value: V) -> Entry { 25 | Self { 26 | key: Rc::new(key), 27 | value: Rc::new(value), 28 | } 29 | } 30 | } 31 | 32 | impl PriorityMap 33 | where 34 | K: Ord + Clone, 35 | V: Eq + Clone + Hash, 36 | { 37 | pub fn new(cap: usize) -> Self { 38 | PriorityMap::new_with_map(cap, HashMap::with_capacity(cap)) 39 | } 40 | } 41 | 42 | impl PriorityMap 43 | where 44 | K: Ord + Clone, 45 | V: Eq + Clone + Hash, 46 | H: BuildHasher, 47 | { 48 | pub fn new_with(cap: usize, hash_builder: H) -> Self { 49 | Self::new_with_map(cap, HashMap::with_capacity_and_hasher(cap, hash_builder)) 50 | } 51 | 52 | fn new_with_map(cap: usize, by_value: HashMap, Box>, H>) -> Self { 53 | assert!( 54 | cap > 0, 55 | "Cannot do much with a cap set to 0, have at least 1 entry or use a different type" 56 | ); 57 | Self { 58 | cap, 59 | by_value, 60 | by_priority: BTreeMap::new(), 61 | } 62 | } 63 | 64 | pub fn capacity(&self) -> usize { 65 | self.by_value.capacity() 66 | } 67 | 68 | pub fn len(&self) -> usize { 69 | self.by_value.len() 70 | } 71 | 72 | pub fn is_empty(&self) -> bool { 73 | self.by_value.is_empty() 74 | } 75 | 76 | pub fn contains(&self, k: &Q) -> bool 77 | where 78 | Rc: Borrow, 79 | Q: Hash + Eq + ?Sized, 80 | { 81 | self.by_value.contains_key(k) 82 | } 83 | 84 | pub fn remove(&mut self, v: &Q) -> bool 85 | where 86 | Rc: Borrow, 87 | Q: Hash + Eq + ?Sized, 88 | { 89 | if let Some(entry) = self.by_value.remove(v) { 90 | let k = entry.key.clone(); 91 | let v = entry.value.clone(); 92 | 93 | if let btree_map::Entry::Occupied(mut occupied) = self.by_priority.entry(k) { 94 | occupied.get_mut().pop(&v); 95 | 96 | // make sure we don't keep empty priority entries 97 | if occupied.get().is_empty() { 98 | occupied.remove(); 99 | } 100 | } 101 | true 102 | } else { 103 | false 104 | } 105 | } 106 | 107 | pub fn iter(&self) -> impl Iterator { 108 | self.by_priority 109 | .values() 110 | .rev() 111 | .flat_map(|v| v.iter()) 112 | .map(|(_, v)| { 113 | let p = unsafe { v.as_ref() }; 114 | (p.key.borrow(), p.value.borrow()) 115 | }) 116 | } 117 | 118 | pub fn get(&self, k: &Q) -> Option<(&'_ K, &'_ V)> 119 | where 120 | Rc: Borrow, 121 | Q: Hash + Eq + ?Sized, 122 | { 123 | let e = self.by_value.get(k)?; 124 | 125 | Some((&e.key, &e.value)) 126 | } 127 | 128 | pub fn put(&mut self, key: K, value: V) { 129 | // check if we have reached the cap 130 | if self.len() >= self.cap { 131 | // if we do check that we are not adding an entry that is lower bound 132 | // than our current lower priority 133 | // 134 | // if this is the case, return now and don't add the entry 135 | if let Some((lowest, _)) = self.by_priority.iter().next() { 136 | if lowest.as_ref() > &key { 137 | return; 138 | } 139 | } 140 | 141 | while self.len() >= self.cap { 142 | self.pop_lowest(); 143 | } 144 | } 145 | 146 | self.remove(&value); 147 | 148 | let entry = Entry::new(key, value); 149 | let mut entry = Box::new(entry); 150 | let entry_ptr: NonNull> = unsafe { NonNull::new_unchecked(entry.as_mut()) }; 151 | let k = entry.key.clone(); 152 | let v = entry.value.clone(); 153 | 154 | if self.by_value.insert(v.clone(), entry).is_some() { 155 | panic!("the previous entry (if any) should have been removed already"); 156 | } 157 | 158 | self.by_priority 159 | .entry(k) 160 | .or_insert_with(lru::LruCache::unbounded) 161 | .put(v, entry_ptr); 162 | } 163 | 164 | pub fn resize(&mut self, cap: usize) { 165 | // return early if capacity doesn't change 166 | if cap == self.cap { 167 | return; 168 | } 169 | 170 | while self.len() > cap { 171 | self.pop_lowest(); 172 | } 173 | self.by_value.shrink_to_fit(); 174 | 175 | self.cap = cap; 176 | } 177 | 178 | fn lower_bound(&self) -> Option> { 179 | if let Some((k, _)) = self.by_priority.iter().next() { 180 | Some(k.clone()) 181 | } else { 182 | None 183 | } 184 | } 185 | 186 | pub fn pop_lowest(&mut self) -> Option<(Rc, Rc)> { 187 | let k = self.lower_bound()?; 188 | 189 | if let btree_map::Entry::Occupied(mut occupied) = self.by_priority.entry(k) { 190 | let lowest = occupied.get_mut().pop_lru(); 191 | 192 | // make sure we don't keep empty priority entries 193 | if occupied.get().is_empty() { 194 | occupied.remove(); 195 | } 196 | 197 | let (v, _) = lowest?; 198 | let entry = self.by_value.remove(&v)?; 199 | 200 | Some((entry.key.clone(), entry.value.clone())) 201 | } else { 202 | None 203 | } 204 | } 205 | 206 | pub fn clear(&mut self) { 207 | self.by_priority.clear(); 208 | self.by_value.clear(); 209 | } 210 | } 211 | 212 | unsafe impl Send for PriorityMap {} 213 | unsafe impl Sync for PriorityMap {} 214 | 215 | #[cfg(test)] 216 | mod tests { 217 | use super::*; 218 | 219 | #[test] 220 | fn empty() { 221 | let mut map = PriorityMap::new(10); 222 | assert!(map.is_empty()); 223 | 224 | map.put(1, "entry".to_owned()); 225 | assert!(!map.is_empty()); 226 | assert_eq!(map.len(), 1); 227 | } 228 | 229 | #[test] 230 | fn contains() { 231 | let mut map = PriorityMap::::new(10); 232 | map.put(1, "entry".to_owned()); 233 | 234 | assert!(map.contains(&"entry".to_owned())); 235 | } 236 | 237 | #[test] 238 | fn remove() { 239 | let mut map = PriorityMap::::new(10); 240 | 241 | let priority = 1; 242 | let entry1 = "entry1".to_owned(); 243 | let entry2 = "entry2".to_owned(); 244 | 245 | map.put(priority, entry1.clone()); 246 | map.put(priority, entry1.clone()); 247 | 248 | assert!(map.remove(&entry1)); 249 | 250 | map.put(priority, entry1); 251 | map.put(priority, entry2.clone()); 252 | 253 | assert!(map.remove(&entry2)); 254 | } 255 | 256 | #[test] 257 | fn ignoring_lower_than_lower_bound() { 258 | let mut map = PriorityMap::::new(5); 259 | map.put(3, "3".to_owned()); 260 | map.put(2, "2".to_owned()); 261 | map.put(5, "5".to_owned()); 262 | map.put(5, "five".to_owned()); 263 | map.put(6, "6".to_owned()); 264 | map.put(4, "4".to_owned()); 265 | map.put(1, "1".to_owned()); 266 | 267 | let mut iter = map.iter(); 268 | 269 | assert_eq!(iter.next(), Some((&6u32, &"6".to_owned()))); 270 | assert_eq!(iter.next(), Some((&5u32, &"five".to_owned()))); 271 | assert_eq!(iter.next(), Some((&5u32, &"5".to_owned()))); 272 | assert_eq!(iter.next(), Some((&4u32, &"4".to_owned()))); 273 | assert_eq!(iter.next(), Some((&3u32, &"3".to_owned()))); 274 | assert_eq!(iter.next(), None); 275 | } 276 | 277 | #[test] 278 | fn ordering() { 279 | let mut map = PriorityMap::::new(10); 280 | map.put(3, "3".to_owned()); 281 | map.put(1, "1".to_owned()); 282 | map.put(2, "2".to_owned()); 283 | map.put(5, "5".to_owned()); 284 | map.put(5, "five".to_owned()); 285 | map.put(6, "6".to_owned()); 286 | map.put(4, "4".to_owned()); 287 | 288 | let mut iter = map.iter(); 289 | 290 | assert_eq!(iter.next(), Some((&6u32, &"6".to_owned()))); 291 | assert_eq!(iter.next(), Some((&5u32, &"five".to_owned()))); 292 | assert_eq!(iter.next(), Some((&5u32, &"5".to_owned()))); 293 | assert_eq!(iter.next(), Some((&4u32, &"4".to_owned()))); 294 | assert_eq!(iter.next(), Some((&3u32, &"3".to_owned()))); 295 | assert_eq!(iter.next(), Some((&2u32, &"2".to_owned()))); 296 | assert_eq!(iter.next(), Some((&1u32, &"1".to_owned()))); 297 | assert_eq!(iter.next(), None); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/profile.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | topic::{InterestLevel, Subscriptions, Topic}, 3 | Gossip, PriorityMap, Subscription, 4 | }; 5 | use keynesis::{key::ed25519, passport::block::Time}; 6 | use std::net::SocketAddr; 7 | 8 | pub struct Profile { 9 | subscriptions: PriorityMap, 10 | gossip: Gossip, 11 | } 12 | 13 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 14 | pub struct Proximity { 15 | priority: usize, 16 | proximity: usize, 17 | } 18 | 19 | impl Profile { 20 | pub fn new(address: SocketAddr, id: &ed25519::SecretKey) -> Self { 21 | let gossip = Gossip::new(address, id, Subscriptions::new().as_slice()); 22 | 23 | Self { 24 | gossip, 25 | subscriptions: PriorityMap::new(Subscriptions::MAX_NUM_SUBSCRIPTIONS), 26 | } 27 | } 28 | 29 | pub fn from_gossip(gossip: Gossip) -> Self { 30 | let mut subscriptions = PriorityMap::new(Subscriptions::MAX_NUM_SUBSCRIPTIONS); 31 | 32 | for subscription in gossip.subscriptions() { 33 | let interest_level = subscription.interest_level(); 34 | let topic = subscription.topic(); 35 | subscriptions.put(interest_level, topic); 36 | } 37 | 38 | Self { 39 | gossip, 40 | subscriptions, 41 | } 42 | } 43 | 44 | pub(crate) fn clear_subscriptions(&mut self) { 45 | self.subscriptions.clear(); 46 | } 47 | 48 | pub(crate) fn subscriptions_mut(&mut self) -> &mut PriorityMap { 49 | &mut self.subscriptions 50 | } 51 | 52 | pub(crate) fn unsubscribe(&mut self, topic: &Topic) { 53 | self.subscriptions.remove(topic); 54 | } 55 | 56 | pub fn gossip(&self) -> &Gossip { 57 | &self.gossip 58 | } 59 | 60 | pub(crate) fn commit_gossip(&mut self, id: &ed25519::SecretKey) -> &Gossip { 61 | let subscriptions = self.subscriptions(); 62 | 63 | self.gossip = Gossip::new(self.address(), id, subscriptions.as_slice()); 64 | 65 | &self.gossip 66 | } 67 | 68 | pub fn id(&self) -> ed25519::PublicKey { 69 | self.gossip.id() 70 | } 71 | 72 | pub fn last_update(&self) -> Time { 73 | self.gossip.time() 74 | } 75 | 76 | pub fn address(&self) -> SocketAddr { 77 | self.gossip.address() 78 | } 79 | 80 | pub fn subscriptions(&self) -> Subscriptions { 81 | let mut subscriptions = Subscriptions::new(); 82 | for (interest_level, topic) in self.subscriptions.iter() { 83 | let sub = Subscription::new(*topic, *interest_level); 84 | subscriptions 85 | .push(sub.as_slice()) 86 | .expect("We are already limiting the number of internal subscriptions"); 87 | } 88 | subscriptions 89 | } 90 | 91 | pub fn proximity_to(&self, to: &Self) -> Proximity { 92 | let mut priority_score = 0; 93 | let mut proximity_score = 0; 94 | for (interest_level, topic) in self.subscriptions.iter() { 95 | if let Some((to, _)) = to.subscriptions.get(topic) { 96 | proximity_score += 1; 97 | priority_score += interest_level.priority_score(*to); 98 | } 99 | } 100 | Proximity { 101 | proximity: proximity_score, 102 | priority: priority_score, 103 | } 104 | } 105 | } 106 | 107 | impl PartialOrd for Proximity { 108 | fn partial_cmp(&self, other: &Self) -> Option { 109 | Some(self.cmp(other)) 110 | } 111 | } 112 | 113 | impl Ord for Proximity { 114 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 115 | use std::cmp::Ordering::{Equal, Greater, Less}; 116 | if self.priority > other.priority { 117 | Greater 118 | } else if self.priority < other.priority { 119 | Less 120 | } else if self.proximity > other.proximity { 121 | Greater 122 | } else if self.proximity < other.proximity { 123 | Less 124 | } else { 125 | Equal 126 | } 127 | } 128 | } 129 | 130 | impl From for Profile { 131 | fn from(gossip: Gossip) -> Self { 132 | Self::from_gossip(gossip) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/profiles.rs: -------------------------------------------------------------------------------- 1 | use crate::Profile; 2 | use keynesis::key::ed25519; 3 | use lru::LruCache; 4 | use std::sync::Arc; 5 | 6 | pub struct Profiles { 7 | pub(crate) dirty: LruCache>, 8 | pub(crate) pool: LruCache>, 9 | pub(crate) trusted: LruCache>, 10 | } 11 | 12 | impl Profiles { 13 | pub fn new(dirty: usize, pool: usize, trusted: usize) -> Self { 14 | Self { 15 | dirty: LruCache::new(dirty), 16 | pool: LruCache::new(pool), 17 | trusted: LruCache::new(trusted), 18 | } 19 | } 20 | 21 | pub fn dirty(&self) -> &LruCache> { 22 | &self.dirty 23 | } 24 | 25 | pub fn pool(&self) -> &LruCache> { 26 | &self.pool 27 | } 28 | 29 | pub fn trusted(&self) -> &LruCache> { 30 | &self.trusted 31 | } 32 | 33 | pub fn promote(&mut self, entry: &ed25519::PublicKey) { 34 | if let Some(profile) = self.pool.pop(entry) { 35 | // if there is an overflow coming up, instead of losing 36 | // the entries we would rotate from the trusted LRU 37 | // we demote the least used to the lower pool 38 | while self.trusted.len() >= self.trusted.cap() { 39 | if let Some((id, profile)) = self.trusted.pop_lru() { 40 | self.pool.put(id, profile); 41 | } else { 42 | unreachable!("cap should be greater than 0") 43 | } 44 | } 45 | 46 | self.trusted.put(*entry, profile); 47 | } 48 | 49 | if let Some(profile) = self.dirty.pop(entry) { 50 | self.pool.put(*entry, profile); 51 | } 52 | } 53 | 54 | pub fn demote(&mut self, entry: &ed25519::PublicKey) { 55 | if let Some(profile) = self.pool.pop(entry) { 56 | self.dirty.put(*entry, profile); 57 | } else if let Some(profile) = self.trusted.pop(entry) { 58 | self.pool.put(*entry, profile); 59 | } 60 | } 61 | 62 | pub fn put(&mut self, id: ed25519::PublicKey, profile: Arc) -> bool { 63 | if let Some(entry) = self.dirty.peek(&id).cloned() { 64 | if entry.last_update() < profile.last_update() { 65 | self.dirty.put(id, profile); 66 | } 67 | false 68 | } else if let Some(entry) = self.trusted.peek(&id).cloned() { 69 | if entry.last_update() < profile.last_update() { 70 | self.trusted.put(id, profile); 71 | true 72 | } else { 73 | false 74 | } 75 | } else if let Some(entry) = self.pool.peek(&id).cloned() { 76 | if entry.last_update() < profile.last_update() { 77 | self.pool.put(id, profile); 78 | true 79 | } else { 80 | false 81 | } 82 | } else { 83 | self.pool.put(id, profile); 84 | true 85 | } 86 | } 87 | 88 | pub fn get(&mut self, id: &ed25519::PublicKey) -> Option<&Arc> { 89 | if let Some(profile) = self.trusted.get(id) { 90 | Some(profile) 91 | } else if let Some(profile) = self.pool.get(id) { 92 | Some(profile) 93 | } else if let Some(profile) = self.dirty.get(id) { 94 | Some(profile) 95 | } else { 96 | None 97 | } 98 | } 99 | } 100 | 101 | impl Default for Profiles { 102 | fn default() -> Self { 103 | Self::new(512, 256, 128) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/topic.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | convert::{TryFrom, TryInto as _}, 3 | fmt::{self, Formatter}, 4 | iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator}, 5 | str::FromStr, 6 | }; 7 | use thiserror::Error; 8 | 9 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 10 | pub struct Topic([u8; Self::SIZE]); 11 | 12 | /// This is the interest associated to a topic 13 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 14 | pub struct InterestLevel(u8); 15 | 16 | #[derive(Clone, Copy)] 17 | pub struct SubscriptionSlice<'a>(&'a [u8]); 18 | 19 | #[derive(Clone, Copy)] 20 | pub struct Subscription([u8; Self::SIZE]); 21 | 22 | #[derive(Clone)] 23 | pub struct Subscriptions(Vec); 24 | 25 | #[derive(Clone, Copy)] 26 | pub struct SubscriptionsSlice<'a>(&'a [u8]); 27 | 28 | pub struct SubscriptionIter<'a>(SubscriptionsSlice<'a>); 29 | 30 | #[derive(Debug, Error)] 31 | pub enum SubscriptionError { 32 | #[error("Invalid, length of a subscription, expected {}", Subscription::SIZE)] 33 | InvalidSize, 34 | 35 | #[error("Invalid subscription ({index})")] 36 | InvalidSubscriptionAt { index: usize }, 37 | 38 | #[error( 39 | "Cannot have more than {} Subscriptions", 40 | Subscriptions::MAX_NUM_SUBSCRIPTIONS 41 | )] 42 | MaxSubscriptionReached, 43 | } 44 | 45 | impl Topic { 46 | pub const SIZE: usize = 32; 47 | 48 | pub const fn new(topic: [u8; Self::SIZE]) -> Self { 49 | Self(topic) 50 | } 51 | } 52 | 53 | impl InterestLevel { 54 | pub const SIZE: usize = 1; 55 | 56 | pub const ZERO: Self = Self::new(0); 57 | 58 | pub const fn new(level: u8) -> Self { 59 | Self(level) 60 | } 61 | 62 | pub fn priority_score(self, other: Self) -> usize { 63 | if self < other { 64 | self.0 as usize 65 | } else { 66 | self.0 as usize + other.0 as usize 67 | } 68 | } 69 | 70 | #[inline(always)] 71 | pub fn no_interest(self) -> bool { 72 | self == Self::ZERO 73 | } 74 | } 75 | 76 | impl Subscription { 77 | pub const SIZE: usize = Topic::SIZE + InterestLevel::SIZE; 78 | 79 | pub fn new(topic: Topic, interest_level: InterestLevel) -> Self { 80 | let mut sub = [0; Self::SIZE]; 81 | 82 | sub[..Topic::SIZE].copy_from_slice(topic.as_ref()); 83 | sub[Topic::SIZE] = interest_level.0; 84 | 85 | Self(sub) 86 | } 87 | 88 | pub fn as_slice(&self) -> SubscriptionSlice<'_> { 89 | SubscriptionSlice::from_slice_unchecked(self.0.as_ref()) 90 | } 91 | 92 | pub fn topic(&self) -> Topic { 93 | self.as_slice().topic() 94 | } 95 | 96 | pub fn interest_level(&self) -> InterestLevel { 97 | self.as_slice().interest_level() 98 | } 99 | } 100 | 101 | impl<'a> SubscriptionSlice<'a> { 102 | pub fn to_owned(self) -> Subscription { 103 | Subscription(self.0.try_into().expect("Valid Subscription slice")) 104 | } 105 | 106 | pub fn try_from_slice(slice: &'a [u8]) -> Result { 107 | if slice.len() != Subscription::SIZE { 108 | return Err(SubscriptionError::InvalidSize); 109 | } 110 | 111 | Ok(Self::from_slice_unchecked(slice)) 112 | } 113 | 114 | pub fn from_slice_unchecked(slice: &'a [u8]) -> Self { 115 | debug_assert_eq!(slice.len(), Subscription::SIZE); 116 | Self(slice) 117 | } 118 | 119 | pub fn topic(self) -> Topic { 120 | Topic( 121 | self.0[..Topic::SIZE] 122 | .as_ref() 123 | .try_into() 124 | .expect("32 bytes of Topic identifier"), 125 | ) 126 | } 127 | 128 | pub fn interest_level(self) -> InterestLevel { 129 | InterestLevel(self.0[Topic::SIZE]) 130 | } 131 | } 132 | 133 | impl Subscriptions { 134 | pub const MAX_NUM_SUBSCRIPTIONS: usize = 0b0000_0011_1111_1111; // 1023 135 | 136 | pub fn new() -> Self { 137 | Self(Vec::with_capacity( 138 | Self::MAX_NUM_SUBSCRIPTIONS * Subscription::SIZE, 139 | )) 140 | } 141 | 142 | pub fn push(&mut self, sub: SubscriptionSlice<'_>) -> Result<(), SubscriptionError> { 143 | if self.as_slice().number_subscriptions() >= Self::MAX_NUM_SUBSCRIPTIONS { 144 | return Err(SubscriptionError::MaxSubscriptionReached); 145 | } 146 | 147 | self.0.extend_from_slice(sub.as_ref()); 148 | 149 | Ok(()) 150 | } 151 | 152 | pub fn as_slice(&self) -> SubscriptionsSlice<'_> { 153 | SubscriptionsSlice(self.0.as_ref()) 154 | } 155 | 156 | pub fn iter(&self) -> SubscriptionIter<'_> { 157 | self.as_slice().iter() 158 | } 159 | } 160 | 161 | impl<'a> SubscriptionsSlice<'a> { 162 | pub fn to_owned(self) -> Subscriptions { 163 | Subscriptions(self.0.to_owned()) 164 | } 165 | 166 | pub fn try_from_slice(slice: &'a [u8]) -> Result { 167 | if slice.len() % Subscription::SIZE != 0 { 168 | return Err(SubscriptionError::InvalidSize); 169 | } 170 | 171 | let slice = Self::from_slice_unchecked(slice); 172 | 173 | if slice.number_subscriptions() > Subscriptions::MAX_NUM_SUBSCRIPTIONS { 174 | return Err(SubscriptionError::MaxSubscriptionReached); 175 | } 176 | 177 | for (index, entry) in slice.iter().enumerate() { 178 | let slice = entry.0; 179 | let _ = SubscriptionSlice::try_from_slice(slice) 180 | .map_err(|_| SubscriptionError::InvalidSubscriptionAt { index })?; 181 | } 182 | 183 | Ok(slice) 184 | } 185 | 186 | pub fn from_slice_unchecked(slice: &'a [u8]) -> Self { 187 | debug_assert_eq!(slice.len() % Subscription::SIZE, 0); 188 | Self(slice) 189 | } 190 | 191 | fn subscription_offset(self, index: usize) -> usize { 192 | index * Subscription::SIZE 193 | } 194 | 195 | pub fn number_subscriptions(self) -> usize { 196 | self.0.len() / Subscription::SIZE 197 | } 198 | 199 | pub fn iter(self) -> SubscriptionIter<'a> { 200 | SubscriptionIter(self) 201 | } 202 | 203 | pub fn pop_front(&mut self) -> Option> { 204 | let obj = self.get(0)?; 205 | 206 | self.0 = &self.0[Subscription::SIZE..]; 207 | 208 | Some(obj) 209 | } 210 | 211 | pub fn pop_back(&mut self) -> Option> { 212 | let index = self.number_subscriptions(); 213 | let sub = self.get(index)?; 214 | 215 | self.0 = &self.0[..index]; 216 | 217 | Some(sub) 218 | } 219 | 220 | pub fn get(self, index: usize) -> Option> { 221 | let len = self.number_subscriptions(); 222 | if len == 0 || len < index { 223 | None 224 | } else { 225 | let index = self.subscription_offset(index); 226 | 227 | Some(SubscriptionSlice::from_slice_unchecked( 228 | &self.0[index..index + Subscription::SIZE], 229 | )) 230 | } 231 | } 232 | } 233 | 234 | /* Default ***************************************************************** */ 235 | 236 | impl Default for Subscriptions { 237 | fn default() -> Self { 238 | Self::new() 239 | } 240 | } 241 | 242 | /* Convert ***************************************************************** */ 243 | 244 | impl<'a> TryFrom<&'a [u8]> for Topic { 245 | type Error = std::array::TryFromSliceError; 246 | fn try_from(value: &[u8]) -> Result { 247 | let bytes = value.try_into()?; 248 | Ok(Topic::new(bytes)) 249 | } 250 | } 251 | 252 | impl FromStr for Topic { 253 | type Err = hex::FromHexError; 254 | fn from_str(s: &str) -> Result { 255 | let mut topic = [0; Topic::SIZE]; 256 | hex::decode_to_slice(s, &mut topic)?; 257 | Ok(Self(topic)) 258 | } 259 | } 260 | 261 | /* AsRef ******************************************************************* */ 262 | 263 | impl<'a> AsRef<[u8]> for SubscriptionSlice<'a> { 264 | fn as_ref(&self) -> &[u8] { 265 | self.0 266 | } 267 | } 268 | 269 | impl<'a> AsRef<[u8]> for SubscriptionsSlice<'a> { 270 | fn as_ref(&self) -> &[u8] { 271 | self.0 272 | } 273 | } 274 | 275 | impl AsRef<[u8]> for Subscription { 276 | fn as_ref(&self) -> &[u8] { 277 | self.0.as_ref() 278 | } 279 | } 280 | 281 | impl AsRef<[u8]> for Topic { 282 | fn as_ref(&self) -> &[u8] { 283 | self.0.as_ref() 284 | } 285 | } 286 | 287 | /* Formatter *************************************************************** */ 288 | 289 | impl fmt::Display for Topic { 290 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 291 | hex::encode(self.as_ref()).fmt(f) 292 | } 293 | } 294 | 295 | impl fmt::Debug for Topic { 296 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 297 | f.debug_tuple("Topic") 298 | .field(&hex::encode(self.as_ref())) 299 | .finish() 300 | } 301 | } 302 | 303 | impl<'a> fmt::Debug for SubscriptionSlice<'a> { 304 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 305 | f.debug_struct("Subscription") 306 | .field("topic", &self.topic()) 307 | .field("interest", &self.interest_level()) 308 | .finish() 309 | } 310 | } 311 | 312 | impl fmt::Debug for Subscription { 313 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 314 | self.as_slice().fmt(f) 315 | } 316 | } 317 | 318 | impl fmt::Debug for Subscriptions { 319 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 320 | self.as_slice().fmt(f) 321 | } 322 | } 323 | 324 | impl<'a> fmt::Debug for SubscriptionsSlice<'a> { 325 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 326 | f.debug_list().entries(self.iter()).finish() 327 | } 328 | } 329 | 330 | /* Iterator **************************************************************** */ 331 | 332 | impl<'a> Iterator for SubscriptionIter<'a> { 333 | type Item = SubscriptionSlice<'a>; 334 | 335 | fn next(&mut self) -> Option { 336 | self.0.pop_front() 337 | } 338 | 339 | fn size_hint(&self) -> (usize, Option) { 340 | let r = self.0.number_subscriptions(); 341 | (r, Some(r)) 342 | } 343 | 344 | fn count(self) -> usize 345 | where 346 | Self: Sized, 347 | { 348 | self.0.number_subscriptions() 349 | } 350 | 351 | fn last(mut self) -> Option 352 | where 353 | Self: Sized, 354 | { 355 | self.0.pop_back() 356 | } 357 | 358 | fn nth(&mut self, n: usize) -> Option { 359 | let index = self.0.subscription_offset(n); 360 | let sub = self.0.get(n)?; 361 | 362 | (self.0).0 = &(self.0).0[index..]; 363 | 364 | Some(sub) 365 | } 366 | } 367 | impl<'a> DoubleEndedIterator for SubscriptionIter<'a> { 368 | fn next_back(&mut self) -> Option { 369 | self.0.pop_back() 370 | } 371 | } 372 | impl<'a> ExactSizeIterator for SubscriptionIter<'a> { 373 | fn len(&self) -> usize { 374 | self.0.number_subscriptions() 375 | } 376 | } 377 | impl<'a> FusedIterator for SubscriptionIter<'a> {} 378 | impl<'a> IntoIterator for SubscriptionsSlice<'a> { 379 | type IntoIter = SubscriptionIter<'a>; 380 | type Item = SubscriptionSlice<'a>; 381 | 382 | fn into_iter(self) -> Self::IntoIter { 383 | self.iter() 384 | } 385 | } 386 | 387 | #[cfg(test)] 388 | mod tests { 389 | use super::*; 390 | use quickcheck::{Arbitrary, Gen}; 391 | 392 | impl Arbitrary for Topic { 393 | fn arbitrary(g: &mut Gen) -> Self { 394 | let mut topic = Topic::new([0; Self::SIZE]); 395 | topic.0.iter_mut().for_each(|byte| { 396 | *byte = u8::arbitrary(g); 397 | }); 398 | topic 399 | } 400 | } 401 | 402 | impl Arbitrary for InterestLevel { 403 | fn arbitrary(g: &mut Gen) -> Self { 404 | InterestLevel::new(u8::arbitrary(g)) 405 | } 406 | } 407 | 408 | impl Arbitrary for Subscription { 409 | fn arbitrary(g: &mut Gen) -> Self { 410 | Self::new(Topic::arbitrary(g), InterestLevel::arbitrary(g)) 411 | } 412 | } 413 | 414 | impl Arbitrary for Subscriptions { 415 | fn arbitrary(g: &mut Gen) -> Self { 416 | let mut subs = Self::new(); 417 | let count = usize::arbitrary(g) % Subscriptions::MAX_NUM_SUBSCRIPTIONS; 418 | 419 | for _ in 0..count { 420 | subs.push(Subscription::arbitrary(g).as_slice()) 421 | .expect("There should be enough space to add all the needed subscriptions"); 422 | } 423 | 424 | subs 425 | } 426 | } 427 | 428 | /// make sure we are reaching an error if we are creating a subscriptions with too 429 | /// many entries 430 | #[test] 431 | fn subscriptions_push_max() { 432 | let mut subs = Subscriptions::new(); 433 | let mut g = quickcheck::Gen::new(1024); 434 | let g = &mut g; 435 | 436 | let count = Subscriptions::MAX_NUM_SUBSCRIPTIONS; 437 | for _ in 0..count { 438 | subs.push(Subscription::arbitrary(g).as_slice()) 439 | .expect("There should be enough space to add all the needed subscriptions"); 440 | } 441 | 442 | subs.push(Subscription::arbitrary(g).as_slice()) 443 | .expect_err("Should have failed as reaching the limits"); 444 | } 445 | 446 | /// make sure we are reaching an error if we are creating a subscriptions with too 447 | /// many entries 448 | #[test] 449 | fn subscriptions_decode_max() { 450 | let mut subs = vec![0; Subscription::SIZE * (Subscriptions::MAX_NUM_SUBSCRIPTIONS + 1)]; 451 | let mut g = quickcheck::Gen::new(1024); 452 | let g = &mut g; 453 | 454 | let count = Subscriptions::MAX_NUM_SUBSCRIPTIONS + 1; 455 | for _ in 0..count { 456 | subs.extend_from_slice(Subscription::arbitrary(g).as_ref()); 457 | } 458 | 459 | SubscriptionsSlice::try_from_slice(subs.as_slice()) 460 | .expect_err("Should have a max size reached error"); 461 | } 462 | 463 | #[test] 464 | fn topic_from_str() { 465 | let topic = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; 466 | 467 | let _topic = Topic::from_str(topic).unwrap(); 468 | } 469 | 470 | #[test] 471 | fn topic_to_string() { 472 | let topic = [ 473 | 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 1, 35, 69, 103, 474 | 137, 171, 205, 239, 1, 35, 69, 103, 137, 171, 205, 239, 475 | ]; 476 | let topic = Topic::new(topic).to_string(); 477 | 478 | debug_assert_eq!( 479 | "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", 480 | topic, 481 | ) 482 | } 483 | 484 | #[quickcheck] 485 | fn parse_valid_subscription(sub: Subscription) -> bool { 486 | let slice = sub.as_slice(); 487 | let _ = SubscriptionSlice::try_from_slice(slice.as_ref()).unwrap(); 488 | true 489 | } 490 | 491 | #[quickcheck] 492 | fn parse_valid_subscriptions(subs: Subscriptions) -> bool { 493 | let slice = subs.as_slice(); 494 | let _ = SubscriptionsSlice::try_from_slice(slice.as_ref()).unwrap(); 495 | true 496 | } 497 | 498 | #[quickcheck] 499 | fn to_string_from_str(topic: Topic) -> bool { 500 | let s = topic.to_string(); 501 | let t: Topic = s.parse().unwrap(); 502 | 503 | topic == t 504 | } 505 | } 506 | -------------------------------------------------------------------------------- /src/topology.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | layer::{self, Layer, LayerBuilder, Selection, ViewBuilder}, 3 | Gossip, Profile, Profiles, Topic, 4 | }; 5 | use keynesis::key::ed25519; 6 | use std::{net::SocketAddr, sync::Arc}; 7 | 8 | pub struct Topology { 9 | view_layers: Vec>, 10 | gossip_layers: Vec>, 11 | profile: Profile, 12 | profiles: Profiles, 13 | } 14 | 15 | struct DefaultBuilder; 16 | 17 | impl LayerBuilder for DefaultBuilder { 18 | fn build_for_view(&self) -> Vec> { 19 | vec![ 20 | Box::new(layer::Rings::new(4)), 21 | Box::new(layer::Vicinity::new(20)), 22 | Box::new(layer::Cyclon::new(20)), 23 | ] 24 | } 25 | 26 | fn build_for_gossip(&self) -> Vec> { 27 | vec![ 28 | Box::new(layer::Rings::new(10)), 29 | Box::new(layer::Vicinity::new(10)), 30 | Box::new(layer::Cyclon::new(10)), 31 | ] 32 | } 33 | } 34 | 35 | impl Topology { 36 | /// create a Topology for the given profile 37 | pub fn new(address: SocketAddr, id: &ed25519::SecretKey) -> Self { 38 | Self::new_with(address, id, DefaultBuilder) 39 | } 40 | 41 | pub fn new_with(address: SocketAddr, id: &ed25519::SecretKey, builder: LB) -> Self 42 | where 43 | LB: LayerBuilder, 44 | { 45 | let profile = Profile::new(address, id); 46 | Self { 47 | view_layers: builder.build_for_view(), 48 | gossip_layers: builder.build_for_gossip(), 49 | 50 | profile, 51 | profiles: Profiles::new(512, 256, 128), 52 | } 53 | } 54 | 55 | pub fn update_profile_subscriptions(&mut self, id: &ed25519::SecretKey) { 56 | self.profile.clear_subscriptions(); 57 | for layer in self.view_layers.iter_mut() { 58 | layer.subscriptions(self.profile.subscriptions_mut()); 59 | } 60 | 61 | self.profile.commit_gossip(id); 62 | } 63 | 64 | /// subscribe to the given topic 65 | /// 66 | /// this function also update our profile 67 | pub fn subscribe_topic(&mut self, topic: Topic) { 68 | for layer in self.view_layers.iter_mut() { 69 | layer.subscribe(topic); 70 | } 71 | } 72 | 73 | /// unsubscribe to the given topic 74 | /// 75 | /// this function also update our profile 76 | pub fn unsubscribe_topic(&mut self, topic: &Topic) { 77 | for layer in self.view_layers.iter_mut() { 78 | layer.unsubscribe(topic); 79 | } 80 | 81 | self.profile.unsubscribe(topic); 82 | } 83 | 84 | /// call this function if you could not establish an handshake from this 85 | /// peer. This will prevent to use it in the next profile update. 86 | /// 87 | /// The node will be removed from our layers, but it will not be 88 | /// entirely from our profile pool. We may share it to other nodes 89 | /// we may find it relevant 90 | pub fn remove_peer(&mut self, id: &ed25519::PublicKey) { 91 | for layer in self.view_layers.iter_mut() { 92 | layer.remove(id); 93 | } 94 | 95 | self.profiles.demote(id); 96 | } 97 | 98 | /// call this function to validate you were able to connect with the given 99 | /// peer. This will help the system make sure this entry is kept and reuse 100 | /// 101 | /// Call this function every time you successfully establish an handshake 102 | pub fn promote_peer(&mut self, id: &ed25519::PublicKey) { 103 | self.profiles.promote(id) 104 | } 105 | 106 | /// add a Peer to the Topology 107 | /// 108 | /// the peer will be considered automatically for all our layers. 109 | /// If the peer seem appropriate it will be added to the view and we will 110 | /// attempt to connect to it later. 111 | /// 112 | /// However, if the peer was already demoted some times (i.e. the peer was already 113 | /// known and we already know we cannot connect to it for now, it will be required 114 | /// to be "forgotten" or to be "promoted" in order to move away from the naughty 115 | /// list). 116 | pub fn add_peer(&mut self, peer: Profile) -> bool { 117 | let id = peer.id(); 118 | 119 | let peer = Arc::new(peer); 120 | 121 | if !self.profiles.put(id, Arc::clone(&peer)) { 122 | return false; 123 | } 124 | 125 | for layer in self.view_layers.iter_mut() { 126 | layer.populate(&self.profile, &peer); 127 | } 128 | 129 | true 130 | } 131 | 132 | pub fn gossips_for(&mut self, recipient: &ed25519::PublicKey) -> Vec { 133 | let mut gossips = Vec::with_capacity(1024); 134 | 135 | let recipient = if let Some(recipient) = self.profiles.get(recipient) { 136 | Arc::clone(recipient) 137 | } else { 138 | return gossips; 139 | }; 140 | 141 | let id = recipient.id(); 142 | 143 | for layer in self.gossip_layers.iter_mut() { 144 | layer.reset(); 145 | } 146 | 147 | for subscription in recipient.subscriptions().iter() { 148 | for layer in self.gossip_layers.iter_mut() { 149 | layer.subscribe(subscription.topic()); 150 | } 151 | } 152 | 153 | for profile in self.view(None, Selection::Any) { 154 | for layer in self.gossip_layers.iter_mut() { 155 | layer.populate(recipient.as_ref(), &profile); 156 | } 157 | } 158 | 159 | let mut builder = ViewBuilder::new(Selection::Any); 160 | for layer in self.gossip_layers.iter_mut() { 161 | layer.view(&mut builder); 162 | } 163 | let mut keys = builder.build(); 164 | 165 | keys.remove(&id); // remove the recipient's ID 166 | 167 | for key in keys { 168 | if let Some(profile) = self.profiles.get(&key) { 169 | gossips.push(profile.gossip().clone()); 170 | } else { 171 | // we populated the gossip's view with the profiles' nodes 172 | // so we should have all the entries that have been selected 173 | // in the view. 174 | unreachable!() 175 | } 176 | } 177 | 178 | gossips.push(self.profile.gossip().clone()); 179 | 180 | gossips 181 | } 182 | 183 | pub fn view( 184 | &mut self, 185 | from: Option<&ed25519::PublicKey>, 186 | selection: Selection, 187 | ) -> Vec> { 188 | let mut builder = ViewBuilder::new(selection); 189 | if let Some(origin) = from { 190 | builder.with_origin(*origin); 191 | } 192 | 193 | for layer in self.view_layers.iter_mut() { 194 | layer.view(&mut builder); 195 | } 196 | 197 | let keys = builder.build(); 198 | 199 | let mut profiles = Vec::with_capacity(keys.len()); 200 | 201 | for key in keys { 202 | if let Some(profile) = self.profiles.get(&key) { 203 | profiles.push(Arc::clone(profile)); 204 | } 205 | } 206 | 207 | profiles 208 | } 209 | 210 | pub fn get(&mut self, id: &ed25519::PublicKey) -> Option<&Arc> { 211 | self.profiles.get(id) 212 | } 213 | 214 | pub fn peers(&self) -> &Profiles { 215 | &self.profiles 216 | } 217 | 218 | pub fn self_profile(&self) -> &Profile { 219 | &self.profile 220 | } 221 | } 222 | --------------------------------------------------------------------------------