Skip to content
This repository has been archived by the owner on Jun 25, 2021. It is now read-only.

Commit

Permalink
feat/routing: use node key as name
Browse files Browse the repository at this point in the history
  • Loading branch information
maqi committed Aug 3, 2016
1 parent 632f6f5 commit 25eec9f
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 99 deletions.
34 changes: 22 additions & 12 deletions src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// relating to use of the SAFE Network Software.

use sodiumoxide::crypto::{box_, hash, sign};
use std::cmp;
use std::fmt::{self, Debug, Formatter};
use xor_name::XorName;

Expand Down Expand Up @@ -56,11 +57,6 @@ impl FullId {
&self.public_id
}

/// Returns mutable reference to public ID.
pub fn public_id_mut(&mut self) -> &mut PublicId {
&mut self.public_id
}

/// Secret signing key.
pub fn signing_private_key(&self) -> &sign::SecretKey {
&self.private_sign_key
Expand All @@ -78,7 +74,7 @@ impl Default for FullId {
}
}

#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, RustcEncodable, RustcDecodable)]
#[derive(Hash, PartialEq, Eq, PartialOrd, Copy, Clone, RustcEncodable, RustcDecodable)]
/// Network identity component containing name and public keys.
pub struct PublicId {
public_encrypt_key: box_::PublicKey,
Expand All @@ -92,18 +88,18 @@ impl Debug for PublicId {
}
}

impl Ord for PublicId {
fn cmp(&self, other: &PublicId) -> cmp::Ordering {
self.name().cmp(other.name())
}
}

