Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the basis of communities. #41

Merged
merged 5 commits into from
Jun 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ serde = { version = "1.0", features = ["derive"] }
bincode = "1.1.3"
rust_sodium = "0.10.2"
openssl = { version = "0.10", features = ["vendored"] }
lazy_static = "1.2.0"
log = "0.4"

[dev-dependencies]
criterion = "0.2.11"
Expand Down
7 changes: 4 additions & 3 deletions hooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ SUCCESS="${GREEN}ok${RC}"

# Check if rustfmt is installed
printf "${PREFIX} Checking for rustfmt ... "
command -v cargo fmt &>/dev/null
if [[ $? == 0 ]]; then
OUT=$(cargo +stable fmt --version 2>&1 > /dev/null ) # Save the output of the command to OUT for later printing
if [[ $? -eq 0 ]]; then
printf "${SUCCESS}\n"
else
printf "${FAILURE}\n"
printf "${OUT}\n"
exit 1
fi

# Check rustfmt against the git tree
printf "${PREFIX} Checking formatting ... "
command cargo fmt -- --check > /dev/null
command cargo +stable fmt -- --check > /dev/null
if [[ $? == 0 ]]; then
printf "${SUCCESS}\n"
exit 0
Expand Down
209 changes: 209 additions & 0 deletions src/community/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
use crate::serialization::{Packet, PacketDeserializer};
use crate::serialization::header::Header;
use std::error::Error;
use std::collections::HashMap;
use crate::networking::address::Address;
use std::fmt;

pub mod peer;

create_error!(
HeaderUnwrapError,
"The community experienced an error trying to deserialize the header of a packet"
);
create_error!(MidError, "Failed to get the mid");
create_error!(
UnknownCommunityError,
"No community with matching mid found"
);
create_error!(InsertionError, "Error inserting community into hashmap");

