grin/grin/src/server.rs
Ignotus Peverell e50703d79e Externalized all peers selection and handling ()
Moved handling to the peer map out of the p2p server and into
its own struct. Allowed factoring code from the net adapter and
simplification of some interactions. Also removes the need for
the adapter to reference the p2p server or peers.

Fixes ,  and 
2017-12-14 01:13:05 +01:00

242 lines
6.5 KiB
Rust

// Copyright 2016 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Grin server implementation, glues the different parts of the system (mostly
//! the peer-to-peer server, the blockchain and the transaction pool) and acts
//! as a facade.
use std::net::SocketAddr;
use std::sync::{Arc, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time;
use futures::{Future, Stream};
use tokio_core::reactor;
use tokio_timer::Timer;
use adapters::*;
use api;
use chain;
use core::{global, genesis};
use miner;
use p2p;
use pool;
use seed;
use sync;
use types::*;
use pow;
use util::LOGGER;
/// Grin server holding internal structures.
pub struct Server {
/// server config
pub config: ServerConfig,
/// event handle
evt_handle: reactor::Handle,
/// handle to our network server
p2p: Arc<p2p::Server>,
/// data store access
chain: Arc<chain::Chain>,
/// in-memory transaction pool
tx_pool: Arc<RwLock<pool::TransactionPool<PoolToChainAdapter>>>,
currently_syncing: Arc<AtomicBool>,
}
impl Server {
/// Instantiates and starts a new server.
pub fn start(config: ServerConfig) -> Result<Server, Error> {
let mut evtlp = reactor::Core::new().unwrap();
let mut mining_config = config.mining_config.clone();
let serv = Server::future(config, &evtlp.handle())?;
if mining_config.as_mut().unwrap().enable_mining {
serv.start_miner(mining_config.unwrap());
}
let forever = Timer::default()
.interval(time::Duration::from_secs(60))
.for_each(move |_| {
debug!(LOGGER, "event loop running");
Ok(())
})
.map_err(|_| ());
evtlp.run(forever).unwrap();
Ok(serv)
}
/// Instantiates a new server associated with the provided future reactor.
pub fn future(mut config: ServerConfig, evt_handle: &reactor::Handle) -> Result<Server, Error> {
let pool_adapter = Arc::new(PoolToChainAdapter::new());
let pool_net_adapter = Arc::new(PoolToNetAdapter::new());
let tx_pool = Arc::new(RwLock::new(pool::TransactionPool::new(
config.pool_config.clone(),
pool_adapter.clone(),
pool_net_adapter.clone(),
)));
let chain_adapter = Arc::new(ChainToPoolAndNetAdapter::new(tx_pool.clone()));
let genesis = match config.chain_type {
global::ChainTypes::Testnet1 => genesis::genesis_testnet1(),
_ => pow::mine_genesis_block(config.mining_config.clone())?,
};
info!(
LOGGER,
"Starting server, genesis block: {}",
genesis.hash(),
);
let shared_chain = Arc::new(chain::Chain::init(
config.db_root.clone(),
chain_adapter.clone(),
genesis.clone(),
pow::verify_size,
)?);
pool_adapter.set_chain(shared_chain.clone());
let currently_syncing = Arc::new(AtomicBool::new(true));
let net_adapter = Arc::new(NetToChainAdapter::new(
currently_syncing.clone(),
shared_chain.clone(),
tx_pool.clone(),
));
let p2p_server = Arc::new(p2p::Server::new(
config.db_root.clone(),
config.capabilities,
config.p2p_config.unwrap(),
net_adapter.clone(),
genesis.hash(),
)?);
chain_adapter.init(p2p_server.peers.clone());
pool_net_adapter.init(p2p_server.peers.clone());
let seed = seed::Seeder::new(
config.capabilities, p2p_server.clone(), p2p_server.peers.clone());
match config.seeding_type.clone() {
Seeding::None => {
warn!(
LOGGER,
"No seed(s) configured, will stay solo until connected to"
);
seed.connect_and_monitor(
evt_handle.clone(),
seed::predefined_seeds(vec![]),
);
}
Seeding::List => {
seed.connect_and_monitor(
evt_handle.clone(),
seed::predefined_seeds(config.seeds.as_mut().unwrap().clone()),
);
}
Seeding::WebStatic => {
seed.connect_and_monitor(
evt_handle.clone(),
seed::web_seeds(evt_handle.clone()),
);
}
_ => {}
}
sync::run_sync(
currently_syncing.clone(),
p2p_server.peers.clone(),
shared_chain.clone(),
);
evt_handle.spawn(p2p_server.start(evt_handle.clone()).map_err(|_| ()));
info!(LOGGER, "Starting rest apis at: {}", &config.api_http_addr);
api::start_rest_apis(
config.api_http_addr.clone(),
shared_chain.clone(),
tx_pool.clone(),
p2p_server.peers.clone(),
);
warn!(LOGGER, "Grin server started.");
Ok(Server {
config: config,
evt_handle: evt_handle.clone(),
p2p: p2p_server,
chain: shared_chain,
tx_pool: tx_pool,
currently_syncing: currently_syncing,
})
}
/// Asks the server to connect to a peer at the provided network address.
pub fn connect_peer(&self, addr: SocketAddr) -> Result<(), Error> {
let handle = self.evt_handle.clone();
handle.spawn(
self.p2p
.connect_peer(addr, handle.clone())
.map(|_| ())
.map_err(|_| ()),
);
Ok(())
}
/// Number of peers
pub fn peer_count(&self) -> u32 {
self.p2p.peers.peer_count()
}
/// Start mining for blocks on a separate thread. Uses toy miner by default,
/// mostly for testing, but can also load a plugin from cuckoo-miner
pub fn start_miner(&self, config: pow::types::MinerConfig) {
let cuckoo_size = global::sizeshift();
let proof_size = global::proofsize();
let currently_syncing = self.currently_syncing.clone();
let mut miner = miner::Miner::new(config.clone(), self.chain.clone(), self.tx_pool.clone());
miner.set_debug_output_id(format!("Port {}", self.config.p2p_config.unwrap().port));
let _ = thread::Builder::new()
.name("miner".to_string())
.spawn(move || {
// TODO push this down in the run loop so miner gets paused anytime we
// decide to sync again
let secs_5 = time::Duration::from_secs(5);
while currently_syncing.load(Ordering::Relaxed) {
thread::sleep(secs_5);
}
miner.run_loop(config.clone(), cuckoo_size as u32, proof_size);
});
}
/// The chain head
pub fn head(&self) -> chain::Tip {
self.chain.head().unwrap()
}
/// Returns a set of stats about this server. This and the ServerStats
/// structure
/// can be updated over time to include any information needed by tests or
/// other
/// consumers
pub fn get_server_stats(&self) -> Result<ServerStats, Error> {
Ok(ServerStats {
peer_count: self.peer_count(),
head: self.head(),
})
}
}