// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see .
use std::collections::{BTreeSet, BTreeMap};
use std::collections::btree_map::Entry;
use std::net::SocketAddr;
use std::sync::Arc;
use ethereum_types::H256;
use ethkey::Public;
use key_server_cluster::{KeyServerSet, KeyServerSetSnapshot};
use key_server_cluster::cluster::{ClusterConfiguration, ServersSetChangeParams};
use key_server_cluster::cluster_sessions::AdminSession;
use key_server_cluster::cluster_connections::{Connection};
use key_server_cluster::cluster_connections_net::{NetConnectionsContainer};
use types::{Error, NodeId};
use NodeKeyPair;
#[derive(Debug, Clone, Copy, PartialEq)]
/// Describes which maintain() call is required.
pub enum Maintain {
/// We need to maintain() both connections && session.
SessionAndConnections,
/// Only call maintain_session.
Session,
/// Only call maintain_connections.
Connections,
}
/// Connection trigger, which executes necessary actions when set of key servers changes.
pub trait ConnectionTrigger: Send + Sync {
/// On maintain interval.
fn on_maintain(&mut self) -> Option;
/// When connection is established.
fn on_connection_established(&mut self, node: &NodeId) -> Option;
/// When connection is closed.
fn on_connection_closed(&mut self, node: &NodeId) -> Option;
/// Maintain active sessions. Returns Some if servers set session creation required.
fn maintain_session(&mut self) -> Option;
/// Maintain active connections.
fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer);
/// Return connector for the servers set change session creator.
fn servers_set_change_creator_connector(&self) -> Arc;
}
/// Servers set change session creator connector.
pub trait ServersSetChangeSessionCreatorConnector: Send + Sync {
/// Get actual administrator public key. For manual-migration configuration it is the pre-configured
/// administrator key. For auto-migration configurations it is the key of actual MigrationSession master node.
fn admin_public(&self, migration_id: Option<&H256>, new_server_set: BTreeSet) -> Result;
/// Set active servers set change session.
fn set_key_servers_set_change_session(&self, session: Arc);
}
/// Simple connection trigger, which only keeps connections to current_set.
pub struct SimpleConnectionTrigger {
/// Key server set cluster.
key_server_set: Arc,
/// Trigger connections.
connections: TriggerConnections,
/// Servers set change session creator connector.
connector: Arc,
}
/// Simple Servers set change session creator connector, which will just return
/// pre-configured administartor public when asked.
pub struct SimpleServersSetChangeSessionCreatorConnector {
/// Secret store administrator public key.
pub admin_public: Option,
}
#[derive(Debug, Clone, Copy, PartialEq)]
/// Action with trigger connections.
pub enum ConnectionsAction {
/// Connect to nodes from old set only.
ConnectToCurrentSet,
/// Connect to nodes from migration set.
ConnectToMigrationSet,
}
/// Trigger connections.
pub struct TriggerConnections {
/// This node key pair.
pub self_key_pair: Arc,
}
impl SimpleConnectionTrigger {
/// Create new simple from cluster configuration.
pub fn with_config(config: &ClusterConfiguration) -> Self {
Self::new(config.key_server_set.clone(), config.self_key_pair.clone(), config.admin_public)
}
/// Create new simple connection trigger.
pub fn new(key_server_set: Arc, self_key_pair: Arc, admin_public: Option) -> Self {
SimpleConnectionTrigger {
key_server_set: key_server_set,
connections: TriggerConnections {
self_key_pair: self_key_pair,
},
connector: Arc::new(SimpleServersSetChangeSessionCreatorConnector {
admin_public: admin_public,
}),
}
}
}
impl ConnectionTrigger for SimpleConnectionTrigger {
fn on_maintain(&mut self) -> Option {
Some(Maintain::Connections)
}
fn on_connection_established(&mut self, _node: &NodeId) -> Option {
None
}
fn on_connection_closed(&mut self, _node: &NodeId) -> Option {
// we do not want to reconnect after every connection close
// because it could be a part of something bigger
None
}
fn maintain_session(&mut self) -> Option {
None
}
fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer) {
self.connections.maintain(ConnectionsAction::ConnectToCurrentSet, connections, &self.key_server_set.snapshot())
}
fn servers_set_change_creator_connector(&self) -> Arc {
self.connector.clone()
}
}
impl ServersSetChangeSessionCreatorConnector for SimpleServersSetChangeSessionCreatorConnector {
fn admin_public(&self, _migration_id: Option<&H256>, _new_server_set: BTreeSet) -> Result {
self.admin_public.clone().ok_or(Error::AccessDenied)
}
fn set_key_servers_set_change_session(&self, _session: Arc) {
}
}
impl TriggerConnections {
pub fn maintain(&self, action: ConnectionsAction, data: &mut NetConnectionsContainer, server_set: &KeyServerSetSnapshot) {
match action {
ConnectionsAction::ConnectToCurrentSet => {
adjust_connections(self.self_key_pair.public(), data, &server_set.current_set);
},
ConnectionsAction::ConnectToMigrationSet => {
let migration_set = server_set.migration.as_ref().map(|s| s.set.clone()).unwrap_or_default();
adjust_connections(self.self_key_pair.public(), data, &migration_set);
},
}
}
}
fn adjust_connections(
self_node_id: &NodeId,
data: &mut NetConnectionsContainer,
required_set: &BTreeMap
) {
if !required_set.contains_key(self_node_id) {
if !data.is_isolated {
trace!(target: "secretstore_net", "{}: isolated from cluser", self_node_id);
}
data.is_isolated = true;
data.connections.clear();
data.nodes.clear();
return;
}
data.is_isolated = false;
for node_to_disconnect in select_nodes_to_disconnect(&data.nodes, required_set) {
if let Entry::Occupied(entry) = data.connections.entry(node_to_disconnect.clone()) {
trace!(target: "secretstore_net", "{}: adjusting connections - removing connection to {} at {}",
self_node_id, entry.get().node_id(), entry.get().node_address());
entry.remove();
}
data.nodes.remove(&node_to_disconnect);
}
for (node_to_connect, node_addr) in required_set {
if node_to_connect != self_node_id {
data.nodes.insert(node_to_connect.clone(), node_addr.clone());
}
}
}
fn select_nodes_to_disconnect(current_set: &BTreeMap, new_set: &BTreeMap) -> Vec {
current_set.iter()
.filter(|&(node_id, node_addr)| match new_set.get(node_id) {
Some(new_node_addr) => node_addr != new_node_addr,
None => true,
})
.map(|(node_id, _)| node_id.clone())
.collect()
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use std::sync::Arc;
use ethkey::{Random, Generator};
use key_server_cluster::{MapKeyServerSet, PlainNodeKeyPair, KeyServerSetSnapshot, KeyServerSetMigration};
use key_server_cluster::cluster_connections_net::NetConnectionsContainer;
use super::{Maintain, TriggerConnections, ConnectionsAction, ConnectionTrigger, SimpleConnectionTrigger,
select_nodes_to_disconnect, adjust_connections};
fn default_connection_data() -> NetConnectionsContainer {
NetConnectionsContainer {
is_isolated: false,
nodes: Default::default(),
connections: Default::default(),
}
}
fn create_connections() -> TriggerConnections {
TriggerConnections {
self_key_pair: Arc::new(PlainNodeKeyPair::new(Random.generate().unwrap())),
}
}
#[test]
fn do_not_disconnect_if_set_is_not_changed() {
let node_id = Random.generate().unwrap().public().clone();
assert_eq!(select_nodes_to_disconnect(
&vec![(node_id, "127.0.0.1:8081".parse().unwrap())].into_iter().collect(),
&vec![(node_id, "127.0.0.1:8081".parse().unwrap())].into_iter().collect()),
vec![]);
}
#[test]
fn disconnect_if_address_has_changed() {
let node_id = Random.generate().unwrap().public().clone();
assert_eq!(select_nodes_to_disconnect(
&vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(),
&vec![(node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect()),
vec![node_id.clone()]);
}
#[test]
fn disconnect_if_node_has_removed() {
let node_id = Random.generate().unwrap().public().clone();
assert_eq!(select_nodes_to_disconnect(
&vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(),
&vec![].into_iter().collect()),
vec![node_id.clone()]);
}
#[test]
fn does_not_disconnect_if_node_has_added() {
let node_id = Random.generate().unwrap().public().clone();
assert_eq!(select_nodes_to_disconnect(
&vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(),
&vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap()),
(Random.generate().unwrap().public().clone(), "127.0.0.1:8082".parse().unwrap())]
.into_iter().collect()),
vec![]);
}
#[test]
fn adjust_connections_disconnects_from_all_nodes_if_not_a_part_of_key_server() {
let self_node_id = Random.generate().unwrap().public().clone();
let other_node_id = Random.generate().unwrap().public().clone();
let mut connection_data = default_connection_data();
connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8081".parse().unwrap());
let required_set = connection_data.nodes.clone();
adjust_connections(&self_node_id, &mut connection_data, &required_set);
assert!(connection_data.nodes.is_empty());
assert!(connection_data.is_isolated);
}
#[test]
fn adjust_connections_connects_to_new_nodes() {
let self_node_id = Random.generate().unwrap().public().clone();
let other_node_id = Random.generate().unwrap().public().clone();
let mut connection_data = default_connection_data();
let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()),
(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect();
adjust_connections(&self_node_id, &mut connection_data, &required_set);
assert!(connection_data.nodes.contains_key(&other_node_id));
assert!(!connection_data.is_isolated);
}
#[test]
fn adjust_connections_reconnects_from_changed_nodes() {
let self_node_id = Random.generate().unwrap().public().clone();
let other_node_id = Random.generate().unwrap().public().clone();
let mut connection_data = default_connection_data();
connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap());
let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()),
(other_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect();
adjust_connections(&self_node_id, &mut connection_data, &required_set);
assert_eq!(connection_data.nodes.get(&other_node_id), Some(&"127.0.0.1:8083".parse().unwrap()));
assert!(!connection_data.is_isolated);
}
#[test]
fn adjust_connections_disconnects_from_removed_nodes() {
let self_node_id = Random.generate().unwrap().public().clone();
let other_node_id = Random.generate().unwrap().public().clone();
let mut connection_data = default_connection_data();
connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap());
let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect();
adjust_connections(&self_node_id, &mut connection_data, &required_set);
assert!(connection_data.nodes.is_empty());
assert!(!connection_data.is_isolated);
}
#[test]
fn adjust_connections_does_not_connects_to_self() {
let self_node_id = Random.generate().unwrap().public().clone();
let mut connection_data = default_connection_data();
let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect();
adjust_connections(&self_node_id, &mut connection_data, &required_set);
assert!(connection_data.nodes.is_empty());
assert!(!connection_data.is_isolated);
}
#[test]
fn maintain_connects_to_current_set_works() {
let connections = create_connections();
let self_node_id = connections.self_key_pair.public().clone();
let current_node_id = Random.generate().unwrap().public().clone();
let migration_node_id = Random.generate().unwrap().public().clone();
let new_node_id = Random.generate().unwrap().public().clone();
let mut connections_data = default_connection_data();
connections.maintain(ConnectionsAction::ConnectToCurrentSet, &mut connections_data, &KeyServerSetSnapshot {
current_set: vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()),
(current_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(),
new_set: vec![(new_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(),
migration: Some(KeyServerSetMigration {
set: vec![(migration_node_id.clone(), "127.0.0.1:8084".parse().unwrap())].into_iter().collect(),
..Default::default()
}),
});
assert_eq!(vec![current_node_id], connections_data.nodes.keys().cloned().collect::>());
}
#[test]
fn maintain_connects_to_migration_set_works() {
let connections = create_connections();
let self_node_id = connections.self_key_pair.public().clone();
let current_node_id = Random.generate().unwrap().public().clone();
let migration_node_id = Random.generate().unwrap().public().clone();
let new_node_id = Random.generate().unwrap().public().clone();
let mut connections_data = default_connection_data();
connections.maintain(ConnectionsAction::ConnectToMigrationSet, &mut connections_data, &KeyServerSetSnapshot {
current_set: vec![(current_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(),
new_set: vec![(new_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(),
migration: Some(KeyServerSetMigration {
set: vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()),
(migration_node_id.clone(), "127.0.0.1:8084".parse().unwrap())].into_iter().collect(),
..Default::default()
}),
});
assert_eq!(vec![migration_node_id].into_iter().collect::>(),
connections_data.nodes.keys().cloned().collect::>());
}
#[test]
fn simple_connections_trigger_only_maintains_connections() {
let key_server_set = Arc::new(MapKeyServerSet::new(false, Default::default()));
let self_key_pair = Arc::new(PlainNodeKeyPair::new(Random.generate().unwrap()));
let mut trigger = SimpleConnectionTrigger::new(key_server_set, self_key_pair, None);
assert_eq!(trigger.on_maintain(), Some(Maintain::Connections));
}
}