Reformat the source code

This commit is contained in:
Artem Vorotnikov
2020-08-05 07:08:03 +03:00
parent 253ff3f37b
commit 610d9baba4
742 changed files with 175791 additions and 141379 deletions

View File

@@ -28,168 +28,169 @@ const PARITY_CLIENT_ID_PREFIX: &str = "Parity-Ethereum";
lazy_static! {
/// Parity versions starting from this will accept block bodies requests
/// of 256 bodies
static ref PARITY_CLIENT_LARGE_REQUESTS_VERSION: Version = Version::parse("2.4.0").unwrap();
static ref PARITY_CLIENT_LARGE_REQUESTS_VERSION: Version = Version::parse("2.4.0").unwrap();
}
/// Description of the software version running in a peer
/// according to https://github.com/ethereum/wiki/wiki/Client-Version-Strings
/// This structure as it is represents the format used by Parity clients. Other
/// vendors may provide additional fields.
#[derive(Clone,Debug,PartialEq,Eq,Serialize)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct ParityClientData {
name: String,
identity: Option<String>,
semver: Version,
os: String,
compiler: String,
name: String,
identity: Option<String>,
semver: Version,
os: String,
compiler: String,
// Capability flags, should be calculated in constructor
can_handle_large_requests: bool,
// Capability flags, should be calculated in constructor
can_handle_large_requests: bool,
}
/// Accessor methods for ParityClientData. This will probably
/// need to be abstracted away into a trait.
impl ParityClientData {
fn new(
name: String,
identity: Option<String>,
semver: Version,
os: String,
compiler: String,
) -> Self {
// Flags logic
let can_handle_large_requests = &semver >= &PARITY_CLIENT_LARGE_REQUESTS_VERSION;
fn new(
name: String,
identity: Option<String>,
semver: Version,
os: String,
compiler: String,
) -> Self {
// Flags logic
let can_handle_large_requests = &semver >= &PARITY_CLIENT_LARGE_REQUESTS_VERSION;
// Instantiate and return
ParityClientData {
name: name,
identity: identity,
semver: semver,
os: os,
compiler: compiler,
// Instantiate and return
ParityClientData {
name: name,
identity: identity,
semver: semver,
os: os,
compiler: compiler,
can_handle_large_requests: can_handle_large_requests,
}
}
can_handle_large_requests: can_handle_large_requests,
}
}
fn name(&self) -> &str {
self.name.as_str()
}
fn name(&self) -> &str {
self.name.as_str()
}
fn identity(&self) -> Option<&str> {
self.identity.as_ref().map(String::as_str)
}
fn identity(&self) -> Option<&str> {
self.identity.as_ref().map(String::as_str)
}
fn semver(&self) -> &Version {
&self.semver
}
fn semver(&self) -> &Version {
&self.semver
}
fn os(&self) -> &str {
self.os.as_str()
}
fn os(&self) -> &str {
self.os.as_str()
}
fn compiler(&self) -> &str {
self.compiler.as_str()
}
fn compiler(&self) -> &str {
self.compiler.as_str()
}
fn can_handle_large_requests(&self) -> bool {
self.can_handle_large_requests
}
fn can_handle_large_requests(&self) -> bool {
self.can_handle_large_requests
}
}
/// Enum describing the version of the software running on a peer.
#[derive(Clone,Debug,Eq,PartialEq,Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub enum ClientVersion {
/// The peer runs software from parity and the string format is known
ParityClient(
/// The actual information fields: name, version, os, ...
ParityClientData
),
/// The string ID is recognized as Parity but the overall format
/// could not be parsed
ParityUnknownFormat(String),
/// Other software vendors than Parity
Other(String),
/// The peer runs software from parity and the string format is known
ParityClient(
/// The actual information fields: name, version, os, ...
ParityClientData,
),
/// The string ID is recognized as Parity but the overall format
/// could not be parsed
ParityUnknownFormat(String),
/// Other software vendors than Parity
Other(String),
}
impl Default for ClientVersion {
fn default() -> Self {
ClientVersion::Other("".to_owned())
}
fn default() -> Self {
ClientVersion::Other("".to_owned())
}
}
/// Provide information about what a particular version of a
/// peer software can do
pub trait ClientCapabilities {
/// Parity versions before PARITY_CLIENT_LARGE_REQUESTS_VERSION would not
/// check the accumulated size of a packet when building a response to a
/// GET_BLOCK_BODIES request. If the packet was larger than a given limit,
/// instead of sending fewer blocks no packet would get sent at all. Query
/// if this version can handle requests for a large number of block bodies.
fn can_handle_large_requests(&self) -> bool;
/// Parity versions before PARITY_CLIENT_LARGE_REQUESTS_VERSION would not
/// check the accumulated size of a packet when building a response to a
/// GET_BLOCK_BODIES request. If the packet was larger than a given limit,
/// instead of sending fewer blocks no packet would get sent at all. Query
/// if this version can handle requests for a large number of block bodies.
fn can_handle_large_requests(&self) -> bool;
/// Service transactions are specific to parity. Query if this version
/// accepts them.
fn accepts_service_transaction(&self) -> bool;
/// Service transactions are specific to parity. Query if this version
/// accepts them.
fn accepts_service_transaction(&self) -> bool;
}
impl ClientCapabilities for ClientVersion {
fn can_handle_large_requests(&self) -> bool {
match self {
ClientVersion::ParityClient(data) => data.can_handle_large_requests(),
ClientVersion::ParityUnknownFormat(_) => false, // Play it safe
ClientVersion::Other(_) => true // As far as we know
}
}
fn accepts_service_transaction(&self) -> bool {
match self {
ClientVersion::ParityClient(_) => true,
ClientVersion::ParityUnknownFormat(_) => true,
ClientVersion::Other(_) => false
}
}
fn can_handle_large_requests(&self) -> bool {
match self {
ClientVersion::ParityClient(data) => data.can_handle_large_requests(),
ClientVersion::ParityUnknownFormat(_) => false, // Play it safe
ClientVersion::Other(_) => true, // As far as we know
}
}
fn accepts_service_transaction(&self) -> bool {
match self {
ClientVersion::ParityClient(_) => true,
ClientVersion::ParityUnknownFormat(_) => true,
ClientVersion::Other(_) => false,
}
}
}
fn is_parity(client_id: &str) -> bool {
client_id.starts_with(LEGACY_CLIENT_ID_PREFIX) || client_id.starts_with(PARITY_CLIENT_ID_PREFIX)
client_id.starts_with(LEGACY_CLIENT_ID_PREFIX) || client_id.starts_with(PARITY_CLIENT_ID_PREFIX)
}
/// Parse known parity formats. Recognizes either a short format with four fields
/// or a long format which includes the same fields and an identity one.
fn parse_parity_format(client_version: &str) -> Result<ParityClientData, ()> {
const PARITY_ID_STRING_MINIMUM_TOKENS: usize = 4;
const PARITY_ID_STRING_MINIMUM_TOKENS: usize = 4;
let tokens: Vec<&str> = client_version.split("/").collect();
let tokens: Vec<&str> = client_version.split("/").collect();
if tokens.len() < PARITY_ID_STRING_MINIMUM_TOKENS {
return Err(())
}
if tokens.len() < PARITY_ID_STRING_MINIMUM_TOKENS {
return Err(());
}
let name = tokens[0];
let name = tokens[0];
let identity = if tokens.len() - 3 > 1 {
Some(tokens[1..(tokens.len() - 3)].join("/"))
} else {
None
};
let identity = if tokens.len() - 3 > 1 {
Some(tokens[1..(tokens.len() - 3)].join("/"))
} else {
None
};
let compiler = tokens[tokens.len() - 1];
let os = tokens[tokens.len() - 2];
let compiler = tokens[tokens.len() - 1];
let os = tokens[tokens.len() - 2];
// If version is in the right position and valid format return a valid
// result. Otherwise return an error.
get_number_from_version(tokens[tokens.len() - 3])
.and_then(|v| Version::parse(v).ok())
.map(|semver| ParityClientData::new(
name.to_owned(),
identity,
semver,
os.to_owned(),
compiler.to_owned(),
))
.ok_or(())
// If version is in the right position and valid format return a valid
// result. Otherwise return an error.
get_number_from_version(tokens[tokens.len() - 3])
.and_then(|v| Version::parse(v).ok())
.map(|semver| {
ParityClientData::new(
name.to_owned(),
identity,
semver,
os.to_owned(),
compiler.to_owned(),
)
})
.ok_or(())
}
/// Parse a version string and return the corresponding
@@ -199,317 +200,347 @@ fn parse_parity_format(client_version: &str) -> Result<ParityClientData, ()> {
/// The parsing for parity may still fail, in which case return a ParityUnknownFormat with
/// the original version string. TryFrom would be a better trait to implement.
impl<T> From<T> for ClientVersion
where T: AsRef<str> {
fn from(client_version: T) -> Self {
let client_version_str: &str = client_version.as_ref();
where
T: AsRef<str>,
{
fn from(client_version: T) -> Self {
let client_version_str: &str = client_version.as_ref();
if !is_parity(client_version_str) {
return ClientVersion::Other(client_version_str.to_owned());
}
if !is_parity(client_version_str) {
return ClientVersion::Other(client_version_str.to_owned());
}
if let Ok(data) = parse_parity_format(client_version_str) {
ClientVersion::ParityClient(data)
} else {
ClientVersion::ParityUnknownFormat(client_version_str.to_owned())
}
}
if let Ok(data) = parse_parity_format(client_version_str) {
ClientVersion::ParityClient(data)
} else {
ClientVersion::ParityUnknownFormat(client_version_str.to_owned())
}
}
}
fn format_parity_version_string(client_version: &ParityClientData, f: &mut fmt::Formatter) -> std::fmt::Result {
let name = client_version.name();
let semver = client_version.semver();
let os = client_version.os();
let compiler = client_version.compiler();
fn format_parity_version_string(
client_version: &ParityClientData,
f: &mut fmt::Formatter,
) -> std::fmt::Result {
let name = client_version.name();
let semver = client_version.semver();
let os = client_version.os();
let compiler = client_version.compiler();
match client_version.identity() {
None => write!(f, "{}/v{}/{}/{}", name, semver, os, compiler),
Some(identity) => write!(f, "{}/{}/v{}/{}/{}", name, identity, semver, os, compiler),
}
match client_version.identity() {
None => write!(f, "{}/v{}/{}/{}", name, semver, os, compiler),
Some(identity) => write!(f, "{}/{}/v{}/{}/{}", name, identity, semver, os, compiler),
}
}
impl fmt::Display for ClientVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
match self {
ClientVersion::ParityClient(data) => format_parity_version_string(data, f),
ClientVersion::ParityUnknownFormat(id) => write!(f, "{}", id),
ClientVersion::Other(id) => write!(f, "{}", id)
}
}
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
match self {
ClientVersion::ParityClient(data) => format_parity_version_string(data, f),
ClientVersion::ParityUnknownFormat(id) => write!(f, "{}", id),
ClientVersion::Other(id) => write!(f, "{}", id),
}
}
}
fn get_number_from_version(version: &str) -> Option<&str> {
if version.starts_with("v") {
return version.get(1..);
}
if version.starts_with("v") {
return version.get(1..);
}
None
None
}
#[cfg(test)]
pub mod tests {
use super::*;
use super::*;
const PARITY_CLIENT_SEMVER: &str = "2.4.0";
const PARITY_CLIENT_OLD_SEMVER: &str = "2.2.0";
const PARITY_CLIENT_OS: &str = "linux";
const PARITY_CLIENT_COMPILER: &str = "rustc";
const PARITY_CLIENT_IDENTITY: &str = "ExpanseSOLO";
const PARITY_CLIENT_MULTITOKEN_IDENTITY: &str = "ExpanseSOLO/abc/v1.2.3";
const PARITY_CLIENT_SEMVER: &str = "2.4.0";
const PARITY_CLIENT_OLD_SEMVER: &str = "2.2.0";
const PARITY_CLIENT_OS: &str = "linux";
const PARITY_CLIENT_COMPILER: &str = "rustc";
const PARITY_CLIENT_IDENTITY: &str = "ExpanseSOLO";
const PARITY_CLIENT_MULTITOKEN_IDENTITY: &str = "ExpanseSOLO/abc/v1.2.3";
fn make_default_version_string() -> String {
format!(
"{}/v{}/{}/{}",
PARITY_CLIENT_ID_PREFIX, PARITY_CLIENT_SEMVER, PARITY_CLIENT_OS, PARITY_CLIENT_COMPILER
)
}
fn make_default_version_string() -> String {
format!(
"{}/v{}/{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_SEMVER,
PARITY_CLIENT_OS,
PARITY_CLIENT_COMPILER
)
}
fn make_default_long_version_string() -> String {
format!(
"{}/{}/v{}/{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_IDENTITY,
PARITY_CLIENT_SEMVER,
PARITY_CLIENT_OS,
PARITY_CLIENT_COMPILER
)
}
fn make_default_long_version_string() -> String {
format!(
"{}/{}/v{}/{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_IDENTITY,
PARITY_CLIENT_SEMVER,
PARITY_CLIENT_OS,
PARITY_CLIENT_COMPILER
)
}
fn make_multitoken_identity_long_version_string() -> String {
format!(
"{}/{}/v{}/{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_MULTITOKEN_IDENTITY,
PARITY_CLIENT_SEMVER,
PARITY_CLIENT_OS,
PARITY_CLIENT_COMPILER
)
}
fn make_multitoken_identity_long_version_string() -> String {
format!(
"{}/{}/v{}/{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_MULTITOKEN_IDENTITY,
PARITY_CLIENT_SEMVER,
PARITY_CLIENT_OS,
PARITY_CLIENT_COMPILER
)
}
fn make_old_semver_version_string() -> String {
format!(
"{}/v{}/{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_OLD_SEMVER,
PARITY_CLIENT_OS,
PARITY_CLIENT_COMPILER
)
}
fn make_old_semver_version_string() -> String {
format!(
"{}/v{}/{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_OLD_SEMVER,
PARITY_CLIENT_OS,
PARITY_CLIENT_COMPILER
)
}
#[test]
pub fn client_version_when_from_empty_string_then_default() {
let default = ClientVersion::default();
#[test]
pub fn client_version_when_from_empty_string_then_default() {
let default = ClientVersion::default();
assert_eq!(ClientVersion::from(""), default);
}
assert_eq!(ClientVersion::from(""), default);
}
#[test]
pub fn get_number_from_version_when_valid_then_number() {
let version_string = format!("v{}", PARITY_CLIENT_SEMVER);
#[test]
pub fn get_number_from_version_when_valid_then_number() {
let version_string = format!("v{}", PARITY_CLIENT_SEMVER);
assert_eq!(
get_number_from_version(&version_string).unwrap(),
PARITY_CLIENT_SEMVER
);
}
assert_eq!(get_number_from_version(&version_string).unwrap(), PARITY_CLIENT_SEMVER);
}
#[test]
pub fn client_version_when_str_parity_format_and_valid_then_all_fields_match() {
let client_version_string = make_default_version_string();
#[test]
pub fn client_version_when_str_parity_format_and_valid_then_all_fields_match() {
let client_version_string = make_default_version_string();
if let ClientVersion::ParityClient(client_version) =
ClientVersion::from(client_version_string.as_str())
{
assert_eq!(client_version.name(), PARITY_CLIENT_ID_PREFIX);
assert_eq!(
*client_version.semver(),
Version::parse(PARITY_CLIENT_SEMVER).unwrap()
);
assert_eq!(client_version.os(), PARITY_CLIENT_OS);
assert_eq!(client_version.compiler(), PARITY_CLIENT_COMPILER);
} else {
panic!("shouldn't be here");
}
}
if let ClientVersion::ParityClient(client_version) = ClientVersion::from(client_version_string.as_str()) {
assert_eq!(client_version.name(), PARITY_CLIENT_ID_PREFIX);
assert_eq!(*client_version.semver(), Version::parse(PARITY_CLIENT_SEMVER).unwrap());
assert_eq!(client_version.os(), PARITY_CLIENT_OS);
assert_eq!(client_version.compiler(), PARITY_CLIENT_COMPILER);
} else {
panic!("shouldn't be here");
}
}
#[test]
pub fn client_version_when_str_parity_long_format_and_valid_then_all_fields_match() {
let client_version_string = make_default_long_version_string();
#[test]
pub fn client_version_when_str_parity_long_format_and_valid_then_all_fields_match() {
let client_version_string = make_default_long_version_string();
if let ClientVersion::ParityClient(client_version) =
ClientVersion::from(client_version_string.as_str())
{
assert_eq!(client_version.name(), PARITY_CLIENT_ID_PREFIX);
assert_eq!(client_version.identity().unwrap(), PARITY_CLIENT_IDENTITY);
assert_eq!(
*client_version.semver(),
Version::parse(PARITY_CLIENT_SEMVER).unwrap()
);
assert_eq!(client_version.os(), PARITY_CLIENT_OS);
assert_eq!(client_version.compiler(), PARITY_CLIENT_COMPILER);
} else {
panic!("shouldnt be here");
}
}
if let ClientVersion::ParityClient(client_version) = ClientVersion::from(client_version_string.as_str()) {
assert_eq!(client_version.name(), PARITY_CLIENT_ID_PREFIX);
assert_eq!(client_version.identity().unwrap(), PARITY_CLIENT_IDENTITY);
assert_eq!(*client_version.semver(), Version::parse(PARITY_CLIENT_SEMVER).unwrap());
assert_eq!(client_version.os(), PARITY_CLIENT_OS);
assert_eq!(client_version.compiler(), PARITY_CLIENT_COMPILER);
} else {
panic!("shouldnt be here");
}
}
#[test]
pub fn client_version_when_str_parity_long_format_and_valid_and_identity_multiple_tokens_then_all_fields_match(
) {
let client_version_string = make_multitoken_identity_long_version_string();
#[test]
pub fn client_version_when_str_parity_long_format_and_valid_and_identity_multiple_tokens_then_all_fields_match() {
let client_version_string = make_multitoken_identity_long_version_string();
if let ClientVersion::ParityClient(client_version) =
ClientVersion::from(client_version_string.as_str())
{
assert_eq!(client_version.name(), PARITY_CLIENT_ID_PREFIX);
assert_eq!(
client_version.identity().unwrap(),
PARITY_CLIENT_MULTITOKEN_IDENTITY
);
assert_eq!(
*client_version.semver(),
Version::parse(PARITY_CLIENT_SEMVER).unwrap()
);
assert_eq!(client_version.os(), PARITY_CLIENT_OS);
assert_eq!(client_version.compiler(), PARITY_CLIENT_COMPILER);
} else {
panic!("shouldnt be here");
}
}
if let ClientVersion::ParityClient(client_version) = ClientVersion::from(client_version_string.as_str()) {
assert_eq!(client_version.name(), PARITY_CLIENT_ID_PREFIX);
assert_eq!(client_version.identity().unwrap(), PARITY_CLIENT_MULTITOKEN_IDENTITY);
assert_eq!(*client_version.semver(), Version::parse(PARITY_CLIENT_SEMVER).unwrap());
assert_eq!(client_version.os(), PARITY_CLIENT_OS);
assert_eq!(client_version.compiler(), PARITY_CLIENT_COMPILER);
} else {
panic!("shouldnt be here");
}
}
#[test]
pub fn client_version_when_string_parity_format_and_valid_then_all_fields_match() {
let client_version_string: String = make_default_version_string();
#[test]
pub fn client_version_when_string_parity_format_and_valid_then_all_fields_match() {
let client_version_string: String = make_default_version_string();
if let ClientVersion::ParityClient(client_version) =
ClientVersion::from(client_version_string.as_str())
{
assert_eq!(client_version.name(), PARITY_CLIENT_ID_PREFIX);
assert_eq!(
*client_version.semver(),
Version::parse(PARITY_CLIENT_SEMVER).unwrap()
);
assert_eq!(client_version.os(), PARITY_CLIENT_OS);
assert_eq!(client_version.compiler(), PARITY_CLIENT_COMPILER);
} else {
panic!("shouldn't be here");
}
}
if let ClientVersion::ParityClient(client_version) = ClientVersion::from(client_version_string.as_str()) {
assert_eq!(client_version.name(), PARITY_CLIENT_ID_PREFIX);
assert_eq!(*client_version.semver(), Version::parse(PARITY_CLIENT_SEMVER).unwrap());
assert_eq!(client_version.os(), PARITY_CLIENT_OS);
assert_eq!(client_version.compiler(), PARITY_CLIENT_COMPILER);
} else {
panic!("shouldn't be here");
}
}
#[test]
pub fn client_version_when_parity_format_and_invalid_then_equals_parity_unknown_client_version_string(
) {
// This is invalid because version has no leading 'v'
let client_version_string = format!(
"{}/{}/{}/{}",
PARITY_CLIENT_ID_PREFIX, PARITY_CLIENT_SEMVER, PARITY_CLIENT_OS, PARITY_CLIENT_COMPILER
);
#[test]
pub fn client_version_when_parity_format_and_invalid_then_equals_parity_unknown_client_version_string() {
// This is invalid because version has no leading 'v'
let client_version_string = format!(
"{}/{}/{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_SEMVER,
PARITY_CLIENT_OS,
PARITY_CLIENT_COMPILER);
let client_version = ClientVersion::from(client_version_string.as_str());
let client_version = ClientVersion::from(client_version_string.as_str());
let parity_unknown = ClientVersion::ParityUnknownFormat(client_version_string.to_string());
let parity_unknown = ClientVersion::ParityUnknownFormat(client_version_string.to_string());
assert_eq!(client_version, parity_unknown);
}
assert_eq!(client_version, parity_unknown);
}
#[test]
pub fn client_version_when_parity_format_without_identity_and_missing_compiler_field_then_equals_parity_unknown_client_version_string(
) {
let client_version_string = format!(
"{}/v{}/{}",
PARITY_CLIENT_ID_PREFIX, PARITY_CLIENT_SEMVER, PARITY_CLIENT_OS,
);
#[test]
pub fn client_version_when_parity_format_without_identity_and_missing_compiler_field_then_equals_parity_unknown_client_version_string() {
let client_version_string = format!(
"{}/v{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_SEMVER,
PARITY_CLIENT_OS,
);
let client_version = ClientVersion::from(client_version_string.as_str());
let client_version = ClientVersion::from(client_version_string.as_str());
let parity_unknown = ClientVersion::ParityUnknownFormat(client_version_string.to_string());
let parity_unknown = ClientVersion::ParityUnknownFormat(client_version_string.to_string());
assert_eq!(client_version, parity_unknown);
}
assert_eq!(client_version, parity_unknown);
}
#[test]
pub fn client_version_when_parity_format_with_identity_and_missing_compiler_field_then_equals_parity_unknown_client_version_string(
) {
let client_version_string = format!(
"{}/{}/v{}/{}",
PARITY_CLIENT_ID_PREFIX, PARITY_CLIENT_IDENTITY, PARITY_CLIENT_SEMVER, PARITY_CLIENT_OS,
);
#[test]
pub fn client_version_when_parity_format_with_identity_and_missing_compiler_field_then_equals_parity_unknown_client_version_string() {
let client_version_string = format!(
"{}/{}/v{}/{}",
PARITY_CLIENT_ID_PREFIX,
PARITY_CLIENT_IDENTITY,
PARITY_CLIENT_SEMVER,
PARITY_CLIENT_OS,
);
let client_version = ClientVersion::from(client_version_string.as_str());
let client_version = ClientVersion::from(client_version_string.as_str());
let parity_unknown = ClientVersion::ParityUnknownFormat(client_version_string.to_string());
let parity_unknown = ClientVersion::ParityUnknownFormat(client_version_string.to_string());
assert_eq!(client_version, parity_unknown);
}
assert_eq!(client_version, parity_unknown);
}
#[test]
pub fn client_version_when_not_parity_format_and_valid_then_other_with_client_version_string() {
let client_version_string = "Geth/main.jnode.network/v1.8.21-stable-9dc5d1a9/linux";
#[test]
pub fn client_version_when_not_parity_format_and_valid_then_other_with_client_version_string() {
let client_version_string = "Geth/main.jnode.network/v1.8.21-stable-9dc5d1a9/linux";
let client_version = ClientVersion::from(client_version_string);
let client_version = ClientVersion::from(client_version_string);
assert_eq!(
client_version,
ClientVersion::Other(client_version_string.to_string())
);
}
assert_eq!(client_version, ClientVersion::Other(client_version_string.to_string()));
}
#[test]
pub fn client_version_when_parity_format_and_valid_then_to_string_equal() {
let client_version_string: String = make_default_version_string();
#[test]
pub fn client_version_when_parity_format_and_valid_then_to_string_equal() {
let client_version_string: String = make_default_version_string();
let client_version = ClientVersion::from(client_version_string.as_str());
let client_version = ClientVersion::from(client_version_string.as_str());
assert_eq!(client_version.to_string(), client_version_string);
}
assert_eq!(client_version.to_string(), client_version_string);
}
#[test]
pub fn client_version_when_other_then_to_string_equal_input_string() {
let client_version_string: String = "Other".to_string();
#[test]
pub fn client_version_when_other_then_to_string_equal_input_string() {
let client_version_string: String = "Other".to_string();
let client_version = ClientVersion::from("Other");
let client_version = ClientVersion::from("Other");
assert_eq!(client_version.to_string(), client_version_string);
}
assert_eq!(client_version.to_string(), client_version_string);
}
#[test]
pub fn client_capabilities_when_parity_old_version_then_handles_large_requests_false() {
let client_version_string: String = make_old_semver_version_string();
#[test]
pub fn client_capabilities_when_parity_old_version_then_handles_large_requests_false() {
let client_version_string: String = make_old_semver_version_string();
let client_version = ClientVersion::from(client_version_string.as_str());
let client_version = ClientVersion::from(client_version_string.as_str());
assert!(!client_version.can_handle_large_requests());
}
assert!(!client_version.can_handle_large_requests());
}
#[test]
pub fn client_capabilities_when_parity_beta_version_then_not_handles_large_requests_true() {
let client_version_string: String = format!(
"{}/v{}/{}/{}",
"Parity-Ethereum", "2.4.0-beta", "x86_64-linux-gnu", "rustc1.31.1"
)
.to_string();
#[test]
pub fn client_capabilities_when_parity_beta_version_then_not_handles_large_requests_true() {
let client_version_string: String = format!(
"{}/v{}/{}/{}",
"Parity-Ethereum",
"2.4.0-beta",
"x86_64-linux-gnu",
"rustc1.31.1")
.to_string();
let client_version = ClientVersion::from(client_version_string.as_str());
let client_version = ClientVersion::from(client_version_string.as_str());
assert!(!client_version.can_handle_large_requests());
}
assert!(!client_version.can_handle_large_requests());
}
#[test]
pub fn client_version_when_to_owned_then_both_objects_equal() {
let client_version_string: String = make_old_semver_version_string();
#[test]
pub fn client_version_when_to_owned_then_both_objects_equal() {
let client_version_string: String = make_old_semver_version_string();
let origin = ClientVersion::from(client_version_string.as_str());
let origin = ClientVersion::from(client_version_string.as_str());
let borrowed = &origin;
let borrowed = &origin;
let owned = origin.to_owned();
let owned = origin.to_owned();
assert_eq!(*borrowed, owned);
}
assert_eq!(*borrowed, owned);
}
#[test]
fn client_version_accepts_service_transaction_for_different_versions() {
assert!(!ClientVersion::from("Geth").accepts_service_transaction());
assert!(
ClientVersion::from("Parity-Ethereum/v2.6.0/linux/rustc").accepts_service_transaction()
);
assert!(
ClientVersion::from("Parity-Ethereum/ABCDEFGH/v2.7.3/linux/rustc")
.accepts_service_transaction()
);
}
#[test]
fn client_version_accepts_service_transaction_for_different_versions() {
assert!(!ClientVersion::from("Geth").accepts_service_transaction());
assert!(ClientVersion::from("Parity-Ethereum/v2.6.0/linux/rustc").accepts_service_transaction());
assert!(ClientVersion::from("Parity-Ethereum/ABCDEFGH/v2.7.3/linux/rustc").accepts_service_transaction());
}
#[test]
fn is_parity_when_parity_then_true() {
let client_id = format!("{}/", PARITY_CLIENT_ID_PREFIX);
#[test]
fn is_parity_when_parity_then_true() {
let client_id = format!("{}/", PARITY_CLIENT_ID_PREFIX);
assert!(is_parity(&client_id));
}
assert!(is_parity(&client_id));
}
#[test]
fn is_parity_when_empty_then_false() {
let client_id = "";
#[test]
fn is_parity_when_empty_then_false() {
let client_id = "";
assert!(!is_parity(&client_id));
}
assert!(!is_parity(&client_id));
}
#[test]
fn is_parity_when_other_then_false() {
let client_id = "other";
#[test]
fn is_parity_when_other_then_false() {
let client_id = "other";
assert!(!is_parity(&client_id));
}
assert!(!is_parity(&client_id));
}
}

