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

WIP feat/routing: use node key as name #1115

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow... why would Z have A's original ID? For the previous line, it would be nicer if your addition was spelled out: "node Z (of NaeManager Y)"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the group X has notified group Y about A's original id

/// `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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets hope the client/joining node is not busy, or it might not manage many iterations. Often this type of code allows a maximum number of tries instead of a timeout.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the network side is waiting based on timeout, so here needs to be wait on timeout as well.

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