impl PublicId {
/// Return initial/relocated name.
pub fn name(&self) -> &XorName {
&self.name
}

/// Name field is initially same as original_name, this should be replaced by relocated name
/// calculated by the nodes close to original_name by using this method
pub fn set_name(&mut self, name: XorName) {
self.name = name;
}

/// Return public signing key.
pub fn encrypting_public_key(&self) -> &box_::PublicKey {
&self.public_encrypt_key
Expand All @@ -122,3 +118,17 @@ impl PublicId {
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn public_id_sorting_test() {
let mut public_ids: Vec<PublicId> = (0..10).map(|_| *FullId::new().public_id()).collect();
public_ids.sort();
for i in 0..9 {
assert!(public_ids[i].name() < public_ids[i + 1].name());
}
}
}
29 changes: 14 additions & 15 deletions src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ impl RoutingMessage {
/// Replaces this message's contents with its hash.
pub fn to_grp_msg_hash(&self) -> Result<RoutingMessage, RoutingError> {
let content = match self.content {
MessageContent::GetNodeNameResponse { .. } |
MessageContent::GetNameRangeResponse { .. } |
MessageContent::GetCloseGroupResponse { .. } |
MessageContent::UserMessagePart { .. } => {
let serialised_msg = try!(serialise(self));
Expand All @@ -300,8 +300,8 @@ pub enum MessageContent {
/// Ask the network to alter your `PublicId` name.
///
/// This is sent by a `Client` to its `NaeManager` with the intent to become a routing node with
/// a new name chosen by the `NaeManager`.
GetNodeName {
/// a new name range chosen by the `NaeManager`.
GetNameRange {
/// The client's `PublicId` (public keys and name)
current_id: PublicId,
/// The message's unique identifier.
Expand All @@ -327,16 +327,15 @@ pub enum MessageContent {
encrypted_connection_info: Vec<u8>,
/// Nonce used to provide a salt in the encrypted message.
nonce_bytes: [u8; box_::NONCEBYTES],
// TODO: The receiver should have that in the node_id_cache.
/// The sender's public ID.
/// The sender's public ID. The receiver should have that in the node_id_cache.
public_id: PublicId,
},
/// Reply with the new `PublicId` for the joining node.
///
/// Sent from the `NodeManager` to the `Client`.
GetNodeNameResponse {
/// Supplied `PublicId`, but with the new name
relocated_id: PublicId,
GetNameRangeResponse {
/// Name Range
name_range: (XorName, XorName),
/// Our close group `PublicId`s.
close_group_ids: Vec<PublicId>,
/// The message's unique identifier.
Expand Down Expand Up @@ -445,9 +444,9 @@ impl Debug for SignedMessage {
impl Debug for MessageContent {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match *self {
MessageContent::GetNodeName { ref current_id, ref message_id } => {
MessageContent::GetNameRange { ref current_id, ref message_id } => {
write!(formatter,
"GetNodeName {{ {:?}, {:?} }}",
"GetNameRange {{ {:?}, {:?} }}",
current_id,
message_id)
}
Expand All @@ -460,13 +459,13 @@ impl Debug for MessageContent {
}
MessageContent::GetCloseGroup(id) => write!(formatter, "GetCloseGroup({:?})", id),
MessageContent::ConnectionInfo { .. } => write!(formatter, "ConnectionInfo {{ .. }}"),
MessageContent::GetNodeNameResponse { ref relocated_id,
ref close_group_ids,
ref message_id } => {
MessageContent::GetNameRangeResponse { ref name_range,
ref close_group_ids,
ref message_id } => {
write!(formatter,
"GetNodeNameResponse {{ {:?}, {:?}, {:?} }}",
"GetNameRangeResponse {{ {:?}, {:?}, {:?} }}",
close_group_ids,
relocated_id,
name_range,
message_id)
}
MessageContent::GetCloseGroupResponse { ref close_group_ids, message_id } => {
Expand Down
30 changes: 18 additions & 12 deletions src/state_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,32 @@ use xor_name::XorName;
/// ## Becoming a node
///
/// If A wants to become a full routing node (`client_restriction == false`), it needs to relocate,
/// i. e. change its name to a value chosen by the network, and then add its peers to its routing
/// table and get added to their routing tables.
/// i. e. change its name to a value that sits in the range chosen by the network, and then add its
/// peers to its routing table and get added to their routing tables.
///
///
/// ### Getting a new network name from the `NaeManager`
/// ### Getting an identity range from the `NaeManager`
///
/// Once in `Client` state, A sends a `GetNodeName` request to the `NaeManager` group authority X
/// of A's current name. X computes a new name and sends it in an `ExpectCloseNode` request to the
/// `NaeManager` Y of A's new name. Each member of Y caches A's public ID, and Y sends a
/// `GetNodeName` response back to A, which includes the public IDs of the members of Y.
/// Once in `Client` state, A sends a `GetNameRange` request to the `NaeManager` group authority X
/// of A's current name. X computes a new name and sends it in an `ExpectCloseNode` request to the
/// `NaeManager` Y of A's possible new name. Each member of Y caches node A's ID, sends a `GetNameRange`
/// response back to A, which includes the public IDs of the members of Y.
///
///
/// ### Computing the new identity
///
/// Once A accumulates the `GetNameRange` response (the quorum_size shall be the same as the previous received
/// from `BootstrapIdentify`), it starts generating a key pair whose public_key falls into the give range. And such
/// public_key will then be used as node A's new name address. Such computing must be completed in a given period,
/// otherwise a new round of connecting shall be understaken.
///
/// ### Connecting to the close group
///
/// To the `ManagedNode` for each public ID it receives from members of Y, A sends its
/// `ConnectionInfo`. It also caches the ID.
/// For each public ID it receives from members of Y, A sends its `ConnectionInfo`.
///
/// For each `ConnectionInfo` that a node Z receives from A, it decides whether it wants A in its
/// routing table. If yes, and if A's ID is in its ID cache, Z sends its own `ConnectionInfo` back
/// to A and also attempts to connect to A via Crust. A does the same, once it receives the
/// For each `ConnectionInfo` that a node Z(members of Y) receives from A, it decides whether it wants A in its
/// routing table. If yes, and if A's ID is in its ID cache(node A's original ID), Z sends its own
/// `ConnectionInfo` back to A and also attempts to connect to A via Crust. A does the same, once it receives the
/// `ConnectionInfo`.
///
/// Once the connection between A and Z is established and a Crust `OnConnect` event is raised,
Expand Down
4 changes: 2 additions & 2 deletions src/states/common/bootstrapped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ pub trait Bootstrapped: AnyState + SendRoutingMessage {

if let Some(ejected) = self.ack_mgr_mut().add_to_pending(ack, unacked_msg) {
// FIXME: This currently occurs for Connect request and
// GetNodeName response. Connect requests arent filtered which
// GetNameRange response. Connect requests arent filtered which
// should get resolved with peer_mgr completion.
// GetNodeName response resends from a node needs to get looked into.
// GetNameRange response resends from a node needs to get looked into.
trace!("{:?} - Ejected pending ack: {:?} - {:?}",
self,
ack,
Expand Down
60 changes: 39 additions & 21 deletions src/states/joining_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use kademlia_routing_table::RoutingTable;
use maidsafe_utilities::serialisation;
use std::fmt::{self, Debug, Formatter};
use std::sync::mpsc::Sender;
use std::time::Duration;
use std::time::{Duration, Instant};

use ack_manager::{Ack, AckManager};
use action::Action;
Expand All @@ -44,19 +44,20 @@ use super::common::Testable;
use super::Node;
use timer::Timer;
use types::MessageId;
#[cfg(feature = "use-mock-crust")]
use xor_name::XorName;

/// Time (in seconds) after which a `GetNodeName` request is resent.
const GET_NODE_NAME_TIMEOUT_SECS: u64 = 60;
/// Time (in seconds) after which a `GetNameRange` request is resent.
const GET_NAME_RANGE_TIMEOUT_SECS: u64 = 60;
/// Time (in seconds) for the duration to compute a valid new id.
const COMPUTING_IDENTITY_TIMEOUT_SECS: u64 = 30;

pub struct JoiningNode {
ack_mgr: AckManager,
cache: Box<Cache>,
crust_service: Service,
event_sender: Sender<Event>,
full_id: FullId,
get_node_name_timer_token: Option<u64>,
get_name_range_timer_token: Option<u64>,
msg_accumulator: MessageAccumulator,
peer_mgr: PeerManager,
signed_msg_filter: SignedMessageFilter,
Expand Down Expand Up @@ -85,7 +86,7 @@ impl JoiningNode {
crust_service: crust_service,
event_sender: event_sender,
full_id: full_id,
get_node_name_timer_token: None,
get_name_range_timer_token: None,
msg_accumulator: MessageAccumulator::with_quorum_size(quorum_size),
peer_mgr: peer_mgr,
signed_msg_filter: SignedMessageFilter::new(),
Expand Down Expand Up @@ -183,8 +184,8 @@ impl JoiningNode {
}

fn handle_timeout(&mut self, token: u64) -> bool {
if self.get_node_name_timer_token == Some(token) {
info!("{:?} Failed to get GetNodeName response.", self);
if self.get_name_range_timer_token == Some(token) {
info!("{:?} Failed to get GetNameRange response.", self);
self.send_event(Event::RestartRequired);
return false;
}
Expand Down Expand Up @@ -274,20 +275,18 @@ impl JoiningNode {
Transition::Stay
}

fn handle_get_node_name_response(&mut self,
relocated_id: PublicId,
mut close_group_ids: Vec<PublicId>,
dst: Authority)
-> Transition {
self.full_id.public_id_mut().set_name(*relocated_id.name());
fn handle_get_name_range_response(&mut self,
mut close_group_ids: Vec<PublicId>,
dst: Authority)
-> Transition {
self.peer_mgr.reset_routing_table(*self.full_id.public_id());

close_group_ids.truncate(GROUP_SIZE / 2);

let mut result = Transition::Stay;

for close_node_id in close_group_ids {
debug!("{:?} Sending connection info to {:?} on GetNodeName response.",
debug!("{:?} Sending connection info to {:?} on GetNameRange response.",
self,
close_node_id);

Expand Down Expand Up @@ -322,10 +321,10 @@ impl JoiningNode {
}

fn relocate(&mut self) -> Result<(), RoutingError> {
let duration = Duration::from_secs(GET_NODE_NAME_TIMEOUT_SECS);
self.get_node_name_timer_token = Some(self.timer.schedule(duration));
let duration = Duration::from_secs(GET_NAME_RANGE_TIMEOUT_SECS);
self.get_name_range_timer_token = Some(self.timer.schedule(duration));

let request_content = MessageContent::GetNodeName {
let request_content = MessageContent::GetNameRange {
current_id: *self.full_id.public_id(),
message_id: MessageId::new(),
};
Expand Down Expand Up @@ -361,6 +360,22 @@ impl JoiningNode {
let _ = self.crust_service.disconnect(*peer_id);
}
}

fn compute_new_id(&mut self, name_range: &(XorName, XorName)) -> Result<(), RoutingError> {
let now = Instant::now();
loop {
if now.elapsed() > Duration::from_secs(COMPUTING_IDENTITY_TIMEOUT_SECS) {
error!("{:?} failed to compute an id between {:?}", self, name_range);
return Err(RoutingError::RejectedPublicId);
}
let new_id = FullId::new();
let new_public_name = *new_id.public_id().name();
if new_public_name > name_range.0 && new_public_name < name_range.1 {
self.full_id = new_id;
return Ok(());
}
}
}
}

impl AnyState for JoiningNode {
Expand Down Expand Up @@ -441,9 +456,12 @@ impl Bootstrapped for JoiningNode {
// Ack
(MessageContent::Ack(ack, _), _, _) => Ok(self.handle_ack_response(ack)),
// GetNodeNameResponse
(MessageContent::GetNodeNameResponse { relocated_id, close_group_ids, .. },
(MessageContent::GetNameRangeResponse { name_range, close_group_ids, .. },
Authority::NodeManager(_),
dst) => Ok(self.handle_get_node_name_response(relocated_id, close_group_ids, dst)),
dst) => {
try!(self.compute_new_id(&name_range));
Ok(self.handle_get_name_range_response(close_group_ids, dst))
}
// ConnectionInfo
(MessageContent::ConnectionInfo { encrypted_connection_info, nonce_bytes, public_id },
src @ Authority::Client { .. },
Expand Down Expand Up @@ -485,7 +503,7 @@ impl Bootstrapped for JoiningNode {

impl Connect for JoiningNode {
fn handle_node_identify(&mut self, public_id: PublicId, peer_id: PeerId) -> Transition {
debug!("{:?} Handling NodeIdentify from {:?}.",
debug!("{:?} Handling NodeIdentify from {:?} as joining_node.",
self,
public_id.name());

Expand Down

0 comments on commit 25eec9f

Please sign in to comment.