diff --git a/Cargo.lock b/Cargo.lock index 4dd57f8..2482002 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,6 +65,50 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if", + "lazy_static", +] + [[package]] name = "crypto-bigint" version = "0.2.11" @@ -104,6 +148,12 @@ dependencies = [ "generic-array 0.14.4", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "fake-simd" version = "0.1.2" @@ -151,12 +201,22 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "ironforce" version = "0.1.0" dependencies = [ "rand", "rand_os", + "rayon", "rsa", "serde", "sha2", @@ -183,6 +243,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg 1.0.1", +] + [[package]] name = "num-bigint-dig" version = "0.7.0" @@ -233,6 +302,16 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "opaque-debug" version = "0.2.3" @@ -355,6 +434,31 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg 1.0.1", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "rsa" version = "0.5.0" @@ -376,6 +480,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.130" diff --git a/Cargo.toml b/Cargo.toml index 960c91b..85e2385 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [features] default = [] -std = [] +std = ["rayon"] [dependencies] rand_os = "*" @@ -17,6 +17,7 @@ sha2 = "0.8.1" rand = "*" rsa = { version = "0.5", features = ["serde"] } serde = { version = "1.0", features = ["derive", "alloc"], default-features = false } +rayon = { version = "1.5.1", optional = true } [profile.dev.package.num-bigint-dig] opt-level = 3 diff --git a/src/crypto.rs b/src/crypto.rs index 030f880..c6bda43 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,7 +1,6 @@ /// This module has wrappers for cryptography with RSA algorithms. /// Its main structs - `PublicKey` and `Keys` implement all functions for key generation, signatures and asymmetric encryption use alloc::vec::Vec; -use alloc::vec; use serde::{Deserialize, Serialize}; use rsa::{RsaPublicKey, RsaPrivateKey, PaddingScheme, PublicKey as RPK}; use rsa::errors::Result as RsaRes; diff --git a/src/interface.rs b/src/interface.rs index e1bee76..939d5d1 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -1 +1,41 @@ -pub trait Interface {} +use alloc::string::String; +use crate::message::MessageBytes; +use crate::res::IFResult; + + +/// Some data that can be provided to the interface to send the message to a target. +/// +/// For IP this might be `IP:port`. +/// Radio interface, for example, may not have the functionality of targeting, but that's fine +pub(crate) type TargetingData = String; + +/// In an std environment we require that the interface can be send safely between threads +#[cfg(not(std))] +pub trait InterfaceRequirements {} +#[cfg(std)] +pub trait InterfaceRequirements = Send + Sync; + + +/// An interface that can be used to +pub trait Interface: InterfaceRequirements { + /// Run one main loop iteration. + /// On platforms that support concurrency, these functions will be run simultaneously for all interfaces. + /// Most likely, this function will accept messages and save them somewhere internally to give out later in `Interface.receive()`. + /// + /// For systems that don't support concurrency, there can be only one interface in this function waits for a message (to avoid blocking). + /// That's why it's necessary to check if it is the case for this interface, and it's done using function `Interface::has_blocking_main()` + fn main_loop_iteration(&mut self) -> IFResult<()>; + /// Check if `main_loop_iteration` stops execution and waits for a message + fn has_blocking_main(&self) -> bool { + false // hopefully... + } + /// Get some way of identification for this interface + fn id(&self) -> &str; + /// Send a message. If no `interface_data` is provided, we should consider it to be a broadcast. + /// If, on the other hand, `interface_data` is not `None`, it should be used to send the message to the target. + fn send(&mut self, message: &[u8] /*MessageBytes*/, interface_data: Option) -> IFResult<()>; + /// Receive a message through this interface. Returns a result with an option of (message bytes, target). + /// `None` means there is no message available at the time. + /// The implementations of this function shouldn't wait for new messages, but + fn receive(&mut self) -> IFResult>; +} diff --git a/src/interfaces/mod.rs b/src/interfaces/mod.rs new file mode 100644 index 0000000..9a66dd6 --- /dev/null +++ b/src/interfaces/mod.rs @@ -0,0 +1,6 @@ +use crate::interface::Interface; +use alloc::vec; + +pub fn get_interfaces() -> alloc::vec::Vec> { + vec![] +} diff --git a/src/lib.rs b/src/lib.rs index ff8301e..e9be0d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,11 +10,9 @@ mod ironforce; mod message; mod transport; mod interface; +mod interfaces; +mod res; #[cfg(test)] mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } } diff --git a/src/message.rs b/src/message.rs index e69de29..ffd4ef2 100644 --- a/src/message.rs +++ b/src/message.rs @@ -0,0 +1,5 @@ +use alloc::vec::Vec; + + +/// A serialized message +pub(crate) type MessageBytes = Vec; diff --git a/src/res.rs b/src/res.rs new file mode 100644 index 0000000..60c591e --- /dev/null +++ b/src/res.rs @@ -0,0 +1,19 @@ +use alloc::format; +use alloc::string::String; +use core::fmt::Debug; + +/// An enum for all errors that may occur +pub enum IFError { + /// An error that was created in some dependency and then converted to `IFError` + General(String), +} + + +impl From for IFError { + /// Convert from other error + fn from(e: T) -> Self { + Self::General(format!("{:?}", e)) + } +} + +pub type IFResult = Result; diff --git a/src/transport.rs b/src/transport.rs index 8b13789..3ca46cd 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -1 +1,95 @@ +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use crate::interface::{Interface, TargetingData}; +use crate::message::MessageBytes; +use crate::res::IFResult; +#[cfg(std)] +use rayon::prelude::*; + + +/// An identification of a peer - something that we can use to send a message to id +#[derive(Clone, Debug)] +pub struct PeerInfo { + /// Something to locally identify this peer + pub peer_id: u64, + /// ID of the interface through which we communicate with this peer + pub interface_id: String, + /// Data that should be passed to the interface to send a message to this peer + pub interface_targeting_data: TargetingData, +} + +impl PeerInfo { + fn new(interface_id: String, interface_targeting_data: TargetingData) -> Self { + Self { + peer_id: rand::random(), + interface_id, + interface_targeting_data, + } + } +} + + +/// The struct that manages all the communication with peers +pub struct Transport { + pub interfaces: Vec>, + peers: Vec, +} + +impl Transport { + /// Find a peer in `self.peers` by its id + fn get_peer_by_id(&self, peer_id: u64) -> Option { + self.peers.iter().find(|peer| peer.peer_id == peer_id).cloned() + } + + /// Try to find a peer in `self.peers` by interface_id and targeting data + fn get_peer_by_parameters(&self, interface_id: &str, data: &str /*&TargetingData*/) -> Option<&PeerInfo> { + self.peers.iter().find(|peer| peer.interface_id == interface_id && peer.interface_targeting_data == data) + } + + /// Insert a new peer into out database and return its id or find an existing one with these parameters + fn find_or_add_peer(&mut self, interface_id: String, data: TargetingData) -> u64 { + match self.get_peer_by_parameters(interface_id.as_str(), &data) { + None => { + let new_peer = PeerInfo::new(interface_id, data); + let peer_id = new_peer.peer_id; + self.peers.push(new_peer); + peer_id + } + Some(peer) => peer.peer_id + } + } + + /// Get interface index by its ID + fn interface_index_by_id(&self, interface_id: &str) -> usize { + self.interfaces.iter().position(|interface| interface.id() == interface_id).unwrap_or_else(|| panic!("Invalid interface id")) + } + + /// Send message bytes to a peer if data is provided or broadcast the data if `peer == None` + pub fn send_message(&mut self, message: MessageBytes, peer_id: Option) -> IFResult<()> { + let peer = if let Some(peer_id) = peer_id { self.get_peer_by_id(peer_id) } else { None }; + match peer { + // Broadcast + None => { + #[cfg(not(std))] + { + for interface in &mut self.interfaces { + interface.send(&message, None)?; + } + } + // If we have concurrency, we will do it concurrently + #[cfg(std)] + { + self.interfaces.par_iter_mut().map(|interface| interface.send(&message, None)).for_each(drop); + } + Ok(()) + } + // Singlecast + Some(peer) => { + let interface_ind = self.interface_index_by_id(peer.interface_id.as_str()); + self.interfaces[interface_ind].send(&message, Some(peer.interface_targeting_data)) + } + } + } +}