From 0c52665f1720f85240540a668f081a5fb418cfb0 Mon Sep 17 00:00:00 2001 From: Ignotus Peverell Date: Tue, 29 Nov 2016 18:51:36 -0800 Subject: [PATCH] Server abstraction aggregating underlying modules (p2p, chain, etc.). Beginning of a test to start multiple servers, connect them and mine. --- grin/Cargo.toml | 4 +- grin/src/lib.rs | 17 ++---- grin/src/miner.rs | 17 +++--- grin/src/server.rs | 120 +++++++++++++++++++++++++++++++++++++++++ grin/tests/simulnet.rs | 57 ++++++++++++++++++++ 5 files changed, 195 insertions(+), 20 deletions(-) create mode 100644 grin/src/server.rs create mode 100644 grin/tests/simulnet.rs diff --git a/grin/Cargo.toml b/grin/Cargo.toml index 9d593148..c42690d8 100644 --- a/grin/Cargo.toml +++ b/grin/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "grin" +name = "grin_grin" version = "0.1.0" authors = ["Ignotus Peverell "] @@ -7,9 +7,11 @@ authors = ["Ignotus Peverell "] grin_chain = { path = "../chain" } grin_core = { path = "../core" } grin_store = { path = "../store" } +grin_p2p = { path = "../p2p" } secp256k1zkp = { path = "../secp256k1zkp" } env_logger="^0.3.5" log = "^0.3" +mioco = "^0.8" time = "^0.1" rand = "^0.3" diff --git a/grin/src/lib.rs b/grin/src/lib.rs index b9bce550..217047c8 100644 --- a/grin/src/lib.rs +++ b/grin/src/lib.rs @@ -24,26 +24,17 @@ #[macro_use] extern crate log; extern crate env_logger; +extern crate mioco; extern crate rand; extern crate time; extern crate grin_chain as chain; extern crate grin_core as core; +extern crate grin_p2p as p2p; extern crate grin_store as store; extern crate secp256k1zkp as secp; mod miner; +mod server; -use store::Store; -use core::genesis::genesis; - -fn main() { - env_logger::init().unwrap(); - - let gen = genesis(); - let db = Store::open("./store").unwrap(); - let mut key = "block:".to_string().into_bytes(); - let mut hash_vec = gen.hash().to_vec(); - key.append(&mut hash_vec); - db.put_ser(&key[..], &gen); -} +pub use server::{Server, ServerConfig}; diff --git a/grin/src/miner.rs b/grin/src/miner.rs index 49514b23..a8bff983 100644 --- a/grin/src/miner.rs +++ b/grin/src/miner.rs @@ -29,18 +29,18 @@ use chain; use secp; pub struct Miner { - chain_state: Arc>, + chain_head: Arc>, chain_store: Arc>, } impl Miner { /// Creates a new Miner. Needs references to the chain state and its /// storage. - pub fn new(chain_state: Arc>, + pub fn new(chain_head: Arc>, chain_store: Arc>) -> Miner { Miner { - chain_state: chain_state, + chain_head: chain_head, chain_store: chain_store, } } @@ -48,13 +48,14 @@ impl Miner { /// Starts the mining loop, building a new block on top of the existing /// chain anytime required and looking for PoW solution. pub fn run_loop(&self) { + info!("Starting miner loop."); loop { // get the latest chain state and build a block on top of it let head: core::BlockHeader; let mut latest_hash: Hash; { head = self.chain_store.lock().unwrap().head_header().unwrap(); - latest_hash = self.chain_state.lock().unwrap().head.last_block_h; + latest_hash = self.chain_head.lock().unwrap().last_block_h; } let b = self.build_block(&head); let mut pow_header = pow::PowHeader::from_block(&b); @@ -63,6 +64,7 @@ impl Miner { // transactions) and as long as the head hasn't changed let deadline = time::get_time().sec + 2; let mut sol = None; + debug!("Mining at Cuckoo{} for at most 2 secs.", b.header.cuckoo_len); while head.hash() == latest_hash && time::get_time().sec < deadline { let pow_hash = pow_header.hash(); let mut miner = cuckoo::Miner::new(pow_hash.to_slice(), @@ -76,18 +78,21 @@ impl Miner { } pow_header.nonce += 1; { - latest_hash = self.chain_state.lock().unwrap().head.last_block_h; + latest_hash = self.chain_head.lock().unwrap().last_block_h; } } // if we found a solution, push our block out if let Some(proof) = sol { + info!("Found valid proof of work, adding block {}.", b.hash()); if let Err(e) = chain::process_block(&b, self.chain_store.lock().unwrap().deref(), chain::NONE) { error!("Error validating mined block: {:?}", e); } - } + } else { + debug!("No solution found, continuing...") + } } } diff --git a/grin/src/server.rs b/grin/src/server.rs new file mode 100644 index 00000000..6f1e9999 --- /dev/null +++ b/grin/src/server.rs @@ -0,0 +1,120 @@ +// 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 mioco; +use std::io; +use std::net::ToSocketAddrs; +use std::sync::{Arc, Mutex}; +use std::thread; + +use chain; +use chain::ChainStore; +use core; +use miner; +use p2p; + +/// Errors than can be reported by a server implementation, mostly wraps underlying components errors. +#[derive(Debug)] +pub enum Error { + /// Error when trying to add a block to the chain + ChainErr(chain::pipe::Error), + /// Peer connection error + PeerErr(core::ser::Error), + /// Data store error + StoreErr(chain::types::Error), +} + +/// Full server configuration, aggregating configurations required for the different components. +#[derive(Debug, Clone)] +pub struct ServerConfig { + /// Directory under which the rocksdb stores will be created + pub db_root: String, + /// Allows overriding the default cuckoo cycle size + pub cuckoo_size: u8, + /// Configuration for the peer-to-peer server + pub p2p_config: p2p::P2PConfig, +} + +impl Default for ServerConfig { + fn default() -> ServerConfig { + ServerConfig{ + db_root: ".grin".to_string(), + cuckoo_size: 0, + p2p_config: p2p::P2PConfig::default(), + } + } +} + +/// Grin server holding internal structures. +pub struct Server { + config: ServerConfig, + /// handle to our network server + p2p: Arc, + /// the reference copy of the current chain state + chain_head: Arc>, + /// data store access + chain_store: Arc>, +} + +impl Server { + /// Instantiates and starts a new server. + pub fn start(config: ServerConfig) -> Result { + let chain_store = try!(chain::store::ChainKVStore::new(config.db_root.clone()).map_err(&Error::StoreErr)); + + // check if we have a head in store, otherwise the genesis block is it + let head = match chain_store.head() { + Ok(tip) => tip, + Err(chain::types::Error::NotFoundErr) => { + let mut gen = core::genesis::genesis(); + if config.cuckoo_size > 0 { + gen.header.cuckoo_len = config.cuckoo_size; + } + let tip = chain::types::Tip::new(gen.hash()); + try!(chain_store.save_tip(&tip).map_err(&Error::StoreErr)); + tip + }, + Err(e) => return Err(Error::StoreErr(e)), + }; + + let server = Arc::new(p2p::Server::new(config.p2p_config)); + let in_server = server.clone(); + mioco::spawn(move || -> io::Result<()> { + try!(in_server.start().map_err(|_| io::Error::last_os_error())); + Ok(()) + }); + + warn!("Grin server started."); + Ok(Server{ + config: config, + p2p: server, + chain_head: Arc::new(Mutex::new(head)), + chain_store: Arc::new(Mutex::new(chain_store)), + }) + } + + /// Asks the server to connect to a peer at the provided network address. + pub fn connect_peer(&self, addr: A) -> Result<(), Error> { + self.p2p.connect_peer(addr).map_err(&Error::PeerErr) + } + + /// Start mining for blocks on a separate thread. Relies on a toy miner, mostly for testing. + pub fn start_miner(&self) { + let miner = miner::Miner::new(self.chain_head.clone(), self.chain_store.clone()); + thread::spawn(move || { + miner.run_loop(); + }); + } +} diff --git a/grin/tests/simulnet.rs b/grin/tests/simulnet.rs new file mode 100644 index 00000000..1f6dc479 --- /dev/null +++ b/grin/tests/simulnet.rs @@ -0,0 +1,57 @@ +// 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. + +extern crate grin_grin as grin; +extern crate grin_core as core; +extern crate grin_p2p as p2p; +extern crate grin_chain as chain; +extern crate mioco; +extern crate env_logger; + +use std::io; +use std::time; + +#[test] +fn simulate_servers() { + env_logger::init().unwrap(); + + mioco::start(move || -> io::Result<()> { + // instantiates 5 servers on different ports + let mut servers = vec![]; + for n in 0..5 { + let s = grin::Server::start( + grin::ServerConfig{ + db_root: format!("target/grin-{}", n), + cuckoo_size: 18, + p2p_config: p2p::P2PConfig{port: 10000+n, ..p2p::P2PConfig::default()} + }).unwrap(); + servers.push(s); + } + + mioco::sleep(time::Duration::from_millis(100)); + + // everyone connects to everyone else + for n in 0..5 { + for m in 0..5 { + if m == n { continue } + let addr = format!("{}:{}", "127.0.0.1", 10000+m); + servers[n].connect_peer(addr.as_str()).unwrap(); + mioco::sleep(time::Duration::from_millis(100)); + println!("c {}", m); + } + } + Ok(()) + }); + +}