Skip to content
36 changes: 15 additions & 21 deletions config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use std::io::prelude::*;
use std::io::BufReader;
use std::path::PathBuf;

use p2p::types::{TESTNET_PEER_PORT, USERNET_PEER_PORT};

use crate::comments::insert_comments;
use crate::core::global;
use crate::p2p;
Expand All @@ -42,10 +44,7 @@ pub const FOREIGN_API_SECRET_FILE_NAME: &str = ".foreign_api_secret";

fn get_grin_path(chain_type: &global::ChainTypes) -> Result<PathBuf, ConfigError> {
// Check if grin dir exists
let mut grin_path = match dirs::home_dir() {
Some(p) => p,
None => PathBuf::new(),
};
let mut grin_path = dirs::home_dir().unwrap_or_else(|| PathBuf::new());
grin_path.push(GRIN_HOME);
grin_path.push(chain_type.shortname());
// Create if the default path doesn't exist
Expand Down Expand Up @@ -113,7 +112,7 @@ fn check_api_secret_files(
pub fn initial_setup_server(chain_type: &global::ChainTypes) -> Result<GlobalConfig, ConfigError> {
check_api_secret_files(chain_type, API_SECRET_FILE_NAME)?;
check_api_secret_files(chain_type, FOREIGN_API_SECRET_FILE_NAME)?;
// Use config file if current directory if it exists, .grin home otherwise
// Use config file in current directory if it exists, .grin home otherwise
if let Some(p) = check_config_current_dir(SERVER_CONFIG_FILE_NAME) {
GlobalConfig::new(p.to_str().unwrap())
} else {
Expand Down Expand Up @@ -168,7 +167,7 @@ impl GlobalConfig {
global::ChainTypes::Mainnet => {}
global::ChainTypes::Testnet => {
defaults.api_http_addr = "127.0.0.1:13413".to_owned();
defaults.p2p_config.port = 13414;
defaults.p2p_config.port = TESTNET_PEER_PORT;
defaults
.stratum_mining_config
.as_mut()
Expand All @@ -182,7 +181,7 @@ impl GlobalConfig {
}
global::ChainTypes::UserTesting => {
defaults.api_http_addr = "127.0.0.1:23413".to_owned();
defaults.p2p_config.port = 23414;
defaults.p2p_config.port = USERNET_PEER_PORT;
defaults.p2p_config.seeding_type = p2p::Seeding::None;
defaults
.stratum_mining_config
Expand Down Expand Up @@ -234,14 +233,12 @@ impl GlobalConfig {
match decoded {
Ok(gc) => {
self.members = Some(gc);
return Ok(self);
}
Err(e) => {
return Err(ConfigError::ParseError(
self.config_file_path.unwrap().to_str().unwrap().to_string(),
format!("{}", e),
));
Ok(self)
}
Err(e) => Err(ConfigError::ParseError(
self.config_file_path.unwrap().to_str().unwrap().to_string(),
format!("{}", e),
)),
}
}

Expand Down Expand Up @@ -275,27 +272,24 @@ impl GlobalConfig {

/// Enable mining
pub fn stratum_enabled(&mut self) -> bool {
return self
.members
self.members
.as_mut()
.unwrap()
.server
.stratum_mining_config
.as_mut()
.unwrap()
.enable_stratum_server
.unwrap();
.unwrap()
}

/// Serialize config
pub fn ser_config(&mut self) -> Result<String, ConfigError> {
let encoded: Result<String, toml::ser::Error> =
toml::to_string(self.members.as_mut().unwrap());
match encoded {
Ok(enc) => return Ok(enc),
Err(e) => {
return Err(ConfigError::SerializationError(format!("{}", e)));
}
Ok(enc) => Ok(enc),
Err(e) => Err(ConfigError::SerializationError(format!("{}", e))),
}
}

Expand Down
116 changes: 82 additions & 34 deletions p2p/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ use crate::msg::PeerAddrs;
use crate::util::secp::pedersen::RangeProof;
use crate::util::RwLock;

/// Default main network peer port.
pub const MAINNET_PEER_PORT: u16 = 3414;
/// Default test network peer port.
pub const TESTNET_PEER_PORT: u16 = 13414;
/// Default user network peer port.
pub const USERNET_PEER_PORT: u16 = 23414;

/// Maximum number of block headers a peer should ever send
pub const MAX_BLOCK_HEADERS: u32 = 512;

Expand Down Expand Up @@ -207,10 +214,10 @@ impl<'de> Deserialize<'de> for PeerAddrs {
}

impl std::hash::Hash for PeerAddr {
/// If loopback address then we care about ip and port.
/// If private address then we care about ip and port.
/// If regular address then we only care about the ip and ignore the port.
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
if self.0.ip().is_loopback() {
if is_private_ip(&self.0.ip()) {
self.0.hash(state);
} else {
self.0.ip().hash(state);
Expand All @@ -219,37 +226,89 @@ impl std::hash::Hash for PeerAddr {
}

impl PartialEq for PeerAddr {
/// If loopback address then we care about ip and port.
/// If private address then we care about ip and port.
/// If regular address then we only care about the ip and ignore the port.
fn eq(&self, other: &PeerAddr) -> bool {
if self.0.ip().is_loopback() {
if is_private_ip(&self.0.ip()) {
self.0 == other.0
} else {
self.0.ip() == other.0.ip()
}
}
}

/// Check if IP address is private.
/// Implementation taken from `core::net:ip_addr` while `is_global` is unstable.
fn is_private_ip(ip: &IpAddr) -> bool {
Comment thread
ardocrat marked this conversation as resolved.
Outdated
match ip {
IpAddr::V4(ip) => {
ip.is_private() || ip.is_loopback() || ip.is_link_local() || ip.is_documentation()
// addresses reserved for future protocols (`192.0.0.0/24`)
// .9 and .10 are documented as globally reachable so they're excluded
|| (
ip.octets()[0] == 192 && ip.octets()[1] == 0 && ip.octets()[2] == 0
&& ip.octets()[3] != 9 && ip.octets()[3] != 10
// this address is part of the Shared Address Space defined in
// [IETF RFC 6598] (`100.64.0.0/10`).
|| ip.octets()[0] == 100 && (ip.octets()[1] & 0b1100_0000 == 0b0100_0000)
)
}
IpAddr::V6(ip) => {
ip.is_loopback() || ip.is_unspecified()
// IPv4-mapped Address (`::ffff:0:0/96`)
|| matches!(ip.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
// IPv4-IPv6 Translat. (`64:ff9b:1::/48`)
|| matches!(ip.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
// Discard-Only Address Block (`100::/64`)
|| matches!(ip.segments(), [0x100, 0, 0, 0, _, _, _, _])
// IETF Protocol Assignments (`2001::/23`)
|| (matches!(ip.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
&& !(
// Port Control Protocol Anycast (`2001:1::1`)
u128::from_be_bytes(ip.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
// Traversal Using Relays around NAT Anycast (`2001:1::2`)
|| u128::from_be_bytes(ip.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
// AMT (`2001:3::/32`)
|| matches!(ip.segments(), [0x2001, 3, _, _, _, _, _, _])
// AS112-v6 (`2001:4:112::/48`)
|| matches!(ip.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
// ORCHIDv2 (`2001:20::/28`)
// Drone Remote ID Protocol Entity Tags (DETs) Prefix (`2001:30::/28`)`
|| matches!(ip.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x3F)
))
// 6to4 (`2002::/16`) – it's not explicitly documented as globally reachable,
// IANA says N/A.
|| matches!(ip.segments(), [0x2002, _, _, _, _, _, _, _])
// Segment Routing (SRv6) SIDs (`5f00::/16`)
|| matches!(ip.segments(), [0x5f00, ..])
|| ip.is_unique_local()
|| ip.is_unicast_link_local()
}
}
}

impl Eq for PeerAddr {}

impl std::fmt::Display for PeerAddr {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
impl fmt::Display for PeerAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}

impl PeerAddr {
/// Convenient way of constructing a new peer_addr from an ip_addr
/// defaults to port 3414 on mainnet and 13414 on testnet.
/// Convenient way of constructing a new peer address from an ip address.
pub fn from_ip(addr: IpAddr) -> PeerAddr {
let port = if global::is_testnet() { 13414 } else { 3414 };
let port = if global::is_testnet() {
TESTNET_PEER_PORT
} else {
MAINNET_PEER_PORT
};
PeerAddr(SocketAddr::new(addr, port))
}

/// If the ip is loopback then our key is "ip:port" (mainly for local usernet testing).
/// Otherwise we only care about the ip (we disallow multiple peers on the same ip address).
/// If the ip is private then our key is "ip:port".
/// Otherwise, we only care about the ip (we disallow multiple peers on the same ip address).
pub fn as_key(&self) -> String {
if self.0.ip().is_loopback() {
if is_private_ip(&self.0.ip()) {
format!("{}:{}", self.0.ip(), self.0.port())
} else {
format!("{}", self.0.ip())
Expand Down Expand Up @@ -296,7 +355,7 @@ impl Default for P2PConfig {
let ipaddr = "::".parse().unwrap();
P2PConfig {
host: ipaddr,
port: 3414,
port: MAINNET_PEER_PORT,
seeding_type: Seeding::default(),
seeds: None,
peers_allow: None,
Expand All @@ -317,42 +376,31 @@ impl Default for P2PConfig {
impl P2PConfig {
/// return ban window
pub fn ban_window(&self) -> i64 {
match self.ban_window {
Some(n) => n,
None => BAN_WINDOW,
}
self.ban_window.unwrap_or_else(|| BAN_WINDOW)
}

/// return maximum inbound peer connections count
pub fn peer_max_inbound_count(&self) -> u32 {
match self.peer_max_inbound_count {
Some(n) => n,
None => PEER_MAX_INBOUND_COUNT,
}
self.peer_max_inbound_count
.unwrap_or_else(|| PEER_MAX_INBOUND_COUNT)
}

/// return maximum outbound peer connections count
pub fn peer_max_outbound_count(&self) -> u32 {
match self.peer_max_outbound_count {
Some(n) => n,
None => PEER_MAX_OUTBOUND_COUNT,
}
self.peer_max_outbound_count
.unwrap_or_else(|| PEER_MAX_OUTBOUND_COUNT)
}

/// return minimum preferred outbound peer count
pub fn peer_min_preferred_outbound_count(&self) -> u32 {
match self.peer_min_preferred_outbound_count {
Some(n) => n,
None => PEER_MIN_PREFERRED_OUTBOUND_COUNT,
}
self.peer_min_preferred_outbound_count
.unwrap_or_else(|| PEER_MIN_PREFERRED_OUTBOUND_COUNT)
}

/// return peer buffer count for listener
pub fn peer_listener_buffer_count(&self) -> u32 {
match self.peer_listener_buffer_count {
Some(n) => n,
None => PEER_LISTENER_BUFFER_COUNT,
}
self.peer_listener_buffer_count
.unwrap_or_else(|| PEER_LISTENER_BUFFER_COUNT)
}
}

Expand Down
33 changes: 30 additions & 3 deletions p2p/tests/peer_addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ use crate::p2p::types::PeerAddr;
fn test_peer_addr_hashing() {
let mut peers: HashMap<PeerAddr, String> = HashMap::new();

let socket_addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)), 8080);
let socket_addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(185, 147, 152, 14)), 8080);
let peer_addr1 = PeerAddr(socket_addr1);
peers.insert(peer_addr1, "peer1".into());

assert!(peers.contains_key(&peer_addr1));
assert_eq!(peers.len(), 1);

let socket_addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)), 8081);
let socket_addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(185, 147, 152, 14)), 8081);
let peer_addr2 = PeerAddr(socket_addr2);

// Expected behavior here is to ignore the port when hashing peer_addr.
// Expected behavior here is to ignore the port when hashing non-private peer_addr.
// This means the two peer_addr instances above are seen as the same addr.
assert!(peers.contains_key(&peer_addr1));
assert!(peers.contains_key(&peer_addr2));
Expand All @@ -52,4 +52,31 @@ fn test_peer_addr_hashing() {
assert_eq!(peer_addr2.0, socket_addr2);
assert_eq!(peer_addr1.0.port(), 8080);
assert_eq!(peer_addr2.0.port(), 8081);

peers = HashMap::new();

let socket_addr1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2)), 8080);
let peer_addr1 = PeerAddr(socket_addr1);
peers.insert(peer_addr1, "peer1".into());

assert!(peers.contains_key(&peer_addr1));
assert_eq!(peers.len(), 1);

let socket_addr2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2)), 8081);
let peer_addr2 = PeerAddr(socket_addr2);

// Expected behavior here is to not ignore the port when hashing private peer_addr.
// This means the two peer_addr instances above are seen as not the same addr.
assert!(peers.contains_key(&peer_addr1));
assert!(!peers.contains_key(&peer_addr2));

peers.insert(peer_addr2, "peer2".into());

// Inserting the second instance is a valid operation as they are treated as not the same addr.
assert!(peers.contains_key(&peer_addr1));
assert!(peers.contains_key(&peer_addr2));
assert_eq!(peers.len(), 2);

// Check they are treated as not the same even though their underlying ports are different.
assert_ne!(peer_addr1, peer_addr2);
}
Loading