/// Example Community
///
/// _**Note:** Try to avoid the use of .unwrap() in actual production code, this is just an example_
/// ```
/// use ipv8::community::peer::Peer;
/// use ipv8::community::Community;
/// use ipv8::serialization::header::Header;
/// use ipv8::serialization::{PacketDeserializer, Packet};
/// use std::net::Ipv4Addr;
/// use ipv8::networking::address::Address;
/// use std::error::Error;
/// use ipv8::IPv8;
/// use ipv8::configuration::Config;
/// use ipv8::serialization::header::HeaderVersion::PyIPV8Header;
/// use ipv8::crypto::keytypes::PublicKey;
///
/// pub struct TestCommunity{
/// peer: Peer
/// }
///
/// impl TestCommunity{
/// fn new() -> Option<Self> {
///
/// // Use the highest available key
/// let pk: PublicKey = PublicKey::from_vec(vec![
/// 48, 129, 167, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 39, 3,
/// 129, 146, 0, 4, 2, 86, 251, 75, 206, 159, 133, 120, 63, 176, 235, 178, 14, 8, 197, 59,
/// 107, 51, 179, 139, 3, 155, 20, 194, 112, 113, 15, 40, 67, 115, 37, 223, 152, 7, 102,
/// 154, 214, 90, 110, 180, 226, 5, 190, 99, 163, 54, 116, 173, 121, 40, 80, 129, 142, 82,
/// 118, 154, 96, 127, 164, 248, 217, 91, 13, 80, 91, 94, 210, 16, 110, 108, 41, 57, 4,
/// 243, 49, 52, 194, 254, 130, 98, 229, 50, 84, 21, 206, 134, 223, 157, 189, 133, 50, 210,
/// 181, 93, 229, 32, 179, 228, 179, 132, 143, 147, 96, 207, 68, 48, 184, 160, 47, 227, 70,
/// 147, 23, 159, 213, 105, 134, 60, 211, 226, 8, 235, 186, 20, 241, 85, 170, 4, 3, 40,
/// 183, 98, 103, 80, 164, 128, 87, 205, 101, 67, 254, 83, 142, 133,
/// ])?;
///
/// // Actually create the community
/// Some(TestCommunity {
/// peer: Peer::new(
/// pk,
/// Address{
/// address: Ipv4Addr::new(0,0,0,0),
/// port: 0
/// },
/// true,
/// )
/// })
/// }
/// }
///
/// impl Community for TestCommunity{
///
/// // Returns the hash of our master peer
/// fn get_mid(&self) -> Option<Vec<u8>> {
/// Some(self.peer.get_sha1()?.to_vec())
/// }
///
/// // The function which will be called when the community receives a packet
/// fn on_receive(&self, header: Header, deserializer: PacketDeserializer, address: Address) -> Result<(),Box<dyn Error>>{
/// # assert_eq!(header.mid_hash, self.get_mid());
/// # assert_eq!(header.version, PyIPV8Header);
/// # assert_eq!(header.message_type, Some(42));
/// // Do some stuff here like to distribute the message based on it's message_type (in the header)
/// Ok(())
/// }
/// }
///
/// let mut config = Config::default();
/// let community = TestCommunity::new().unwrap();
/// let mid = community.get_mid();
/// config.communities.add_community(Box::new(community));
/// let ipv8 = IPv8::new(config);
///
/// // now simulate a packet coming in
///
/// // Create a packet to test the community with
/// let packet = Packet::new(Header{
/// size: 23,
/// version: PyIPV8Header,
/// mid_hash: mid,
/// message_type: Some(42),
/// }).unwrap();
///
/// // Normally you would want to sign the packet here
///
/// // Yeet the packet
/// ipv8.config.communities.forward_message(packet,Address{
/// address: Ipv4Addr::new(42,42,42,42),
/// port: 42,
/// });
///
/// ```
pub trait Community {
// fn new() -> Option<Self>
// where
// Self: Sized;

/// Returns the hash of our master peer public key
fn get_mid(&self) -> Option<Vec<u8>>;

/// Gets called whenever a packet is received directed at this community
/// DO NOT OVERRIDE
fn receive(
&self,
header: Header,
deserializer: PacketDeserializer,
address: Address,
) -> Result<(), Box<dyn Error>> {
// DO NOT OVERRIDE
//! used to pre-decode the header and filter out messages

fn warn_deprecated(message: &str, address: Address) -> Result<(), Box<dyn Error>> {
warn!(
"Received deprecated message {} from ({:?})",
message, address
);
Ok(())
}
match header.message_type.ok_or(HeaderUnwrapError)? {
255 => warn_deprecated("reserved-255", address),
254 => warn_deprecated("on-missing-sequence", address),
253 => warn_deprecated("missing-proof", address),
252 => warn_deprecated("signature-request", address),
251 => warn_deprecated("signature-response", address),
248 => warn_deprecated("on-identity", address),
247 => warn_deprecated("on-missing-identity", address),
244 => warn_deprecated("destroy-community", address),
243 => warn_deprecated("authorize", address),
242 => warn_deprecated("revoke", address),
241 => warn_deprecated("subjective-set", address),
240 => warn_deprecated("missing-subjective-set", address),
239 => warn_deprecated("on-missing-message", address),
238 => warn_deprecated("undo-own", address),
237 => warn_deprecated("undo-other", address),
236 => warn_deprecated("dynamic-settings", address),
235 => warn_deprecated("missing-last-message", address),
_ => self.on_receive(header, deserializer, address),
}
}

fn on_receive(
&self,
header: Header,
deserializer: PacketDeserializer,
address: Address,
) -> Result<(), Box<dyn Error>>;
}

pub struct CommunityRegistry {
// mid, community
communities: HashMap<Vec<u8>, Box<dyn Community>>,
}

