Server abstraction aggregating underlying modules (p2p, chain, etc.). Beginning of a test to start multiple servers, connect them and mine.

This commit is contained in:
Ignotus Peverell 2016-11-29 18:51:36 -08:00
parent 309cb497f8
commit 0c52665f17
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
5 changed files with 195 additions and 20 deletions

View file

@ -1,5 +1,5 @@
[package]
name = "grin"
name = "grin_grin"
version = "0.1.0"
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
@ -7,9 +7,11 @@ authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
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"

View file

@ -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};

View file

@ -29,18 +29,18 @@ use chain;
use secp;
pub struct Miner {
chain_state: Arc<Mutex<chain::State>>,
chain_head: Arc<Mutex<chain::Tip>>,
chain_store: Arc<Mutex<chain::ChainStore>>,
}
impl Miner {
/// Creates a new Miner. Needs references to the chain state and its
/// storage.
pub fn new(chain_state: Arc<Mutex<chain::State>>,
pub fn new(chain_head: Arc<Mutex<chain::Tip>>,
chain_store: Arc<Mutex<chain::ChainStore>>)
-> 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,17 +78,20 @@ 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...")
}
}
}

120
grin/src/server.rs Normal file
View file

@ -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<p2p::Server>,
/// the reference copy of the current chain state
chain_head: Arc<Mutex<chain::Tip>>,
/// data store access
chain_store: Arc<Mutex<chain::ChainStore>>,
}
impl Server {
/// Instantiates and starts a new server.
pub fn start(config: ServerConfig) -> Result<Server, Error> {
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<A: ToSocketAddrs>(&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();
});
}
}

57
grin/tests/simulnet.rs Normal file
View file

@ -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(())
});
}