View File

@@ -20,12 +20,17 @@ use super::NodeId;
/// Filtered connection direction.
pub enum ConnectionDirection {
Inbound,
Outbound,
Inbound,
Outbound,
}
/// Connection filter. Each connection is checked against `connection_allowed`.
pub trait ConnectionFilter : Send + Sync {
/// Filter a connection. Returns `true` if connection should be allowed. `false` if rejected.
fn connection_allowed(&self, own_id: &NodeId, connecting_id: &NodeId, direction: ConnectionDirection) -> bool;
pub trait ConnectionFilter: Send + Sync {
/// Filter a connection. Returns `true` if connection should be allowed. `false` if rejected.
fn connection_allowed(
&self,
own_id: &NodeId,
connecting_id: &NodeId,
direction: ConnectionDirection,
) -> bool;
}

View File

@@ -18,226 +18,229 @@
// https://github.com/paritytech/parity-ethereum/issues/10302
#![allow(deprecated)]
use std::{io, net, fmt};
use libc::{ENFILE, EMFILE};
use crypto;
use ethkey;
use io::IoError;
use {rlp, ethkey, crypto, snappy};
use libc::{EMFILE, ENFILE};
use rlp;
use snappy;
use std::{fmt, io, net};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DisconnectReason
{
DisconnectRequested,
TCPError,
BadProtocol,
UselessPeer,
TooManyPeers,
DuplicatePeer,
IncompatibleProtocol,
NullIdentity,
ClientQuit,
UnexpectedIdentity,
LocalIdentity,
PingTimeout,
Unknown,
pub enum DisconnectReason {
DisconnectRequested,
TCPError,
BadProtocol,
UselessPeer,
TooManyPeers,
DuplicatePeer,
IncompatibleProtocol,
NullIdentity,
ClientQuit,
UnexpectedIdentity,
LocalIdentity,
PingTimeout,
Unknown,
}
impl DisconnectReason {
pub fn from_u8(n: u8) -> DisconnectReason {
match n {
0 => DisconnectReason::DisconnectRequested,
1 => DisconnectReason::TCPError,
2 => DisconnectReason::BadProtocol,
3 => DisconnectReason::UselessPeer,
4 => DisconnectReason::TooManyPeers,
5 => DisconnectReason::DuplicatePeer,
6 => DisconnectReason::IncompatibleProtocol,
7 => DisconnectReason::NullIdentity,
8 => DisconnectReason::ClientQuit,
9 => DisconnectReason::UnexpectedIdentity,
10 => DisconnectReason::LocalIdentity,
11 => DisconnectReason::PingTimeout,
_ => DisconnectReason::Unknown,
}
}
pub fn from_u8(n: u8) -> DisconnectReason {
match n {
0 => DisconnectReason::DisconnectRequested,
1 => DisconnectReason::TCPError,
2 => DisconnectReason::BadProtocol,
3 => DisconnectReason::UselessPeer,
4 => DisconnectReason::TooManyPeers,
5 => DisconnectReason::DuplicatePeer,
6 => DisconnectReason::IncompatibleProtocol,
7 => DisconnectReason::NullIdentity,
8 => DisconnectReason::ClientQuit,
9 => DisconnectReason::UnexpectedIdentity,
10 => DisconnectReason::LocalIdentity,
11 => DisconnectReason::PingTimeout,
_ => DisconnectReason::Unknown,
}
}
}
impl fmt::Display for DisconnectReason {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::DisconnectReason::*;
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::DisconnectReason::*;
let msg = match *self {
DisconnectRequested => "disconnect requested",
TCPError => "TCP error",
BadProtocol => "bad protocol",
UselessPeer => "useless peer",
TooManyPeers => "too many peers",
DuplicatePeer => "duplicate peer",
IncompatibleProtocol => "incompatible protocol",
NullIdentity => "null identity",
ClientQuit => "client quit",
UnexpectedIdentity => "unexpected identity",
LocalIdentity => "local identity",
PingTimeout => "ping timeout",
Unknown => "unknown",
};
let msg = match *self {
DisconnectRequested => "disconnect requested",
TCPError => "TCP error",
BadProtocol => "bad protocol",
UselessPeer => "useless peer",
TooManyPeers => "too many peers",
DuplicatePeer => "duplicate peer",
IncompatibleProtocol => "incompatible protocol",
NullIdentity => "null identity",
ClientQuit => "client quit",
UnexpectedIdentity => "unexpected identity",
LocalIdentity => "local identity",
PingTimeout => "ping timeout",
Unknown => "unknown",
};
f.write_str(msg)
}
f.write_str(msg)
}
}
error_chain! {
foreign_links {
SocketIo(IoError) #[doc = "Socket IO error."];
Decompression(snappy::InvalidInput) #[doc = "Decompression error."];
Rlp(rlp::DecoderError) #[doc = "Rlp decoder error."];
}
foreign_links {
SocketIo(IoError) #[doc = "Socket IO error."];
Decompression(snappy::InvalidInput) #[doc = "Decompression error."];
Rlp(rlp::DecoderError) #[doc = "Rlp decoder error."];
}
errors {
#[doc = "Error concerning the network address parsing subsystem."]
AddressParse {
description("Failed to parse network address"),
display("Failed to parse network address"),
}
errors {
#[doc = "Error concerning the network address parsing subsystem."]
AddressParse {
description("Failed to parse network address"),
display("Failed to parse network address"),
}
#[doc = "Error concerning the network address resolution subsystem."]
AddressResolve(err: Option<io::Error>) {
description("Failed to resolve network address"),
display("Failed to resolve network address {}", err.as_ref().map_or("".to_string(), |e| e.to_string())),
}
#[doc = "Error concerning the network address resolution subsystem."]
AddressResolve(err: Option<io::Error>) {
description("Failed to resolve network address"),
display("Failed to resolve network address {}", err.as_ref().map_or("".to_string(), |e| e.to_string())),
}
#[doc = "Authentication failure"]
Auth {
description("Authentication failure"),
display("Authentication failure"),
}
#[doc = "Authentication failure"]
Auth {
description("Authentication failure"),
display("Authentication failure"),
}
#[doc = "Unrecognised protocol"]
BadProtocol {
description("Bad protocol"),
display("Bad protocol"),
}
#[doc = "Unrecognised protocol"]
BadProtocol {
description("Bad protocol"),
display("Bad protocol"),
}
#[doc = "Expired message"]
Expired {
description("Expired message"),
display("Expired message"),
}
#[doc = "Expired message"]
Expired {
description("Expired message"),
display("Expired message"),
}
#[doc = "Peer not found"]
PeerNotFound {
description("Peer not found"),
display("Peer not found"),
}
#[doc = "Peer not found"]
PeerNotFound {
description("Peer not found"),
display("Peer not found"),
}
#[doc = "Peer is disconnected"]
Disconnect(reason: DisconnectReason) {
description("Peer disconnected"),
display("Peer disconnected: {}", reason),
}
#[doc = "Peer is disconnected"]
Disconnect(reason: DisconnectReason) {
description("Peer disconnected"),
display("Peer disconnected: {}", reason),
}
#[doc = "Invalid node id"]
InvalidNodeId {
description("Invalid node id"),
display("Invalid node id"),
}
#[doc = "Invalid node id"]
InvalidNodeId {
description("Invalid node id"),
display("Invalid node id"),
}
#[doc = "Packet size is over the protocol limit"]
OversizedPacket {
description("Packet is too large"),
display("Packet is too large"),
}
#[doc = "Packet size is over the protocol limit"]
OversizedPacket {
description("Packet is too large"),
display("Packet is too large"),
}
#[doc = "Reached system resource limits for this process"]
ProcessTooManyFiles {
description("Too many open files in process."),
display("Too many open files in this process. Check your resource limits and restart parity"),
}
#[doc = "Reached system resource limits for this process"]
ProcessTooManyFiles {
description("Too many open files in process."),
display("Too many open files in this process. Check your resource limits and restart parity"),
}
#[doc = "Reached system wide resource limits"]
SystemTooManyFiles {
description("Too many open files on system."),
display("Too many open files on system. Consider closing some processes/release some file handlers or increas the system-wide resource limits and restart parity."),
}
#[doc = "Reached system wide resource limits"]
SystemTooManyFiles {
description("Too many open files on system."),
display("Too many open files on system. Consider closing some processes/release some file handlers or increas the system-wide resource limits and restart parity."),
}
#[doc = "An unknown IO error occurred."]
Io(err: io::Error) {
description("IO Error"),
display("Unexpected IO error: {}", err),
}
}
#[doc = "An unknown IO error occurred."]
Io(err: io::Error) {
description("IO Error"),
display("Unexpected IO error: {}", err),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
match err.raw_os_error() {
Some(ENFILE) => ErrorKind::ProcessTooManyFiles.into(),
Some(EMFILE) => ErrorKind::SystemTooManyFiles.into(),
_ => Error::from_kind(ErrorKind::Io(err))
}
}
fn from(err: io::Error) -> Self {
match err.raw_os_error() {
Some(ENFILE) => ErrorKind::ProcessTooManyFiles.into(),
Some(EMFILE) => ErrorKind::SystemTooManyFiles.into(),
_ => Error::from_kind(ErrorKind::Io(err)),
}
}
}
impl From<ethkey::Error> for Error {
fn from(_err: ethkey::Error) -> Self {
ErrorKind::Auth.into()
}
fn from(_err: ethkey::Error) -> Self {
ErrorKind::Auth.into()
}
}
impl From<ethkey::crypto::Error> for Error {
fn from(_err: ethkey::crypto::Error) -> Self {
ErrorKind::Auth.into()
}
fn from(_err: ethkey::crypto::Error) -> Self {
ErrorKind::Auth.into()
}
}
impl From<crypto::error::SymmError> for Error {
fn from(_err: crypto::error::SymmError) -> Self {
ErrorKind::Auth.into()
}
fn from(_err: crypto::error::SymmError) -> Self {
ErrorKind::Auth.into()
}
}
impl From<net::AddrParseError> for Error {
fn from(_err: net::AddrParseError) -> Self { ErrorKind::AddressParse.into() }
fn from(_err: net::AddrParseError) -> Self {
ErrorKind::AddressParse.into()
}
}
#[test]
fn test_errors() {
assert_eq!(DisconnectReason::ClientQuit, DisconnectReason::from_u8(8));
let mut r = DisconnectReason::DisconnectRequested;
for i in 0 .. 20 {
r = DisconnectReason::from_u8(i);
}
assert_eq!(DisconnectReason::Unknown, r);
assert_eq!(DisconnectReason::ClientQuit, DisconnectReason::from_u8(8));
let mut r = DisconnectReason::DisconnectRequested;
for i in 0..20 {
r = DisconnectReason::from_u8(i);
}
assert_eq!(DisconnectReason::Unknown, r);
match *<Error as From<rlp::DecoderError>>::from(rlp::DecoderError::RlpIsTooBig).kind() {
ErrorKind::Rlp(_) => {},
_ => panic!("Unexpected error"),
}
match *<Error as From<rlp::DecoderError>>::from(rlp::DecoderError::RlpIsTooBig).kind() {
ErrorKind::Rlp(_) => {}
_ => panic!("Unexpected error"),
}
match *<Error as From<ethkey::crypto::Error>>::from(ethkey::crypto::Error::InvalidMessage).kind() {
ErrorKind::Auth => {},
_ => panic!("Unexpected error"),
}
match *<Error as From<ethkey::crypto::Error>>::from(ethkey::crypto::Error::InvalidMessage)
.kind()
{
ErrorKind::Auth => {}
_ => panic!("Unexpected error"),
}
}
#[test]
fn test_io_errors() {
use libc::{EMFILE, ENFILE};
use libc::{EMFILE, ENFILE};
assert_matches!(
<Error as From<io::Error>>::from(
io::Error::from_raw_os_error(ENFILE)
).kind(),
ErrorKind::ProcessTooManyFiles);
assert_matches!(
<Error as From<io::Error>>::from(io::Error::from_raw_os_error(ENFILE)).kind(),
ErrorKind::ProcessTooManyFiles
);
assert_matches!(
<Error as From<io::Error>>::from(
io::Error::from_raw_os_error(EMFILE)
).kind(),
ErrorKind::SystemTooManyFiles);
assert_matches!(
<Error as From<io::Error>>::from(io::Error::from_raw_os_error(EMFILE)).kind(),
ErrorKind::SystemTooManyFiles
);
assert_matches!(
<Error as From<io::Error>>::from(
io::Error::from_raw_os_error(0)
).kind(),
ErrorKind::Io(_));
assert_matches!(
<Error as From<io::Error>>::from(io::Error::from_raw_os_error(0)).kind(),
ErrorKind::Io(_)
);
}

View File

@@ -14,23 +14,24 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
#![recursion_limit="128"]
#![recursion_limit = "128"]
extern crate parity_crypto as crypto;
extern crate ethcore_io as io;
extern crate ethereum_types;
extern crate ethkey;
extern crate rlp;
extern crate ipnetwork;
extern crate parity_snappy as snappy;
extern crate libc;
extern crate parity_crypto as crypto;
extern crate parity_snappy as snappy;
extern crate rlp;
extern crate semver;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[cfg(test)] #[macro_use]
#[cfg(test)]
#[macro_use]
extern crate assert_matches;
#[macro_use]
@@ -44,21 +45,23 @@ pub mod client_version;
mod connection_filter;
mod error;
pub use connection_filter::{ConnectionFilter, ConnectionDirection};
pub use connection_filter::{ConnectionDirection, ConnectionFilter};
pub use error::{DisconnectReason, Error, ErrorKind};
pub use io::TimerToken;
pub use error::{Error, ErrorKind, DisconnectReason};
use client_version::ClientVersion;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
use std::str::{self, FromStr};
use std::sync::Arc;
use std::time::Duration;
use ipnetwork::{IpNetwork, IpNetworkError};
use ethkey::Secret;
use ethereum_types::H512;
use ethkey::Secret;
use ipnetwork::{IpNetwork, IpNetworkError};
use rlp::{Decodable, DecoderError, Rlp};
use std::{
cmp::Ordering,
collections::HashMap,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
str::{self, FromStr},
sync::Arc,
time::Duration,
};
/// Protocol handler level packet id
pub type PacketId = u8;
@@ -74,324 +77,351 @@ pub type PeerId = usize;
/// Messages used to communitate with the event loop from other threads.
#[derive(Clone)]
pub enum NetworkIoMessage {
/// Register a new protocol handler.
AddHandler {
/// Handler shared instance.
handler: Arc<NetworkProtocolHandler + Sync>,
/// Protocol Id.
protocol: ProtocolId,
/// Supported protocol versions and number of packet IDs reserved by the protocol (packet count).
versions: Vec<(u8, u8)>,
},
/// Register a new protocol timer
AddTimer {
/// Protocol Id.
protocol: ProtocolId,
/// Timer token.
token: TimerToken,
/// Timer delay.
delay: Duration,
},
/// Initliaze public interface.
InitPublicInterface,
/// Disconnect a peer.
Disconnect(PeerId),
/// Disconnect and temporary disable peer.
DisablePeer(PeerId),
/// Network has been started with the host as the given enode.
NetworkStarted(String),
/// Register a new protocol handler.
AddHandler {
/// Handler shared instance.
handler: Arc<NetworkProtocolHandler + Sync>,
/// Protocol Id.
protocol: ProtocolId,
/// Supported protocol versions and number of packet IDs reserved by the protocol (packet count).
versions: Vec<(u8, u8)>,
},
/// Register a new protocol timer
AddTimer {
/// Protocol Id.
protocol: ProtocolId,
/// Timer token.
token: TimerToken,
/// Timer delay.
delay: Duration,
},
/// Initliaze public interface.
InitPublicInterface,
/// Disconnect a peer.
Disconnect(PeerId),
/// Disconnect and temporary disable peer.
DisablePeer(PeerId),
/// Network has been started with the host as the given enode.
NetworkStarted(String),
}
/// Shared session information
#[derive(Debug, Clone)]
pub struct SessionInfo {
/// Peer public key
pub id: Option<NodeId>,
/// Peer client ID
pub client_version: ClientVersion,
/// Peer RLPx protocol version
pub protocol_version: u32,
/// Session protocol capabilities
pub capabilities: Vec<SessionCapabilityInfo>,
/// Peer protocol capabilities
pub peer_capabilities: Vec<PeerCapabilityInfo>,
/// Peer ping delay
pub ping: Option<Duration>,
/// True if this session was originated by us.
pub originated: bool,
/// Remote endpoint address of the session
pub remote_address: String,
/// Local endpoint address of the session
pub local_address: String,
/// Peer public key
pub id: Option<NodeId>,
/// Peer client ID
pub client_version: ClientVersion,
/// Peer RLPx protocol version
pub protocol_version: u32,
/// Session protocol capabilities
pub capabilities: Vec<SessionCapabilityInfo>,
/// Peer protocol capabilities
pub peer_capabilities: Vec<PeerCapabilityInfo>,
/// Peer ping delay
pub ping: Option<Duration>,
/// True if this session was originated by us.
pub originated: bool,
/// Remote endpoint address of the session
pub remote_address: String,
/// Local endpoint address of the session
pub local_address: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerCapabilityInfo {
pub protocol: ProtocolId,
pub version: u8,
pub protocol: ProtocolId,
pub version: u8,
}
impl Decodable for PeerCapabilityInfo {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let p: Vec<u8> = rlp.val_at(0)?;
if p.len() != 3 {
return Err(DecoderError::Custom("Invalid subprotocol string length. Should be 3"));
}
let mut p2: ProtocolId = [0u8; 3];
p2.clone_from_slice(&p);
Ok(PeerCapabilityInfo {
protocol: p2,
version: rlp.val_at(1)?
})
}
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let p: Vec<u8> = rlp.val_at(0)?;
if p.len() != 3 {
return Err(DecoderError::Custom(
"Invalid subprotocol string length. Should be 3",
));
}
let mut p2: ProtocolId = [0u8; 3];
p2.clone_from_slice(&p);
Ok(PeerCapabilityInfo {
protocol: p2,
version: rlp.val_at(1)?,
})
}
}
impl ToString for PeerCapabilityInfo {
fn to_string(&self) -> String {
format!("{}/{}", str::from_utf8(&self.protocol[..]).unwrap_or("???"), self.version)
}
fn to_string(&self) -> String {
format!(
"{}/{}",
str::from_utf8(&self.protocol[..]).unwrap_or("???"),
self.version
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SessionCapabilityInfo {
pub protocol: [u8; 3],
pub version: u8,
pub packet_count: u8,
pub id_offset: u8,
pub protocol: [u8; 3],
pub version: u8,
pub packet_count: u8,
pub id_offset: u8,
}
impl PartialOrd for SessionCapabilityInfo {
fn partial_cmp(&self, other: &SessionCapabilityInfo) -> Option<Ordering> {
Some(self.cmp(other))
}
fn partial_cmp(&self, other: &SessionCapabilityInfo) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SessionCapabilityInfo {
fn cmp(&self, b: &SessionCapabilityInfo) -> Ordering {
// By protocol id first
if self.protocol != b.protocol {
return self.protocol.cmp(&b.protocol);
}
// By version
self.version.cmp(&b.version)
}
fn cmp(&self, b: &SessionCapabilityInfo) -> Ordering {
// By protocol id first
if self.protocol != b.protocol {
return self.protocol.cmp(&b.protocol);
}
// By version
self.version.cmp(&b.version)
}
}
/// Network service configuration
#[derive(Debug, PartialEq, Clone)]
pub struct NetworkConfiguration {
/// Directory path to store general network configuration. None means nothing will be saved
pub config_path: Option<String>,
/// Directory path to store network-specific configuration. None means nothing will be saved
pub net_config_path: Option<String>,
/// IP address to listen for incoming connections. Listen to all connections by default
pub listen_address: Option<SocketAddr>,
/// IP address to advertise. Detected automatically if none.
pub public_address: Option<SocketAddr>,
/// Port for UDP connections, same as TCP by default
pub udp_port: Option<u16>,
/// Enable NAT configuration
pub nat_enabled: bool,
/// Enable discovery
pub discovery_enabled: bool,
/// List of initial node addresses
pub boot_nodes: Vec<String>,
/// Use provided node key instead of default
pub use_secret: Option<Secret>,
/// Minimum number of connected peers to maintain
pub min_peers: u32,
/// Maximum allowed number of peers
pub max_peers: u32,
/// Maximum handshakes
pub max_handshakes: u32,
/// Reserved protocols. Peers with <key> protocol get additional <value> connection slots.
pub reserved_protocols: HashMap<ProtocolId, u32>,
/// List of reserved node addresses.
pub reserved_nodes: Vec<String>,
/// The non-reserved peer mode.
pub non_reserved_mode: NonReservedPeerMode,
/// IP filter
pub ip_filter: IpFilter,
/// Client identifier
pub client_version: String,
/// Directory path to store general network configuration. None means nothing will be saved
pub config_path: Option<String>,
/// Directory path to store network-specific configuration. None means nothing will be saved
pub net_config_path: Option<String>,
/// IP address to listen for incoming connections. Listen to all connections by default
pub listen_address: Option<SocketAddr>,
/// IP address to advertise. Detected automatically if none.
pub public_address: Option<SocketAddr>,
/// Port for UDP connections, same as TCP by default
pub udp_port: Option<u16>,
/// Enable NAT configuration
pub nat_enabled: bool,
/// Enable discovery
pub discovery_enabled: bool,
/// List of initial node addresses
pub boot_nodes: Vec<String>,
/// Use provided node key instead of default
pub use_secret: Option<Secret>,
/// Minimum number of connected peers to maintain
pub min_peers: u32,
/// Maximum allowed number of peers
pub max_peers: u32,
/// Maximum handshakes
pub max_handshakes: u32,
/// Reserved protocols. Peers with <key> protocol get additional <value> connection slots.
pub reserved_protocols: HashMap<ProtocolId, u32>,
/// List of reserved node addresses.
pub reserved_nodes: Vec<String>,
/// The non-reserved peer mode.
pub non_reserved_mode: NonReservedPeerMode,
/// IP filter
pub ip_filter: IpFilter,
/// Client identifier
pub client_version: String,
}
impl Default for NetworkConfiguration {
fn default() -> Self {
NetworkConfiguration::new()
}
fn default() -> Self {
NetworkConfiguration::new()
}
}
impl NetworkConfiguration {
/// Create a new instance of default settings.
pub fn new() -> Self {
NetworkConfiguration {
config_path: None,
net_config_path: None,
listen_address: None,
public_address: None,
udp_port: None,
nat_enabled: true,
discovery_enabled: true,
boot_nodes: Vec::new(),
use_secret: None,
min_peers: 25,
max_peers: 50,
max_handshakes: 64,
reserved_protocols: HashMap::new(),
ip_filter: IpFilter::default(),
reserved_nodes: Vec::new(),
non_reserved_mode: NonReservedPeerMode::Accept,
client_version: "Parity-network".into(),
}
}
/// Create a new instance of default settings.
pub fn new() -> Self {
NetworkConfiguration {
config_path: None,
net_config_path: None,
listen_address: None,
public_address: None,
udp_port: None,
nat_enabled: true,
discovery_enabled: true,
boot_nodes: Vec::new(),
use_secret: None,
min_peers: 25,
max_peers: 50,
max_handshakes: 64,
reserved_protocols: HashMap::new(),
ip_filter: IpFilter::default(),
reserved_nodes: Vec::new(),
non_reserved_mode: NonReservedPeerMode::Accept,
client_version: "Parity-network".into(),
}
}
/// Create new default configuration with specified listen port.
pub fn new_with_port(port: u16) -> NetworkConfiguration {
let mut config = NetworkConfiguration::new();
config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port)));
config
}
/// Create new default configuration with specified listen port.
pub fn new_with_port(port: u16) -> NetworkConfiguration {
let mut config = NetworkConfiguration::new();
config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(0, 0, 0, 0),
port,
)));
config
}
/// Create new default configuration for localhost-only connection with random port (usefull for testing)
pub fn new_local() -> NetworkConfiguration {
let mut config = NetworkConfiguration::new();
config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0)));
config.nat_enabled = false;
config
}
/// Create new default configuration for localhost-only connection with random port (usefull for testing)
pub fn new_local() -> NetworkConfiguration {
let mut config = NetworkConfiguration::new();
config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(127, 0, 0, 1),
0,
)));
config.nat_enabled = false;
config
}
}
/// IO access point. This is passed to all IO handlers and provides an interface to the IO subsystem.
pub trait NetworkContext {
/// Send a packet over the network to another peer.
fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
/// Send a packet over the network to another peer.
fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
/// Send a packet over the network to another peer using specified protocol.
fn send_protocol(&self, protocol: ProtocolId, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
/// Send a packet over the network to another peer using specified protocol.
fn send_protocol(
&self,
protocol: ProtocolId,
peer: PeerId,
packet_id: PacketId,
data: Vec<u8>,
) -> Result<(), Error>;
/// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing.
fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
/// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing.
fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
/// Disconnect a peer and prevent it from connecting again.
fn disable_peer(&self, peer: PeerId);
/// Disconnect a peer and prevent it from connecting again.
fn disable_peer(&self, peer: PeerId);
/// Disconnect peer. Reconnect can be attempted later.
fn disconnect_peer(&self, peer: PeerId);
/// Disconnect peer. Reconnect can be attempted later.
fn disconnect_peer(&self, peer: PeerId);
/// Check if the session is still active.
fn is_expired(&self) -> bool;
/// Check if the session is still active.
fn is_expired(&self) -> bool;
/// Register a new IO timer. 'IoHandler::timeout' will be called with the token.
fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error>;
/// Register a new IO timer. 'IoHandler::timeout' will be called with the token.
fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error>;
/// Returns peer identification string
fn peer_client_version(&self, peer: PeerId) -> ClientVersion;
/// Returns peer identification string
fn peer_client_version(&self, peer: PeerId) -> ClientVersion;
/// Returns information on p2p session
fn session_info(&self, peer: PeerId) -> Option<SessionInfo>;
/// Returns information on p2p session
fn session_info(&self, peer: PeerId) -> Option<SessionInfo>;
/// Returns max version for a given protocol.
fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option<u8>;
/// Returns max version for a given protocol.
fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option<u8>;
/// Returns this object's subprotocol name.
fn subprotocol_name(&self) -> ProtocolId;
/// Returns this object's subprotocol name.
fn subprotocol_name(&self) -> ProtocolId;
/// Returns whether the given peer ID is a reserved peer.
fn is_reserved_peer(&self, peer: PeerId) -> bool;
/// Returns whether the given peer ID is a reserved peer.
fn is_reserved_peer(&self, peer: PeerId) -> bool;
/// Returns the size the payload shouldn't exceed
fn payload_soft_limit(&self) -> usize;
/// Returns the size the payload shouldn't exceed
fn payload_soft_limit(&self) -> usize;
}
impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext {
fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
(**self).send(peer, packet_id, data)
}
impl<'a, T> NetworkContext for &'a T
where
T: ?Sized + NetworkContext,
{
fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
(**self).send(peer, packet_id, data)
}
fn send_protocol(&self, protocol: ProtocolId, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
(**self).send_protocol(protocol, peer, packet_id, data)
}
fn send_protocol(
&self,
protocol: ProtocolId,
peer: PeerId,
packet_id: PacketId,
data: Vec<u8>,
) -> Result<(), Error> {
(**self).send_protocol(protocol, peer, packet_id, data)
}
fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
(**self).respond(packet_id, data)
}
fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
(**self).respond(packet_id, data)
}
fn disable_peer(&self, peer: PeerId) {
(**self).disable_peer(peer)
}
fn disable_peer(&self, peer: PeerId) {
(**self).disable_peer(peer)
}
fn disconnect_peer(&self, peer: PeerId) {
(**self).disconnect_peer(peer)
}
fn disconnect_peer(&self, peer: PeerId) {
(**self).disconnect_peer(peer)
}
fn is_expired(&self) -> bool {
(**self).is_expired()
}
fn is_expired(&self) -> bool {
(**self).is_expired()
}
fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error> {
(**self).register_timer(token, delay)
}
fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error> {
(**self).register_timer(token, delay)
}
fn peer_client_version(&self, peer: PeerId) -> ClientVersion {
(**self).peer_client_version(peer)
}
fn peer_client_version(&self, peer: PeerId) -> ClientVersion {
(**self).peer_client_version(peer)
}
fn session_info(&self, peer: PeerId) -> Option<SessionInfo> {
(**self).session_info(peer)
}
fn session_info(&self, peer: PeerId) -> Option<SessionInfo> {
(**self).session_info(peer)
}
fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option<u8> {
(**self).protocol_version(protocol, peer)
}
fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option<u8> {
(**self).protocol_version(protocol, peer)
}
fn subprotocol_name(&self) -> ProtocolId {
(**self).subprotocol_name()
}
fn subprotocol_name(&self) -> ProtocolId {
(**self).subprotocol_name()
}
fn is_reserved_peer(&self, peer: PeerId) -> bool {
(**self).is_reserved_peer(peer)
}
fn is_reserved_peer(&self, peer: PeerId) -> bool {
(**self).is_reserved_peer(peer)
}
fn payload_soft_limit(&self) -> usize {
(**self).payload_soft_limit()
}
fn payload_soft_limit(&self) -> usize {
(**self).payload_soft_limit()
}
}
/// Network IO protocol handler. This needs to be implemented for each new subprotocol.
/// All the handler function are called from within IO event loop.
/// `Message` is the type for message data.
pub trait NetworkProtocolHandler: Sync + Send {
/// Initialize the handler
fn initialize(&self, _io: &NetworkContext) {}
/// Called when new network packet received.
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]);
/// Called when new peer is connected. Only called when peer supports the same protocol.
fn connected(&self, io: &NetworkContext, peer: &PeerId);
/// Called when a previously connected peer disconnects.
fn disconnected(&self, io: &NetworkContext, peer: &PeerId);
/// Timer function called after a timeout created with `NetworkContext::timeout`.
fn timeout(&self, _io: &NetworkContext, _timer: TimerToken) {}
/// Initialize the handler
fn initialize(&self, _io: &NetworkContext) {}
/// Called when new network packet received.
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]);
/// Called when new peer is connected. Only called when peer supports the same protocol.
fn connected(&self, io: &NetworkContext, peer: &PeerId);
/// Called when a previously connected peer disconnects.
fn disconnected(&self, io: &NetworkContext, peer: &PeerId);
/// Timer function called after a timeout created with `NetworkContext::timeout`.
fn timeout(&self, _io: &NetworkContext, _timer: TimerToken) {}
}
/// Non-reserved peer modes.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NonReservedPeerMode {
/// Accept them. This is the default.
Accept,
/// Deny them.
Deny,
/// Accept them. This is the default.
Accept,
/// Deny them.
Deny,
}
impl NonReservedPeerMode {
/// Attempt to parse the peer mode from a string.
pub fn parse(s: &str) -> Option<Self> {
match s {
"accept" => Some(NonReservedPeerMode::Accept),
"deny" => Some(NonReservedPeerMode::Deny),
_ => None,
}
}
/// Attempt to parse the peer mode from a string.
pub fn parse(s: &str) -> Option<Self> {
match s {
"accept" => Some(NonReservedPeerMode::Accept),
"deny" => Some(NonReservedPeerMode::Deny),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -423,7 +453,9 @@ impl IpFilter {
"none" => filter.predefined = AllowIP::None,
custom => {
if custom.starts_with("-") {
filter.custom_block.push(IpNetwork::from_str(&custom.to_owned().split_off(1))?)
filter
.custom_block
.push(IpNetwork::from_str(&custom.to_owned().split_off(1))?)
} else {
filter.custom_allow.push(IpNetwork::from_str(custom)?)
}
@@ -437,12 +469,12 @@ impl IpFilter {
/// IP fiter
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AllowIP {
/// Connect to any address
All,
/// Connect to private network only
Private,
/// Connect to public network only
Public,
/// Connect to any address
All,
/// Connect to private network only
Private,
/// Connect to public network only
Public,
/// Block all addresses
None,
}