impl CommunityRegistry {
pub fn add_community(&mut self, item: Box<dyn Community>) -> Result<(), Box<dyn Error>> {
self.communities
.insert(item.get_mid().ok_or(MidError)?, item)
.ok_or(InsertionError)?;
Ok(())
}

/// Forwards the message to the corresponding community
pub fn forward_message(&self, packet: Packet, address: Address) -> Result<(), Box<dyn Error>> {
// deserialize the header
let deserializer = packet.start_deserialize();

// We use peek here instead of get, even though we give the header along with the receive call
// this is because at this point, the header is not verified yet so we still assume the message is valid.
// We can't verify the header here yet as not all messages have a signature. Communities will have to decide
// on their own if they want to verify the header. We do give it along as only having to deserialize the header once
// makes it slightly more efficient.
let header = deserializer.peek_header()?;

// get the mid from the header and use it for a hashtable lookup
let mid = header.mid_hash.as_ref().ok_or(MidError)?;
let community = self.communities.get(mid).ok_or(UnknownCommunityError)?;

// Actually forward it
community.receive(header, deserializer, address)
}
}

impl Default for CommunityRegistry {
fn default() -> Self {
Self {
communities: HashMap::new(),
}
}
}
22 changes: 22 additions & 0 deletions src/community/peer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::crypto::keytypes::PublicKey;
use crate::networking::address::Address;

pub struct Peer {
key: PublicKey,
address: Address,
intro: bool,
}

impl Peer {
pub fn new(key: PublicKey, address: Address, intro: bool) -> Self {
Self {
key,
address,
intro,
}
}

pub fn get_sha1(&self) -> Option<[u8; 20]> {
self.key.sha1()
}
}
6 changes: 6 additions & 0 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use crate::networking::address::Address;
use std::net::Ipv4Addr;
use crate::community::CommunityRegistry;

pub struct Config {
/// Default list of host used for peer discovery
pub default_hosts: Vec<Address>,
/// from py-ipv8 configuration. UDP socket address.
/// There split up in "address" and "port"
pub socketaddress: Address,

/// The registry containing all the communities
pub communities: CommunityRegistry,
}

impl Default for Config {
Expand All @@ -17,6 +21,8 @@ impl Default for Config {
port: 8090,
},

communities: CommunityRegistry::default(),

default_hosts: vec![
// Dispersy
Address {
Expand Down
29 changes: 28 additions & 1 deletion src/crypto/keytypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
//! | HIGH_SIGNATURE_LENGTH | 144 | 153 | 9 | 144 |
//! | ED25519_SIGNATURE_LENGTH | 64 | 64 | 0 | 64 |

use std::fmt;

use openssl;
use openssl::sha::sha1;
use rust_sodium::crypto::sign::ed25519;
use std::fmt;

// TODO: when ed25519 becomes available for rust OpenSSL, rust_sodium will be removed.

Expand Down Expand Up @@ -129,6 +131,10 @@ impl PublicKey {
Some(m)
}
}

pub fn sha1(&self) -> Option<[u8; 20]> {
Some(sha1(&self.to_vec()?))
}
}

impl PartialEq for PublicKey {
Expand Down Expand Up @@ -350,4 +356,25 @@ mod tests {
keyvec
);
}

#[test]
fn test_sha1() {
let keyvec = vec![
48, 64, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 1, 3, 44, 0, 4,
0, 80, 239, 172, 104, 165, 76, 172, 6, 229, 136, 156, 105, 23, 249, 46, 30, 148, 87,
105, 57, 6, 105, 134, 2, 229, 115, 169, 44, 162, 41, 190, 228, 56, 20, 100, 64, 79,
167, 224, 118, 14,
];

let key = PublicKey::from_vec(keyvec.clone()).unwrap();
let sha1sum = key.sha1().unwrap();

assert_eq!(
[
254, 127, 138, 49, 67, 126, 206, 58, 169, 85, 132, 14, 211, 247, 13, 170, 244, 166,
152, 180,
],
sha1sum
)
}
}
8 changes: 6 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#[macro_use]
extern crate log;

pub mod error;
pub mod serialization;

pub mod community;
pub mod configuration;
pub mod crypto;
pub mod event;
Expand All @@ -17,10 +21,10 @@ use configuration::Config;
/// use ipv8::IPv8;
/// use ipv8::configuration::Config;
///
/// let ipv8_instance = IPv8::new(Config::default());
/// let ipv8 = IPv8::new(Config::default());
/// ```
pub struct IPv8 {
config: Config,
pub config: Config,
}

impl IPv8 {
Expand Down
Loading