2016-11-03 09:19:35 -07:00
|
|
|
use std::net::SocketAddr;
|
2017-01-10 00:58:55 -08:00
|
|
|
use std::thread;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::sync::mpsc::{channel, Sender, Receiver};
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use sync::{create_sync_peers, create_local_sync_node, create_sync_connection_factory, SyncListener};
|
2016-11-05 07:32:57 -07:00
|
|
|
use message::Services;
|
2017-01-10 00:58:55 -08:00
|
|
|
use primitives::hash::H256;
|
2016-11-04 04:08:58 -07:00
|
|
|
use util::{open_db, init_db, node_table_path};
|
2016-11-25 03:37:27 -08:00
|
|
|
use {config, p2p, PROTOCOL_VERSION, PROTOCOL_MINIMUM};
|
2016-12-07 05:14:52 -08:00
|
|
|
use super::super::rpc;
|
2016-11-03 09:19:35 -07:00
|
|
|
|
2017-01-10 00:58:55 -08:00
|
|
|
enum BlockNotifierTask {
|
|
|
|
NewBlock(H256),
|
|
|
|
Stop,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct BlockNotifier {
|
|
|
|
tx: Sender<BlockNotifierTask>,
|
|
|
|
is_synchronizing: Arc<AtomicBool>,
|
|
|
|
worker_thread: Option<thread::JoinHandle<()>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BlockNotifier {
|
|
|
|
pub fn new(block_notify_command: String) -> Self {
|
|
|
|
let (tx, rx) = channel();
|
|
|
|
let is_synchronizing = Arc::new(AtomicBool::default());
|
|
|
|
BlockNotifier {
|
|
|
|
tx: tx,
|
|
|
|
is_synchronizing: is_synchronizing.clone(),
|
|
|
|
worker_thread: Some(thread::Builder::new()
|
|
|
|
.name("Block notification thread".to_owned())
|
|
|
|
.spawn(move || BlockNotifier::worker(rx, block_notify_command))
|
|
|
|
.expect("Error creating block notification thread"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn worker(rx: Receiver<BlockNotifierTask>, block_notify_command: String) {
|
|
|
|
for cmd in rx {
|
|
|
|
match cmd {
|
|
|
|
BlockNotifierTask::NewBlock(new_block_hash) => {
|
|
|
|
let new_block_hash = new_block_hash.to_reversed_str();
|
|
|
|
let command = block_notify_command.replace("%s", &new_block_hash);
|
|
|
|
let c_command = ::std::ffi::CString::new(command.clone()).unwrap();
|
|
|
|
unsafe {
|
|
|
|
use libc::system;
|
|
|
|
|
|
|
|
let err = system(c_command.as_ptr());
|
|
|
|
if err != 0 {
|
|
|
|
error!(target: "pbtc", "Block notification command {} exited with error code {}", command, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
BlockNotifierTask::Stop => {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
trace!(target: "pbtc", "Block notification thread stopped");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SyncListener for BlockNotifier {
|
|
|
|
fn synchronization_state_switched(&self, is_synchronizing: bool) {
|
|
|
|
self.is_synchronizing.store(is_synchronizing, Ordering::SeqCst);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn best_storage_block_inserted(&self, block_hash: &H256) {
|
|
|
|
if !self.is_synchronizing.load(Ordering::SeqCst) {
|
|
|
|
self.tx.send(BlockNotifierTask::NewBlock(block_hash.clone()))
|
|
|
|
.expect("Block notification thread have the same lifetime as `BlockNotifier`")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for BlockNotifier {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if let Some(join_handle) = self.worker_thread.take() {
|
|
|
|
let _ = self.tx.send(BlockNotifierTask::Stop);
|
|
|
|
join_handle.join().expect("Clean shutdown.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 09:19:35 -07:00
|
|
|
pub fn start(cfg: config::Config) -> Result<(), String> {
|
|
|
|
let mut el = p2p::event_loop();
|
|
|
|
|
2016-11-07 01:15:15 -08:00
|
|
|
let db = open_db(&cfg);
|
2016-11-07 02:20:27 -08:00
|
|
|
try!(init_db(&cfg, &db));
|
2016-11-07 01:15:15 -08:00
|
|
|
|
2016-11-24 22:58:04 -08:00
|
|
|
let nodes_path = node_table_path(&cfg);
|
|
|
|
|
2016-11-03 09:19:35 -07:00
|
|
|
let p2p_cfg = p2p::Config {
|
2016-11-16 15:33:35 -08:00
|
|
|
threads: cfg.p2p_threads,
|
|
|
|
inbound_connections: cfg.inbound_connections,
|
|
|
|
outbound_connections: cfg.outbound_connections,
|
2016-11-03 09:19:35 -07:00
|
|
|
connection: p2p::NetConfig {
|
2016-11-16 16:02:34 -08:00
|
|
|
protocol_version: PROTOCOL_VERSION,
|
|
|
|
protocol_minimum: PROTOCOL_MINIMUM,
|
2016-11-03 09:19:35 -07:00
|
|
|
magic: cfg.magic,
|
|
|
|
local_address: SocketAddr::new("127.0.0.1".parse().unwrap(), cfg.port),
|
2016-11-05 07:32:57 -07:00
|
|
|
services: Services::default().with_network(true),
|
2016-11-25 03:37:27 -08:00
|
|
|
user_agent: cfg.user_agent,
|
2016-11-03 09:19:35 -07:00
|
|
|
start_height: 0,
|
2016-12-07 05:40:57 -08:00
|
|
|
relay: true,
|
2016-11-03 09:19:35 -07:00
|
|
|
},
|
|
|
|
peers: cfg.connect.map_or_else(|| vec![], |x| vec![x]),
|
2016-11-30 05:37:17 -08:00
|
|
|
seeds: cfg.seednodes,
|
2016-11-24 22:58:04 -08:00
|
|
|
node_table_path: nodes_path,
|
2016-11-30 07:01:11 -08:00
|
|
|
internet_protocol: cfg.internet_protocol,
|
2016-11-03 09:19:35 -07:00
|
|
|
};
|
|
|
|
|
2016-12-18 23:29:53 -08:00
|
|
|
let sync_peers = create_sync_peers();
|
2017-01-11 05:36:33 -08:00
|
|
|
let local_sync_node = create_local_sync_node(cfg.magic, db.clone(), sync_peers.clone());
|
2016-12-18 23:29:53 -08:00
|
|
|
let sync_connection_factory = create_sync_connection_factory(sync_peers.clone(), local_sync_node.clone());
|
2016-11-03 09:19:35 -07:00
|
|
|
|
2017-01-10 00:58:55 -08:00
|
|
|
if let Some(block_notify_command) = cfg.block_notify_command {
|
|
|
|
local_sync_node.install_sync_listener(Box::new(BlockNotifier::new(block_notify_command)));
|
|
|
|
}
|
|
|
|
|
2016-12-12 10:18:43 -08:00
|
|
|
let p2p = try!(p2p::P2P::new(p2p_cfg, sync_connection_factory, el.handle()).map_err(|x| x.to_string()));
|
2016-12-07 11:39:12 -08:00
|
|
|
let rpc_deps = rpc::Dependencies {
|
2016-12-12 10:49:01 -08:00
|
|
|
network: cfg.magic,
|
|
|
|
storage: db,
|
2016-12-07 11:39:12 -08:00
|
|
|
local_sync_node: local_sync_node,
|
2016-12-12 10:18:43 -08:00
|
|
|
p2p_context: p2p.context().clone(),
|
2016-12-20 02:42:51 -08:00
|
|
|
remote: el.remote(),
|
2016-12-07 11:39:12 -08:00
|
|
|
};
|
|
|
|
let _rpc_server = try!(rpc::new_http(cfg.rpc_config, rpc_deps));
|
2016-12-07 05:14:52 -08:00
|
|
|
|
2016-11-03 09:19:35 -07:00
|
|
|
try!(p2p.run().map_err(|_| "Failed to start p2p module"));
|
|
|
|
el.run(p2p::forever()).unwrap();
|
|
|
|
Ok(())
|
|
|
|
}
|