mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 11:31: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]
|
[package]
|
||||||
name = "grin"
|
name = "grin_grin"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
||||||
|
|
||||||
|
@ -7,9 +7,11 @@ authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
||||||
grin_chain = { path = "../chain" }
|
grin_chain = { path = "../chain" }
|
||||||
grin_core = { path = "../core" }
|
grin_core = { path = "../core" }
|
||||||
grin_store = { path = "../store" }
|
grin_store = { path = "../store" }
|
||||||
|
grin_p2p = { path = "../p2p" }
|
||||||
secp256k1zkp = { path = "../secp256k1zkp" }
|
secp256k1zkp = { path = "../secp256k1zkp" }
|
||||||
|
|
||||||
env_logger="^0.3.5"
|
env_logger="^0.3.5"
|
||||||
log = "^0.3"
|
log = "^0.3"
|
||||||
|
mioco = "^0.8"
|
||||||
time = "^0.1"
|
time = "^0.1"
|
||||||
rand = "^0.3"
|
rand = "^0.3"
|
||||||
|
|
|
@ -24,26 +24,17 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
|
extern crate mioco;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
extern crate grin_chain as chain;
|
extern crate grin_chain as chain;
|
||||||
extern crate grin_core as core;
|
extern crate grin_core as core;
|
||||||
|
extern crate grin_p2p as p2p;
|
||||||
extern crate grin_store as store;
|
extern crate grin_store as store;
|
||||||
extern crate secp256k1zkp as secp;
|
extern crate secp256k1zkp as secp;
|
||||||
|
|
||||||
mod miner;
|
mod miner;
|
||||||
|
mod server;
|
||||||
|
|
||||||
use store::Store;
|
pub use server::{Server, ServerConfig};
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,18 +29,18 @@ use chain;
|
||||||
use secp;
|
use secp;
|
||||||
|
|
||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
chain_state: Arc<Mutex<chain::State>>,
|
chain_head: Arc<Mutex<chain::Tip>>,
|
||||||
chain_store: Arc<Mutex<chain::ChainStore>>,
|
chain_store: Arc<Mutex<chain::ChainStore>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Miner {
|
impl Miner {
|
||||||
/// Creates a new Miner. Needs references to the chain state and its
|
/// Creates a new Miner. Needs references to the chain state and its
|
||||||
/// storage.
|
/// 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>>)
|
chain_store: Arc<Mutex<chain::ChainStore>>)
|
||||||
-> Miner {
|
-> Miner {
|
||||||
Miner {
|
Miner {
|
||||||
chain_state: chain_state,
|
chain_head: chain_head,
|
||||||
chain_store: chain_store,
|
chain_store: chain_store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,13 +48,14 @@ impl Miner {
|
||||||
/// Starts the mining loop, building a new block on top of the existing
|
/// Starts the mining loop, building a new block on top of the existing
|
||||||
/// chain anytime required and looking for PoW solution.
|
/// chain anytime required and looking for PoW solution.
|
||||||
pub fn run_loop(&self) {
|
pub fn run_loop(&self) {
|
||||||
|
info!("Starting miner loop.");
|
||||||
loop {
|
loop {
|
||||||
// get the latest chain state and build a block on top of it
|
// get the latest chain state and build a block on top of it
|
||||||
let head: core::BlockHeader;
|
let head: core::BlockHeader;
|
||||||
let mut latest_hash: Hash;
|
let mut latest_hash: Hash;
|
||||||
{
|
{
|
||||||
head = self.chain_store.lock().unwrap().head_header().unwrap();
|
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 b = self.build_block(&head);
|
||||||
let mut pow_header = pow::PowHeader::from_block(&b);
|
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
|
// transactions) and as long as the head hasn't changed
|
||||||
let deadline = time::get_time().sec + 2;
|
let deadline = time::get_time().sec + 2;
|
||||||
let mut sol = None;
|
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 {
|
while head.hash() == latest_hash && time::get_time().sec < deadline {
|
||||||
let pow_hash = pow_header.hash();
|
let pow_hash = pow_header.hash();
|
||||||
let mut miner = cuckoo::Miner::new(pow_hash.to_slice(),
|
let mut miner = cuckoo::Miner::new(pow_hash.to_slice(),
|
||||||
|
@ -76,17 +78,20 @@ impl Miner {
|
||||||
}
|
}
|
||||||
pow_header.nonce += 1;
|
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 we found a solution, push our block out
|
||||||
if let Some(proof) = sol {
|
if let Some(proof) = sol {
|
||||||
|
info!("Found valid proof of work, adding block {}.", b.hash());
|
||||||
if let Err(e) = chain::process_block(&b,
|
if let Err(e) = chain::process_block(&b,
|
||||||
self.chain_store.lock().unwrap().deref(),
|
self.chain_store.lock().unwrap().deref(),
|
||||||
chain::NONE) {
|
chain::NONE) {
|
||||||
error!("Error validating mined block: {:?}", e);
|
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