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

NetworkID #131

Merged
merged 7 commits into from
Oct 17, 2023
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Add new facade function `new` to creating `RwLock` based on feature flag. [#94]
- Add `NetworkId` in configuration [#123]

### Changed

- Change `RwLock` API to support diagnostics feature flag [#94]
- Change network wire encoding to support `NetworkId` [#123]

## [0.5.0] - 2023-05-17

Expand Down Expand Up @@ -121,6 +123,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#112]: https://github.com/dusk-network/kadcast/issues/112
[#115]: https://github.com/dusk-network/kadcast/issues/115
[#117]: https://github.com/dusk-network/kadcast/issues/117
[#123]: https://github.com/dusk-network/kadcast/issues/123

<!-- Releases -->

Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ pub const DEFAULT_BLOCKLIST_REFRESH_SECS: u64 = 10;

#[derive(Clone, Serialize, Deserialize)]
pub struct Config {
/// KadcastID
pub kadcast_id: Option<u8>,

/// Public `SocketAddress` of the [Peer]. No domain name allowed
///
/// This is the address where other peers can contact you.
Expand Down Expand Up @@ -86,6 +89,7 @@ impl Default for Config {
fn default() -> Self {
Self {
public_address: "127.0.0.1:9000".to_string(),
kadcast_id: None,
listen_address: None,
bootstrapping_nodes: vec![],
auto_propagate: ENABLE_BROADCAST_PROPAGATION,
Expand Down
101 changes: 101 additions & 0 deletions src/encoding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Structs Encoding Explanation

This document explains how various structs are encoded and decoded in the provided Rust code. The document covers the following structs:

1. [Message Struct](#1-message-struct)
2. [Header Struct](#2-header-struct)
3. [PeerEncodedInfo Struct](#3-peerencodedinfo-struct)
4. [NodePayload Struct](#4-nodepayload-struct)
5. [BroadcastPayload Struct](#5-broadcastpayload-struct)
6. [Marshallable Trait](#6-marshallable-trait)

---

## 1. Message Struct

**Purpose**: The `Message` struct represents various types of network messages, such as Ping, Pong, FindNodes, Nodes, and Broadcast. Each message has its header and associated payload.

**Encoding**:

| Field | Length (bytes) | Description |
|------------------|-----------------|-------------------------------------------------|
| Message Type | 1 | Type identifier for the message. |
| Header | Variable | Header of the message. |
| Payload | Variable | Payload data specific to the message type. |

- The length of the Header and Payload fields depends on the message type.

---

## 2. Header Struct

**Purpose**: The `Header` struct represents the header of a network message. It includes information such as the sender's binary ID, sender port, network ID, and reserved bytes.

**Encoding**:

| Field | Length (bytes) | Description |
|------------------|-----------------|---------------------------------------|
| Binary ID | 32 | The binary ID of the sender. |
| Nonce | 8 | Nonce of the sender. |
| Sender Port | 2 | Port of the sender (Little Endian). |
| Network ID | 1 | Network ID. |
| Reserved Bytes | 2 | Reserved bytes. |

---

## 3. PeerEncodedInfo Struct

**Purpose**: The `PeerEncodedInfo` struct contains information about a peer, including their IP address, port, and binary ID.

**Encoding**:

| Field | Length (bytes) | Description |
|---------------|----------------|-----------------------------------|
| IP Info | Variable | The IP address (IPv4 or IPv6). |
| Port | 2 | Port of the peer (LE encoding) |
| Binary ID | 32 | The binary ID of the peer. |

- The length of the IP Info field depends on whether it's IPv4 or IPv6.

- IPv4 peers are identified with a leading `0` in the IP Info field. IPv6 peers do not have a leading `0`.

---

## 4. NodePayload Struct

**Purpose**: The `NodePayload` struct represents a collection of peer information.

**Encoding**:

| Field | Length (bytes) | Description |
|------------------|-----------------|-------------------------------------------------|
| Number of Peers | 2 | Number of peers in the payload. |
| Peer Info | Variable | Information about each peer (PeerEncodedInfo). |

- The length of the Peer Info field depends on the number of peers in the payload.

- The number of peers is prepended as a 2-byte length before the Peer Info field.

---

## 5. BroadcastPayload Struct

**Purpose**: The `BroadcastPayload` struct represents the payload of a broadcast message. It includes the message's height and the message content.

**Encoding**:

| Field | Length (bytes) | Description |
|------------------------|----------------|----------------------------------------|
| Height | 1 | Height of the message. |
| Message Content Length | 4 | Length of the message (Little Endian). |
| Message Content | Variable | The content of the message. |

- The length of the Message Content field depends on the size of the message.

- The length of the Message Content is prepended as a 4-byte length before the content.

---

## 6. Marshallable Trait

The `Marshallable` trait defines methods for encoding and decoding the structs into/from binary data.
18 changes: 10 additions & 8 deletions src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,34 @@ mod tests {

#[test]
fn test_encode_ping() -> Result<()> {
let peer = PeerNode::generate("192.168.0.1:666")?;
let peer = PeerNode::generate("192.168.0.1:666", 0)?;
let a = Message::Ping(peer.to_header());
test_kadkast_marshal(a)
}
#[test]
fn test_encode_pong() -> Result<()> {
let peer = PeerNode::generate("192.168.0.1:666")?;
let peer = PeerNode::generate("192.168.0.1:666", 0)?;
let a = Message::Pong(peer.to_header());
test_kadkast_marshal(a)
}

#[test]
fn test_encode_find_nodes() -> Result<()> {
let peer = PeerNode::generate("192.168.0.1:666")?;
let target = *PeerNode::generate("192.168.1.1:666")?.id().as_binary();
let peer = PeerNode::generate("192.168.0.1:666", 0)?;
let target =
*PeerNode::generate("192.168.1.1:666", 0)?.id().as_binary();
let a = Message::FindNodes(peer.to_header(), target);
test_kadkast_marshal(a)
}

#[test]
fn test_encode_nodes() -> Result<()> {
let peer = PeerNode::generate("192.168.0.1:666")?;
let peer = PeerNode::generate("192.168.0.1:666", 0)?;
let nodes = vec![
PeerNode::generate("192.168.1.1:666")?,
PeerNode::generate("192.168.1.1:666", 0)?,
PeerNode::generate(
"[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:666",
0,
)?,
]
.iter()
Expand All @@ -72,13 +74,13 @@ mod tests {

#[test]
fn test_encode_empty_nodes() -> Result<()> {
let peer = PeerNode::generate("192.168.0.1:666")?;
let peer = PeerNode::generate("192.168.0.1:666", 0)?;
let a = Message::Nodes(peer.to_header(), NodePayload { peers: vec![] });
test_kadkast_marshal(a)
}
#[test]
fn test_encode_broadcast() -> Result<()> {
let peer = PeerNode::generate("192.168.0.1:666")?;
let peer = PeerNode::generate("192.168.0.1:666", 0)?;
let a = Message::Broadcast(
peer.to_header(),
BroadcastPayload {
Expand Down
9 changes: 9 additions & 0 deletions src/encoding/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::{kbucket::BinaryID, K_ID_LEN_BYTES, K_NONCE_LEN};
pub struct Header {
pub(crate) binary_id: BinaryID,
pub(crate) sender_port: u16,
pub(crate) network_id: u8,
pub(crate) reserved: [u8; 2],
}

Expand All @@ -30,6 +31,7 @@ impl Marshallable for Header {
writer.write_all(self.binary_id.as_binary())?;
writer.write_all(self.binary_id.nonce())?;
writer.write_all(&self.sender_port.to_le_bytes())?;
writer.write_all(&[self.network_id])?;
writer.write_all(&self.reserved)?;
Ok(())
}
Expand All @@ -50,12 +52,19 @@ impl Marshallable for Header {
let mut port_buffer = [0; 2];
reader.read_exact(&mut port_buffer)?;
let port = u16::from_le_bytes(port_buffer);

let mut network_id = [0; 1];
reader.read_exact(&mut network_id)?;
let network_id = network_id[0];

let mut reserved = [0; 2];
reader.read_exact(&mut reserved)?;

Ok(Header {
binary_id,
sender_port: port,
reserved,
network_id,
})
}
}
9 changes: 9 additions & 0 deletions src/handling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ impl MessageHandler {
let remote_peer = PeerNode::from_socket(
remote_peer_addr,
*message.header().binary_id(),
message.header().network_id,
);

match handler.handle_peer(remote_peer).await {
Expand All @@ -114,6 +115,14 @@ impl MessageHandler {
);
continue;
}
Err(NodeInsertError::MismatchNetwork(n)) => {
error!(
"Unable to insert node - NETWORK MISMATCH {} - {}",
n.value().address(),
n.network_id,
);
continue;
}
};

handler.handle_message(message, remote_peer_addr).await;
Expand Down
26 changes: 12 additions & 14 deletions src/kbucket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ impl<V> Tree<V> {
&mut self,
node: Node<V>,
) -> Result<InsertOk<V>, InsertError<V>> {
if self.root().network_id != node.network_id {
return Err(NodeInsertError::MismatchNetwork(node));
}
match self.root.calculate_distance(&node) {
None => Err(NodeInsertError::Invalid(node)),
Some(height) => self.get_or_create_bucket(height).insert(node),
Expand Down Expand Up @@ -99,7 +102,7 @@ impl<V> Tree<V> {
) -> impl Iterator<Item = BucketHeight> {
let max_buckets = (crate::K_ID_LEN_BYTES * 8) as BucketHeight;
(0..max_buckets).filter(move |h| {
self.buckets.get(h).map_or_else(|| true, |b| b.is_idle())
self.buckets.get(h).map_or_else(|| true, |b| b.has_idle())
})
}

Expand All @@ -110,7 +113,7 @@ impl<V> Tree<V> {
{
self.buckets
.iter()
.filter(|(_, bucket)| bucket.is_idle())
.filter(|(_, bucket)| bucket.has_idle())
.map(|(&height, bucket)| (height, bucket.pick::<K_ALPHA>()))
}

Expand Down Expand Up @@ -172,29 +175,24 @@ mod tests {
use crate::tests::Result;
#[test]
fn test_buckets() -> Result<()> {
let root = PeerNode::generate("192.168.0.1:666")?;
let root = PeerNode::generate("192.168.0.1:666", 0)?;
let mut config = BucketConfig::default();
config.node_evict_after = Duration::from_millis(5000);
config.node_ttl = Duration::from_secs(60);

let mut route_table = Tree::new(root, config);
for i in 2..255 {
let res = route_table.insert(PeerNode::generate(
&format!("192.168.0.{}:666", i)[..],
)?);
let node = PeerNode::generate(format!("192.168.0.{}:666", i), 0)?;
let res = route_table.insert(node);
match res {
Ok(_) => {}
Err(error) => match error {
NodeInsertError::Invalid(_) => {
assert!(false)
}
_ => {}
},
Ok(_) | Err(NodeInsertError::Full(_)) => {}
_ => panic!("Node must be valid"),
}
}
let res = route_table
.insert(PeerNode::generate("192.168.0.1:666")?)
.insert(PeerNode::generate("192.168.0.1:666", 0)?)
.expect_err("this should be an error");

assert!(if let NodeInsertError::Invalid(_) = res {
true
} else {
Expand Down
Loading