use std::fmt;
use std::sync::{Arc, RwLock};
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;
use serde_json::Value;
use iron::Handler;
use crypto::{Hash, PublicKey, SecretKey};
use storage::{Fork, Snapshot};
use messages::RawTransaction;
use encoding::Error as MessageError;
use node::{ApiSender, Node, State, TransactionSend};
use blockchain::{Blockchain, ConsensusConfig, Schema, StoredConfiguration, ValidatorKeys};
use helpers::{Height, Milliseconds, ValidatorId};
use super::transaction::Transaction;
#[allow(unused_variables, unused_mut)]
pub trait Service: Send + Sync + 'static {
fn service_id(&self) -> u16;
fn service_name(&self) -> &str;
fn state_hash(&self, snapshot: &Snapshot) -> Vec<Hash>;
fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError>;
fn initialize(&self, fork: &mut Fork) -> Value {
Value::Null
}
fn handle_commit(&self, context: &ServiceContext) {}
fn public_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
None
}
fn private_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
None
}
}
#[derive(Debug)]
pub struct ServiceContext {
validator_id: Option<ValidatorId>,
service_keypair: (PublicKey, SecretKey),
api_sender: ApiSender,
fork: Fork,
stored_configuration: StoredConfiguration,
height: Height,
}
impl ServiceContext {
pub fn new(
service_public_key: PublicKey,
service_secret_key: SecretKey,
api_sender: ApiSender,
fork: Fork,
) -> ServiceContext {
let (stored_configuration, height) = {
let schema = Schema::new(fork.as_ref());
let stored_configuration = schema.actual_configuration();
let height = schema.height();
(stored_configuration, height)
};
let validator_id = stored_configuration
.validator_keys
.iter()
.position(|validator| service_public_key == validator.service_key)
.map(|id| ValidatorId(id as u16));
ServiceContext {
validator_id,
service_keypair: (service_public_key, service_secret_key),
api_sender,
fork,
stored_configuration,
height,
}
}
pub fn validator_id(&self) -> Option<ValidatorId> {
self.validator_id
}
pub fn snapshot(&self) -> &Snapshot {
self.fork.as_ref()
}
pub fn height(&self) -> Height {
self.height
}
pub fn validators(&self) -> &[ValidatorKeys] {
self.stored_configuration.validator_keys.as_slice()
}
pub fn public_key(&self) -> &PublicKey {
&self.service_keypair.0
}
pub fn secret_key(&self) -> &SecretKey {
&self.service_keypair.1
}
pub fn actual_consensus_config(&self) -> &ConsensusConfig {
&self.stored_configuration.consensus
}
pub fn actual_service_config(&self, service: &Service) -> &Value {
&self.stored_configuration.services[service.service_name()]
}
pub fn transaction_sender(&self) -> &TransactionSend {
&self.api_sender
}
pub fn stored_configuration(&self) -> &StoredConfiguration {
&self.stored_configuration
}
}
#[derive(Debug, Default)]
pub struct ApiNodeState {
incoming_connections: HashSet<SocketAddr>,
outgoing_connections: HashSet<SocketAddr>,
reconnects_timeout: HashMap<SocketAddr, Milliseconds>,
peers_info: HashMap<SocketAddr, PublicKey>,
is_enabled: bool,
}
impl ApiNodeState {
fn new() -> ApiNodeState {
Self::default()
}
}
#[derive(Clone, Debug)]
pub struct SharedNodeState {
state: Arc<RwLock<ApiNodeState>>,
pub state_update_timeout: Milliseconds,
}
impl SharedNodeState {
pub fn new(state_update_timeout: Milliseconds) -> SharedNodeState {
SharedNodeState {
state: Arc::new(RwLock::new(ApiNodeState::new())),
state_update_timeout,
}
}
pub fn incoming_connections(&self) -> Vec<SocketAddr> {
self.state
.read()
.expect("Expected read lock.")
.incoming_connections
.iter()
.cloned()
.collect()
}
pub fn outgoing_connections(&self) -> Vec<SocketAddr> {
self.state
.read()
.expect("Expected read lock.")
.outgoing_connections
.iter()
.cloned()
.collect()
}
pub fn reconnects_timeout(&self) -> Vec<(SocketAddr, Milliseconds)> {
self.state
.read()
.expect("Expected read lock.")
.reconnects_timeout
.iter()
.map(|(c, e)| (*c, *e))
.collect()
}
pub fn peers_info(&self) -> Vec<(SocketAddr, PublicKey)> {
self.state
.read()
.expect("Expected read lock.")
.peers_info
.iter()
.map(|(c, e)| (*c, *e))
.collect()
}
pub fn update_node_state(&self, state: &State) {
for (p, c) in state.peers().iter() {
self.state
.write()
.expect("Expected write lock.")
.peers_info
.insert(c.addr(), *p);
}
}
pub fn is_enabled(&self) -> bool {
let state = self.state.read().expect("Expected read lock.");
state.is_enabled
}
pub fn set_enabled(&self, is_enabled: bool) {
let mut state = self.state.write().expect("Expected read lock.");
state.is_enabled = is_enabled;
}
pub fn state_update_timeout(&self) -> Milliseconds {
self.state_update_timeout
}
pub fn add_incoming_connection(&self, addr: SocketAddr) {
self.state
.write()
.expect("Expected write lock")
.incoming_connections
.insert(addr);
}
pub fn add_outgoing_connection(&self, addr: SocketAddr) {
self.state
.write()
.expect("Expected write lock")
.outgoing_connections
.insert(addr);
}
pub fn remove_incoming_connection(&self, addr: &SocketAddr) -> bool {
self.state
.write()
.expect("Expected write lock")
.incoming_connections
.remove(addr)
}
pub fn remove_outgoing_connection(&self, addr: &SocketAddr) -> bool {
self.state
.write()
.expect("Expected write lock")
.outgoing_connections
.remove(addr)
}
pub fn add_reconnect_timeout(
&self,
addr: SocketAddr,
timeout: Milliseconds,
) -> Option<Milliseconds> {
self.state
.write()
.expect("Expected write lock")
.reconnects_timeout
.insert(addr, timeout)
}
pub fn remove_reconnect_timeout(&self, addr: &SocketAddr) -> Option<Milliseconds> {
self.state
.write()
.expect("Expected write lock")
.reconnects_timeout
.remove(addr)
}
}
pub struct ApiContext {
blockchain: Blockchain,
node_channel: ApiSender,
public_key: PublicKey,
secret_key: SecretKey,
}
impl ApiContext {
pub fn new(node: &Node) -> ApiContext {
let handler = node.handler();
ApiContext {
blockchain: handler.blockchain.clone(),
node_channel: node.channel(),
public_key: *node.state().service_public_key(),
secret_key: node.state().service_secret_key().clone(),
}
}
pub fn from_parts(
blockchain: &Blockchain,
node_channel: ApiSender,
public_key: &PublicKey,
secret_key: &SecretKey,
) -> ApiContext {
ApiContext {
blockchain: blockchain.clone(),
node_channel,
public_key: *public_key,
secret_key: secret_key.clone(),
}
}
pub fn blockchain(&self) -> &Blockchain {
&self.blockchain
}
pub fn node_channel(&self) -> &ApiSender {
&self.node_channel
}
pub fn public_key(&self) -> &PublicKey {
&self.public_key
}
pub fn secret_key(&self) -> &SecretKey {
&self.secret_key
}
}
impl ::std::fmt::Debug for ApiContext {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ApiContext(blockchain: {:?}, public_key: {:?})",
self.blockchain,
self.public_key
)
}
}
impl<'a, S: Service> From<S> for Box<Service + 'a> {
fn from(s: S) -> Self {
Box::new(s) as Box<Service>
}
}