mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
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:
parent
309cb497f8
commit
0c52665f17
5 changed files with 195 additions and 20 deletions
|
@ -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"
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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,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...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
120
grin/src/server.rs
Normal file
120
grin/src/server.rs
Normal 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
57
grin/tests/simulnet.rs
Normal 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(())
|
||||
});
|
||||
|
||||
}
|
Loading…
Reference in a new issue