From 045f5bb4daff4b7cc4717eaa527ac21b4bdf1a6c Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Tue, 22 Aug 2017 19:23:54 +0100 Subject: [PATCH] POW refactor (#100) * Adding output stats from plugin, when available * adding grin config * moving pow crate from core/pow to it's own crate * moving POW/mining stuff into pow module, and removing mining dependency from chain * refactored most of mining into pow module...miner.rs still in server * update grin tests * updating genesis block mining to use configured miner, if available * chain tests back into chain, done with pow refactor * use tag of cuckoo-miner for pr --- .travis.yml | 1 + Cargo.toml | 1 + chain/Cargo.toml | 5 +- chain/src/chain.rs | 35 ++++-- chain/src/pipe.rs | 5 +- chain/src/types.rs | 2 + chain/tests/mine_simple_chain.rs | 58 +++++----- config/Cargo.toml | 1 + config/src/config.rs | 5 +- config/src/lib.rs | 1 + config/src/types.rs | 5 +- core/src/global.rs | 27 ++++- core/src/lib.rs | 1 - grin.toml | 5 + grin/Cargo.toml | 9 +- grin/src/lib.rs | 11 +- grin/src/miner.rs | 160 ++++++++++++++++---------- grin/src/server.rs | 18 ++- grin/src/types.rs | 58 +--------- grin/tests/framework.rs | 4 +- grin/tests/simulnet.rs | 5 +- pow/Cargo.toml | 24 ++++ {core/src/pow => pow/src}/cuckoo.rs | 8 +- core/src/pow/mod.rs => pow/src/lib.rs | 95 ++++++++++++--- {grin => pow}/src/plugin.rs | 14 ++- {core/src/pow => pow/src}/siphash.rs | 0 pow/src/types.rs | 74 ++++++++++++ 27 files changed, 417 insertions(+), 215 deletions(-) create mode 100644 pow/Cargo.toml rename {core/src/pow => pow/src}/cuckoo.rs (99%) rename core/src/pow/mod.rs => pow/src/lib.rs (60%) rename {grin => pow}/src/plugin.rs (96%) rename {core/src/pow => pow/src}/siphash.rs (100%) create mode 100644 pow/src/types.rs diff --git a/.travis.yml b/.travis.yml index c89b8cb28..0b2addb92 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ env: - TEST_DIR=p2p - TEST_DIR=api - TEST_DIR=pool + - TEST_DIR=pow - RUST_TEST_THREADS=1 TEST_DIR=grin script: cd $TEST_DIR && cargo test --verbose diff --git a/Cargo.toml b/Cargo.toml index 90c415431..cae2adcaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ grin_wallet = { path = "./wallet" } grin_grin = { path = "./grin" } grin_config = { path = "./config" } grin_core = { path = "./core" } +grin_pow = { path = "./pow"} secp256k1zkp = { path = "./secp256k1zkp" } blake2-rfc = "~0.2.17" diff --git a/chain/Cargo.toml b/chain/Cargo.toml index f6702b031..d763afb9d 100644 --- a/chain/Cargo.toml +++ b/chain/Cargo.toml @@ -19,8 +19,5 @@ secp256k1zkp = { path = "../secp256k1zkp" } [dev-dependencies] env_logger="^0.3.5" rand = "^0.3" +grin_pow = { path = "../pow" } -#just to instantiate a mining worker during a test -#while the miner implementation supports both -#cuckoo_miner and the built-in version -grin_grin = { path = "../grin" } diff --git a/chain/src/chain.rs b/chain/src/chain.rs index ed3724e86..86132a6d4 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -23,14 +23,11 @@ use secp::pedersen::Commitment; use core::core::{Block, BlockHeader, Output}; use core::core::target::Difficulty; use core::core::hash::Hash; -use core::{consensus, genesis, pow}; -use core::pow::MiningWorker; use grin_store; use pipe; use store; use types::*; -use core::global; use core::global::{MiningParameterMode,MINING_PARAMETER_MODE}; const MAX_ORPHANS: usize = 20; @@ -57,12 +54,28 @@ pub struct Chain { head: Arc>, block_process_lock: Arc>, orphans: Arc>>, + + //POW verification function + pow_verifier: fn(&BlockHeader, u32) -> bool, } unsafe impl Sync for Chain {} unsafe impl Send for Chain {} impl Chain { + + /// Check whether the chain exists. If not, the call to 'init' will + /// expect an already mined genesis block. This keeps the chain free + /// from needing to know about the mining implementation + pub fn chain_exists(db_root: String)->bool { + let chain_store = store::ChainKVStore::new(db_root).unwrap(); + match chain_store.head() { + Ok(_) => {true}, + Err(grin_store::Error::NotFoundErr) => false, + Err(_) => false, + } + } + /// Initializes the blockchain and returns a new Chain instance. Does a /// check /// on the current chain head to make sure it exists and creates one based @@ -71,6 +84,8 @@ impl Chain { pub fn init( db_root: String, adapter: Arc, + gen_block: Option, + pow_verifier: fn(&BlockHeader, u32) -> bool, ) -> Result { let chain_store = store::ChainKVStore::new(db_root)?; @@ -78,15 +93,11 @@ impl Chain { let head = match chain_store.head() { Ok(tip) => tip, Err(grin_store::Error::NotFoundErr) => { - info!("No genesis block found, creating and saving one."); - let mut gen = genesis::genesis(); - let diff = gen.header.difficulty.clone(); - - let sz = global::sizeshift(); - let proof_size = global::proofsize(); + if let None = gen_block { + return Err(Error::GenesisBlockRequired); + } - let mut internal_miner = pow::cuckoo::Miner::new(consensus::EASINESS, sz as u32, proof_size); - pow::pow_size(&mut internal_miner, &mut gen.header, diff, sz as u32).unwrap(); + let gen = gen_block.unwrap(); chain_store.save_block(&gen)?; // saving a new tip based on genesis @@ -107,6 +118,7 @@ impl Chain { head: Arc::new(Mutex::new(head)), block_process_lock: Arc::new(Mutex::new(true)), orphans: Arc::new(Mutex::new(VecDeque::with_capacity(MAX_ORPHANS + 1))), + pow_verifier: pow_verifier, }) } @@ -170,6 +182,7 @@ impl Chain { store: self.store.clone(), adapter: self.adapter.clone(), head: head, + pow_verifier: self.pow_verifier, lock: self.block_process_lock.clone(), } } diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index bf2819162..82b3fad8e 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -22,7 +22,6 @@ use time; use core::consensus; use core::core::hash::{Hash, Hashed}; use core::core::{BlockHeader, Block}; -use core::pow; use types::*; use store; use core::global; @@ -38,6 +37,8 @@ pub struct BlockContext { pub adapter: Arc, /// The head pub head: Tip, + /// The POW verification function + pub pow_verifier: fn(&BlockHeader, u32) -> bool, /// The lock pub lock: Arc>, } @@ -157,7 +158,7 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E consensus::DEFAULT_SIZESHIFT }; debug!("Validating block with cuckoo size {}", cycle_size); - if !pow::verify_size(header, cycle_size as u32) { + if !(ctx.pow_verifier)(header, cycle_size as u32) { return Err(Error::InvalidPow); } } diff --git a/chain/src/types.rs b/chain/src/types.rs index 413f9d18b..e47b89816 100644 --- a/chain/src/types.rs +++ b/chain/src/types.rs @@ -61,6 +61,8 @@ pub enum Error { StoreErr(grin_store::Error), /// Error serializing or deserializing a type SerErr(ser::Error), + /// No chain exists and genesis block is required + GenesisBlockRequired, /// Anything else Other(String), } diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 54d9e6fae..278724542 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Grin Developers +// Copyright 2017 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. @@ -12,32 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate grin_core; -extern crate grin_chain; +extern crate grin_core as core; +extern crate grin_chain as chain; extern crate env_logger; extern crate time; extern crate rand; extern crate secp256k1zkp as secp; - -extern crate grin_grin as grin; +extern crate grin_pow as pow; use std::fs; use std::sync::Arc; use std::thread; use rand::os::OsRng; -use grin_chain::types::*; -use grin_core::core::hash::Hashed; -use grin_core::core::target::Difficulty; -use grin_core::pow; -use grin_core::core; -use grin_core::consensus; -use grin_core::pow::cuckoo; -use grin_core::global; -use grin_core::global::MiningParameterMode; - -use grin_core::pow::MiningWorker; +use chain::types::*; +use core::core::hash::Hashed; +use core::core::target::Difficulty; +use core::consensus; +use core::global; +use core::global::MiningParameterMode; +use pow::{types, cuckoo, MiningWorker}; fn clean_output_dir(dir_name:&str){ let _ = fs::remove_dir_all(dir_name); @@ -50,14 +45,18 @@ fn mine_empty_chain() { global::set_mining_mode(MiningParameterMode::AutomatedTesting); let mut rng = OsRng::new().unwrap(); - let chain = grin_chain::Chain::init(".grin".to_string(), Arc::new(NoopAdapter {})) - .unwrap(); + let mut genesis_block = None; + if !chain::Chain::chain_exists(".grin".to_string()){ + genesis_block=pow::mine_genesis_block(None); + } + let chain = chain::Chain::init(".grin".to_string(), Arc::new(NoopAdapter {}), + genesis_block, pow::verify_size).unwrap(); // mine and add a few blocks let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); let reward_key = secp::key::SecretKey::new(&secp, &mut rng); - let mut miner_config = grin::MinerConfig { + let mut miner_config = types::MinerConfig { enable_mining: true, burn_reward: true, ..Default::default() @@ -67,7 +66,7 @@ fn mine_empty_chain() { let mut cuckoo_miner = cuckoo::Miner::new(consensus::EASINESS, global::sizeshift() as u32, global::proofsize()); for n in 1..4 { let prev = chain.head_header().unwrap(); - let mut b = core::Block::new(&prev, vec![], reward_key).unwrap(); + let mut b = core::core::Block::new(&prev, vec![], reward_key).unwrap(); b.header.timestamp = prev.timestamp + time::Duration::seconds(60); let difficulty = consensus::next_difficulty(chain.difficulty_iter()).unwrap(); @@ -81,7 +80,7 @@ fn mine_empty_chain() { ).unwrap(); let bhash = b.hash(); - chain.process_block(b, grin_chain::EASY_POW).unwrap(); + chain.process_block(b, chain::EASY_POW).unwrap(); // checking our new head let head = chain.head().unwrap(); @@ -96,8 +95,13 @@ fn mine_forks() { clean_output_dir(".grin2"); let mut rng = OsRng::new().unwrap(); - let chain = grin_chain::Chain::init(".grin2".to_string(), Arc::new(NoopAdapter {})) - .unwrap(); + + let mut genesis_block = None; + if !chain::Chain::chain_exists(".grin2".to_string()){ + genesis_block=pow::mine_genesis_block(None); + } + let chain = chain::Chain::init(".grin2".to_string(), Arc::new(NoopAdapter {}), + genesis_block, pow::verify_size).unwrap(); // mine and add a few blocks let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); @@ -105,11 +109,11 @@ fn mine_forks() { for n in 1..4 { let prev = chain.head_header().unwrap(); - let mut b = core::Block::new(&prev, vec![], reward_key).unwrap(); + let mut b = core::core::Block::new(&prev, vec![], reward_key).unwrap(); b.header.timestamp = prev.timestamp + time::Duration::seconds(60); b.header.total_difficulty = Difficulty::from_num(2 * n); let bhash = b.hash(); - chain.process_block(b, grin_chain::SKIP_POW).unwrap(); + chain.process_block(b, chain::SKIP_POW).unwrap(); // checking our new head thread::sleep(::std::time::Duration::from_millis(50)); @@ -119,11 +123,11 @@ fn mine_forks() { assert_eq!(head.prev_block_h, prev.hash()); // build another block with higher difficulty - let mut b = core::Block::new(&prev, vec![], reward_key).unwrap(); + let mut b = core::core::Block::new(&prev, vec![], reward_key).unwrap(); b.header.timestamp = prev.timestamp + time::Duration::seconds(60); b.header.total_difficulty = Difficulty::from_num(2 * n + 1); let bhash = b.hash(); - chain.process_block(b, grin_chain::SKIP_POW).unwrap(); + chain.process_block(b, chain::SKIP_POW).unwrap(); // checking head switch thread::sleep(::std::time::Duration::from_millis(50)); diff --git a/config/Cargo.toml b/config/Cargo.toml index f8853e543..7c98631b3 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -10,5 +10,6 @@ serde_derive = "~1.0.8" toml = "0.4" grin_grin = { path = "../grin" } +grin_pow = { path = "../pow"} grin_p2p = { path = "../p2p" } grin_wallet = { path = "../wallet"} diff --git a/config/src/config.rs b/config/src/config.rs index 048315a13..876d46fb3 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -20,9 +20,8 @@ use std::path::PathBuf; use std::fs::File; use toml; -use grin::{ServerConfig, - MinerConfig}; - +use grin::ServerConfig; +use pow::types::MinerConfig; use types::{ConfigMembers, GlobalConfig, ConfigError}; diff --git a/config/src/lib.rs b/config/src/lib.rs index c4ecf78b9..9f1e3823a 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -29,6 +29,7 @@ extern crate toml; extern crate grin_grin as grin; extern crate grin_p2p as p2p; extern crate grin_wallet as wallet; +extern crate grin_pow as pow; pub mod config; pub mod types; diff --git a/config/src/types.rs b/config/src/types.rs index 8a7f2bf86..13a2c5d01 100644 --- a/config/src/types.rs +++ b/config/src/types.rs @@ -18,9 +18,8 @@ use std::path::PathBuf; use std::io; use std::fmt; -use grin::{ServerConfig, - MinerConfig}; - +use grin::ServerConfig; +use pow::types::MinerConfig; /// Error type wrapping config errors. #[derive(Debug)] diff --git a/core/src/global.rs b/core/src/global.rs index cf371b126..fb0d676a5 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -94,6 +94,17 @@ pub fn is_automated_testing_mode() -> bool { } } +/// Are we in production mode? +pub fn is_production_mode() -> bool { + let param_ref=MINING_PARAMETER_MODE.read().unwrap(); + if let MiningParameterMode::Production=*param_ref { + return true; + } else { + return false; + } +} + + /// Helper function to get a nonce known to create a valid POW on /// the genesis block, to prevent it taking ages. Should be fine for now /// as the genesis block POW solution turns out to be the same for every new block chain @@ -104,6 +115,20 @@ pub fn get_genesis_nonce() -> u64 { match *param_ref { MiningParameterMode::AutomatedTesting => 0, //won't make a difference MiningParameterMode::UserTesting => 22141, //Magic nonce for current genesis block at cuckoo16 - MiningParameterMode::Production => 0, //TBD + MiningParameterMode::Production => 1429942738856787200, //Magic nonce for current genesis at cuckoo30 } } + +/// Returns the genesis POW for production, because it takes far too long to mine at production values +/// using the internal miner + +pub fn get_genesis_pow() -> [u32;42]{ + //TODO: This is diff 26, probably just want a 10: mine one + [7444824, 11926557, 28520390, 30594072, 50854023, 52797085, 57882033, + 59816511, 61404804, 84947619, 87779345, 115270337, 162618676, + 166860710, 178656003, 178971372, 200454733, 209197630, 221231015, + 228598741, 241012783, 245401183, 279080304, 295848517, 327300943, + 329741709, 366394532, 382493153, 389329248, 404353381, 406012911, + 418813499, 426573907, 452566575, 456930760, 463021458, 474340589, + 476248039, 478197093, 487576917, 495653489, 501862896] +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 9fd4d457f..49a494a7d 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -42,6 +42,5 @@ pub mod macros; pub mod consensus; pub mod core; pub mod genesis; -pub mod pow; pub mod ser; pub mod global; diff --git a/grin.toml b/grin.toml index f85c11adc..97d99478c 100644 --- a/grin.toml +++ b/grin.toml @@ -90,6 +90,11 @@ cuckoo_miner_plugin_type = "simple" #the list of parameters if you're using "edgetrim" #cuckoo_miner_parameter_list = {NUM_THREADS=4, NUM_TRIMS=7} +#The amount of time, in seconds, to attempt to mine on a particular +#header before stopping and re-collecting transactions from the pool + +attempt_time_per_block = 30 + #the wallet reciever to which coinbase rewards will be sent wallet_receiver_url = "http://127.0.0.1:13415" diff --git a/grin/Cargo.toml b/grin/Cargo.toml index 66192df01..f0d5caea7 100644 --- a/grin/Cargo.toml +++ b/grin/Cargo.toml @@ -13,12 +13,9 @@ grin_p2p = { path = "../p2p" } grin_pool = { path = "../pool" } grin_util = { path = "../util" } grin_wallet = { path = "../wallet" } +grin_pow = { path = "../pow" } secp256k1zkp = { path = "../secp256k1zkp" } -cuckoo_miner = { git = "https://github.com/mimblewimble/cuckoo-miner", tag="grin_integration_5"} -#cuckoo_miner = { path = "../../cuckoo-miner"} - -blake2-rfc = "~0.2.17" env_logger="^0.3.5" futures = "^0.1.9" futures-cpupool = "^0.1.3" @@ -30,5 +27,7 @@ serde_derive = "~1.0.8" tokio-core="^0.1.1" tokio-timer="^0.1.0" rand = "^0.3" -lazy_static = "~0.2.8" itertools = "~0.6.0" + +[dev_dependencies] +blake2-rfc = "~0.2.17" diff --git a/grin/src/lib.rs b/grin/src/lib.rs index 33863011f..9f141450a 100644 --- a/grin/src/lib.rs +++ b/grin/src/lib.rs @@ -34,8 +34,6 @@ extern crate serde_derive; extern crate time; extern crate tokio_core; extern crate tokio_timer; -#[macro_use] -extern crate lazy_static; extern crate itertools; extern crate grin_api as api; @@ -47,18 +45,15 @@ extern crate grin_pool as pool; extern crate grin_store as store; extern crate grin_util as util; extern crate grin_wallet as wallet; +extern crate grin_pow as pow; extern crate secp256k1zkp as secp; -extern crate cuckoo_miner; - mod adapters; -mod miner; -mod plugin; mod server; mod seed; mod sync; mod types; +mod miner; pub use server::{Server}; -pub use types::{ServerConfig, MinerConfig, Seeding, ServerStats}; -pub use plugin::PluginMiner; +pub use types::{ServerConfig, Seeding, ServerStats}; diff --git a/grin/src/miner.rs b/grin/src/miner.rs index 8d213de74..51524d125 100644 --- a/grin/src/miner.rs +++ b/grin/src/miner.rs @@ -27,24 +27,28 @@ use api; use core::consensus; use core::core; use core::core::Proof; -use core::pow::cuckoo; +use pow::cuckoo; use core::core::target::Difficulty; use core::core::{Block, BlockHeader}; use core::core::hash::{Hash, Hashed}; -use core::pow::MiningWorker; +use pow::MiningWorker; +use pow::types::MinerConfig; use core::ser; use core::ser::{AsFixedBytes}; +//use core::genesis; + use chain; use secp; use pool; -use types::{MinerConfig, ServerConfig}; use util; use wallet::{CbAmount, WalletReceiveRequest, CbData}; -use plugin::PluginMiner; +use pow::plugin::PluginMiner; + use itertools::Itertools; + // Max number of transactions this miner will assemble in a block const MAX_TX: u32 = 5000; @@ -140,33 +144,47 @@ impl Miner { } /// Inner part of the mining loop for cuckoo-miner asynch mode - pub fn inner_loop_async(&self, plugin_miner:&mut PluginMiner, - difficulty:Difficulty, - b:&mut Block, - cuckoo_size: u32, - head:&BlockHeader, - latest_hash:&Hash) - -> Option { + pub fn inner_loop_async(&self, + plugin_miner:&mut PluginMiner, + difficulty:Difficulty, + b:&mut Block, + cuckoo_size: u32, + head:&BlockHeader, + latest_hash:&Hash, + attempt_time_per_block: u32) + -> Option { - debug!("(Server ID: {}) Mining at Cuckoo{} for at most 2 secs at height {} and difficulty {}.", - self.debug_output_id, - cuckoo_size, - b.header.height, - b.header.difficulty); + debug!("(Server ID: {}) Mining at Cuckoo{} for at most {} secs at height {} and difficulty {}.", + self.debug_output_id, + cuckoo_size, + attempt_time_per_block, + b.header.height, + b.header.difficulty); - // look for a pow for at most 2 sec on the same block (to give a chance to new + // look for a pow for at most 10 sec on the same block (to give a chance to new // transactions) and as long as the head hasn't changed // Will change this to something else at some point - let deadline = time::get_time().sec + 2; + let deadline = time::get_time().sec + attempt_time_per_block as i64; + + // how often to output stats + let stat_output_interval = 2; + let mut next_stat_output = time::get_time().sec + stat_output_interval; //Get parts of the header let mut header_parts = HeaderPartWriter::default(); ser::Writeable::write(&b.header, &mut header_parts).unwrap(); let (pre, post) = header_parts.parts_as_hex_strings(); + //Just test output to mine a genesis block when needed + /*let mut header_parts = HeaderPartWriter::default(); + let gen = genesis::genesis(); + ser::Writeable::write(&gen.header, &mut header_parts).unwrap(); + let (pre, post) = header_parts.parts_as_hex_strings(); + println!("pre, post: {}, {}", pre, post);*/ + //Start the miner working - let miner = plugin_miner.get_consumable(); - let job_handle=miner.notify(1, &pre, &post, difficulty.into_num()).unwrap(); + let miner = plugin_miner.get_consumable(); + let job_handle=miner.notify(1, &pre, &post, difficulty.into_num()).unwrap(); let mut sol=None; @@ -174,8 +192,26 @@ impl Miner { if let Some(s) = job_handle.get_solution() { sol = Some(Proof::new(s.solution_nonces.to_vec())); b.header.nonce=s.get_nonce_as_u64(); + println!("Nonce: {}", b.header.nonce); break; } + if time::get_time().sec > next_stat_output { + let stats = job_handle.get_stats(); + if let Ok(stat_vec) = stats { + for s in stat_vec { + if s.last_start_time==0 { + continue; + } + let last_solution_time_secs = s.last_solution_time as f64 / 1000.0; + let last_hashes_per_sec = 1.0 / last_solution_time_secs; + debug!("Mining on Device {} - {}: Last hash time: {} - Hashes per second: {:.*} - Total Attempts: {}", + s.device_id, s.device_name, + last_solution_time_secs, 3, last_hashes_per_sec, + s.iterations_completed); + } + } + next_stat_output = time::get_time().sec + stat_output_interval; + } } if sol==None { debug!("(Server ID: {}) No solution found after {} iterations, continuing...", @@ -190,21 +226,23 @@ impl Miner { /// The inner part of mining loop for synchronous mode pub fn inner_loop_sync(&self, - miner:&mut T, - b:&mut Block, - cuckoo_size: u32, - head:&BlockHeader, - latest_hash:&mut Hash) - -> Option { + miner:&mut T, + b:&mut Block, + cuckoo_size: u32, + head:&BlockHeader, + attempt_time_per_block: u32, + latest_hash:&mut Hash) + -> Option { // look for a pow for at most 2 sec on the same block (to give a chance to new // transactions) and as long as the head hasn't changed - let deadline = time::get_time().sec + 2; + let deadline = time::get_time().sec + attempt_time_per_block as i64; - debug!("(Server ID: {}) Mining at Cuckoo{} for at most 2 secs on block {} at difficulty {}.", - self.debug_output_id, - cuckoo_size, - latest_hash, - b.header.difficulty); + debug!("(Server ID: {}) Mining at Cuckoo{} for at most {} secs on block {} at difficulty {}.", + self.debug_output_id, + cuckoo_size, + attempt_time_per_block, + latest_hash, + b.header.difficulty); let mut iter_count = 0; if self.config.slow_down_in_millis != None && self.config.slow_down_in_millis.unwrap() > 0 { @@ -224,7 +262,7 @@ impl Miner { b.header.difficulty, proof_diff);*/ - if proof_diff >= b.header.difficulty { + if proof_diff >= b.header.difficulty { sol = Some(proof); break; } @@ -241,8 +279,8 @@ impl Miner { if sol==None { debug!("(Server ID: {}) No solution found after {} iterations, continuing...", - self.debug_output_id, - iter_count) + self.debug_output_id, + iter_count) } sol @@ -251,17 +289,16 @@ 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, - miner_config:MinerConfig, - server_config:ServerConfig, - cuckoo_size:u32, - proof_size:usize) { + miner_config:MinerConfig, + cuckoo_size:u32, + proof_size:usize) { info!("(Server ID: {}) Starting miner loop.", self.debug_output_id); let mut plugin_miner=None; let mut miner=None; if miner_config.use_cuckoo_miner { plugin_miner = Some(PluginMiner::new(consensus::EASINESS, cuckoo_size, proof_size)); - plugin_miner.as_mut().unwrap().init(miner_config.clone(),server_config); + plugin_miner.as_mut().unwrap().init(miner_config.clone()); } else { miner = Some(cuckoo::Miner::new(consensus::EASINESS, cuckoo_size, proof_size)); } @@ -284,32 +321,35 @@ impl Miner { if let Some(mut p) = plugin_miner.as_mut() { if use_async { sol = self.inner_loop_async(&mut p, - b.header.difficulty.clone(), - &mut b, - cuckoo_size, - &head, - &latest_hash); + b.header.difficulty.clone(), + &mut b, + cuckoo_size, + &head, + &latest_hash, + miner_config.attempt_time_per_block); } else { sol = self.inner_loop_sync(p, - &mut b, - cuckoo_size, - &head, - &mut latest_hash); + &mut b, + cuckoo_size, + &head, + miner_config.attempt_time_per_block, + &mut latest_hash); } } if let Some(mut m) = miner.as_mut() { sol = self.inner_loop_sync(m, - &mut b, - cuckoo_size, - &head, - &mut latest_hash); + &mut b, + cuckoo_size, + &head, + miner_config.attempt_time_per_block, + &mut latest_hash); } // if we found a solution, push our block out if let Some(proof) = sol { info!("(Server ID: {}) Found valid proof of work, adding block {}.", - self.debug_output_id, b.hash()); - b.header.pow = proof; + self.debug_output_id, b.hash()); + b.header.pow = proof; let opts = if cuckoo_size < consensus::DEFAULT_SIZESHIFT as u32 { chain::EASY_POW } else { @@ -346,10 +386,10 @@ impl Miner { let (output, kernel) = coinbase; let mut b = core::Block::with_reward(head, txs, output, kernel).unwrap(); debug!("(Server ID: {}) Built new block with {} inputs and {} outputs, difficulty: {}", - self.debug_output_id, - b.inputs.len(), - b.outputs.len(), - difficulty); + self.debug_output_id, + b.inputs.len(), + b.outputs.len(), + difficulty); // making sure we're not spending time mining a useless block let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); @@ -370,7 +410,7 @@ impl Miner { core::Block::reward_output(skey, &secp_inst).unwrap() } else { let url = format!("{}/v1/receive/coinbase", - self.config.wallet_receiver_url.as_str()); + self.config.wallet_receiver_url.as_str()); let request = WalletReceiveRequest::Coinbase(CbAmount{amount: consensus::REWARD}); let res: CbData = api::client::post(url.as_str(), &request) diff --git a/grin/src/server.rs b/grin/src/server.rs index 6ac1201c6..7950fdf75 100644 --- a/grin/src/server.rs +++ b/grin/src/server.rs @@ -34,6 +34,7 @@ use pool; use seed; use sync; use types::*; +use pow; use core::global; @@ -81,8 +82,17 @@ impl Server { let tx_pool = Arc::new(RwLock::new(pool::TransactionPool::new(pool_adapter.clone()))); let chain_adapter = Arc::new(ChainToPoolAndNetAdapter::new(tx_pool.clone())); + + let mut genesis_block = None; + if !chain::Chain::chain_exists(config.db_root.clone()){ + genesis_block=pow::mine_genesis_block(config.mining_config.clone()); + } + let shared_chain = Arc::new(chain::Chain::init(config.db_root.clone(), - chain_adapter.clone())?); + chain_adapter.clone(), + genesis_block, + pow::verify_size)?); + pool_adapter.set_chain(shared_chain.clone()); let peer_store = Arc::new(p2p::PeerStore::new(config.db_root.clone())?); @@ -139,16 +149,14 @@ impl Server { /// 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: MinerConfig) { + pub fn start_miner(&self, config: pow::types::MinerConfig) { let cuckoo_size = global::sizeshift(); let proof_size = global::proofsize(); - 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 server_config = self.config.clone(); thread::spawn(move || { - miner.run_loop(config.clone(), server_config, cuckoo_size as u32, proof_size); + miner.run_loop(config.clone(), cuckoo_size as u32, proof_size); }); } diff --git a/grin/src/types.rs b/grin/src/types.rs index 3f5316b76..296300617 100644 --- a/grin/src/types.rs +++ b/grin/src/types.rs @@ -13,12 +13,12 @@ // limitations under the License. use std::convert::From; -use std::collections::HashMap; use api; use chain; use p2p; use store; +use pow; use core::global::MiningParameterMode; /// Error type wrapping underlying module errors. @@ -96,42 +96,7 @@ pub struct ServerConfig { pub p2p_config: Option, /// Configuration for the mining daemon - pub mining_config: Option, -} - -/// Mining configuration -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MinerConfig { - /// Whether to start the miner with the server - pub enable_mining: bool, - - /// Whether to use the cuckoo-miner crate and plugin for mining - pub use_cuckoo_miner: bool, - - /// Whether to use the async version of mining - pub cuckoo_miner_async_mode: Option, - - /// The location in which cuckoo miner plugins are stored - pub cuckoo_miner_plugin_dir: Option, - - /// The type of plugin to use (ends up filtering the filename) - pub cuckoo_miner_plugin_type: Option, - - /// Cuckoo-miner parameters... these vary according - /// to the plugin being loaded - pub cuckoo_miner_parameter_list: Option>, - - /// Base address to the HTTP wallet receiver - pub wallet_receiver_url: String, - - /// Attributes the reward to a random private key instead of contacting the - /// wallet receiver. Mostly used for tests. - pub burn_reward: bool, - - /// a testing attribute for the time being that artifically slows down the - /// mining loop by adding a sleep to the thread - pub slow_down_in_millis: Option, - + pub mining_config: Option, } impl Default for ServerConfig { @@ -143,28 +108,12 @@ impl Default for ServerConfig { seeding_type: Seeding::None, seeds: None, p2p_config: Some(p2p::P2PConfig::default()), - mining_config: Some(MinerConfig::default()), + mining_config: Some(pow::types::MinerConfig::default()), mining_parameter_mode: Some(MiningParameterMode::Production), } } } -impl Default for MinerConfig { - fn default() -> MinerConfig { - MinerConfig { - enable_mining: false, - use_cuckoo_miner: false, - cuckoo_miner_async_mode: None, - cuckoo_miner_plugin_dir: None, - cuckoo_miner_plugin_type: None, - cuckoo_miner_parameter_list: None, - wallet_receiver_url: "http://localhost:13416".to_string(), - burn_reward: false, - slow_down_in_millis: Some(0), - } - } -} - /// Thread-safe container to return all sever related stats that other /// consumers might be interested in, such as test results /// @@ -178,3 +127,4 @@ pub struct ServerStats { /// Chain head pub head: chain::Tip, } + diff --git a/grin/tests/framework.rs b/grin/tests/framework.rs index 948b02bf9..e43fe9c46 100644 --- a/grin/tests/framework.rs +++ b/grin/tests/framework.rs @@ -18,6 +18,7 @@ extern crate grin_p2p as p2p; extern crate grin_chain as chain; extern crate grin_api as api; extern crate grin_wallet as wallet; +extern crate grin_pow as pow; extern crate secp256k1zkp as secp; extern crate blake2_rfc as blake2; @@ -200,7 +201,6 @@ impl LocalServerContainer { seeds=vec![self.config.seed_addr.to_string()]; } - let s = grin::Server::future( grin::ServerConfig{ api_http_addr: api_addr, @@ -219,7 +219,7 @@ impl LocalServerContainer { thread::sleep(time::Duration::from_millis(1000)); } - let miner_config = grin::MinerConfig { + let miner_config = pow::types::MinerConfig { enable_mining: self.config.start_miner, burn_reward: self.config.burn_mining_rewards, use_cuckoo_miner: false, diff --git a/grin/tests/simulnet.rs b/grin/tests/simulnet.rs index edaedadbd..2c670bab0 100644 --- a/grin/tests/simulnet.rs +++ b/grin/tests/simulnet.rs @@ -18,6 +18,7 @@ extern crate grin_p2p as p2p; extern crate grin_chain as chain; extern crate grin_api as api; extern crate grin_wallet as wallet; +extern crate grin_pow as pow; extern crate secp256k1zkp as secp; extern crate env_logger; @@ -200,7 +201,7 @@ fn a_simulate_block_propagation() { let mut evtlp = reactor::Core::new().unwrap(); let handle = evtlp.handle(); - let miner_config = grin::MinerConfig { + let miner_config = pow::types::MinerConfig { enable_mining: true, burn_reward: true, use_cuckoo_miner: false, @@ -267,7 +268,7 @@ fn simulate_full_sync() { let mut evtlp = reactor::Core::new().unwrap(); let handle = evtlp.handle(); - let miner_config = grin::MinerConfig { + let miner_config = pow::types::MinerConfig { enable_mining: true, burn_reward: true, use_cuckoo_miner: false, diff --git a/pow/Cargo.toml b/pow/Cargo.toml new file mode 100644 index 000000000..c382542fb --- /dev/null +++ b/pow/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "grin_pow" +version = "0.1.0" +authors = ["Ignotus Peverell "] +workspace = ".." + +[dependencies] +blake2-rfc = "~0.2.17" +rand = "^0.3" +time = "^0.1" +env_logger="^0.3.5" +log = "^0.3" +lazy_static = "~0.2.8" +serde = "~1.0.8" +serde_derive = "~1.0.8" + +cuckoo_miner = { git = "https://github.com/mimblewimble/cuckoo-miner", tag="grin_integration_6"} +#cuckoo_miner = { path = "../../cuckoo-miner"} + +grin_core = { path = "../core" } + +[dev_dependencies] +grin_chain = { path = "../chain"} +secp256k1zkp = { path = "../secp256k1zkp" } diff --git a/core/src/pow/cuckoo.rs b/pow/src/cuckoo.rs similarity index 99% rename from core/src/pow/cuckoo.rs rename to pow/src/cuckoo.rs index 3e199ef1e..5eddbcea8 100644 --- a/core/src/pow/cuckoo.rs +++ b/pow/src/cuckoo.rs @@ -22,9 +22,9 @@ use std::cmp; use blake2; -use core::Proof; -use pow::siphash::siphash24; -use pow::MiningWorker; +use core::core::Proof; +use siphash::siphash24; +use MiningWorker; const MAXPATHLEN: usize = 8192; @@ -326,7 +326,7 @@ fn u8_to_u64(p:&[u8], i: usize) -> u64 { #[cfg(test)] mod test { use super::*; - use core::Proof; + use core::core::Proof; static V1:[u32;42] = [0x1fe9, 0x2050, 0x4581, 0x6322, 0x65ab, 0xb3c1, 0xc1a4, diff --git a/core/src/pow/mod.rs b/pow/src/lib.rs similarity index 60% rename from core/src/pow/mod.rs rename to pow/src/lib.rs index 847d2b976..39f3f931b 100644 --- a/core/src/pow/mod.rs +++ b/pow/src/lib.rs @@ -22,18 +22,41 @@ //! Note that this miner implementation is here mostly for tests and //! reference. It's not optimized for speed. +#![deny(non_upper_case_globals)] +#![deny(non_camel_case_types)] +#![deny(non_snake_case)] +#![deny(unused_mut)] +#![warn(missing_docs)] + +extern crate blake2_rfc as blake2; +extern crate rand; +extern crate time; +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate log; +extern crate env_logger; +extern crate serde; +#[macro_use] +extern crate serde_derive; + +extern crate grin_core as core; + +extern crate cuckoo_miner; + mod siphash; +pub mod plugin; pub mod cuckoo; +pub mod types; -use time; - -use consensus::EASINESS; -use core::BlockHeader; -use core::hash::Hashed; -use core::Proof; -use core::target::Difficulty; -use pow::cuckoo::{Cuckoo, Error}; - +use core::consensus; +use core::core::BlockHeader; +use core::core::hash::Hashed; +use core::core::Proof; +use core::core::target::Difficulty; +use core::global; +use core::genesis; +use cuckoo::{Cuckoo, Error}; /// Should be implemented by anything providing mining services /// @@ -41,7 +64,7 @@ use pow::cuckoo::{Cuckoo, Error}; pub trait MiningWorker { /// This only sets parameters and does initialisation work now - fn new(ease: u32, sizeshift: u32, proof_size:usize) -> Self; + fn new(ease: u32, sizeshift: u32, proof_size:usize) -> Self where Self:Sized; /// Actually perform a mining attempt on the given input and /// return a proof if found @@ -57,7 +80,7 @@ pub fn verify_size(bh: &BlockHeader, cuckoo_sz: u32) -> bool { if bh.difficulty > bh.pow.clone().to_difficulty() { return false; } - Cuckoo::new(&bh.hash()[..], cuckoo_sz).verify(bh.pow.clone(), EASINESS as u64) + Cuckoo::new(&bh.hash()[..], cuckoo_sz).verify(bh.pow.clone(), consensus::EASINESS as u64) } /// Uses the much easier Cuckoo20 (mostly for @@ -66,12 +89,50 @@ pub fn pow20(miner:&mut T, bh: &mut BlockHeader, diff: Difficul pow_size(miner, bh, diff, 20) } +/// Mines a genesis block, using the config specified miner if specified. Otherwise, +/// uses the internal miner +/// + +pub fn mine_genesis_block(miner_config:Option)->Option { + info!("Starting miner loop for Genesis Block"); + let mut gen = genesis::genesis(); + let diff = gen.header.difficulty.clone(); + + let sz = global::sizeshift() as u32; + let proof_size = global::proofsize(); + + let mut miner:Box = match miner_config { + Some(c) => { + if c.use_cuckoo_miner { + let mut p = plugin::PluginMiner::new(consensus::EASINESS, sz, proof_size); + p.init(c.clone()); + Box::new(p) + + } else { + Box::new(cuckoo::Miner::new(consensus::EASINESS, sz, proof_size)) + } + }, + None => Box::new(cuckoo::Miner::new(consensus::EASINESS, sz, proof_size)), + }; + pow_size(&mut *miner, &mut gen.header, diff, sz as u32).unwrap(); + Some(gen) +} + /// Runs a proof of work computation over the provided block using the provided Mining Worker, /// until the required difficulty target is reached. May take a while for a low target... -pub fn pow_size(miner:&mut T, bh: &mut BlockHeader, +pub fn pow_size(miner:&mut T, bh: &mut BlockHeader, diff: Difficulty, _: u32) -> Result<(), Error> { let start_nonce = bh.nonce; + // if we're in production mode, try the pre-mined solution first + if global::is_production_mode() { + let p = Proof::new(global::get_genesis_pow().to_vec()); + if p.clone().to_difficulty() >= diff { + bh.pow = p; + return Ok(()); + } + } + // try to find a cuckoo cycle on that header hash loop { // can be trivially optimized by avoiding re-serialization every time but this @@ -103,10 +164,10 @@ pub fn pow_size(miner:&mut T, bh: &mut BlockHeader, mod test { use super::*; use global; - use core::target::Difficulty; - use genesis; - use consensus::MINIMUM_DIFFICULTY; - use global::MiningParameterMode; + use core::core::target::Difficulty; + use core::genesis; + use core::consensus::MINIMUM_DIFFICULTY; + use core::global::MiningParameterMode; #[test] @@ -114,7 +175,7 @@ mod test { global::set_mining_mode(MiningParameterMode::AutomatedTesting); let mut b = genesis::genesis(); b.header.nonce = 310; - let mut internal_miner = cuckoo::Miner::new(EASINESS, global::sizeshift() as u32, global::proofsize()); + let mut internal_miner = cuckoo::Miner::new(consensus::EASINESS, global::sizeshift() as u32, global::proofsize()); pow_size(&mut internal_miner, &mut b.header, Difficulty::from_num(MINIMUM_DIFFICULTY), global::sizeshift() as u32).unwrap(); assert!(b.header.nonce != 310); assert!(b.header.pow.clone().to_difficulty() >= Difficulty::from_num(MINIMUM_DIFFICULTY)); diff --git a/grin/src/plugin.rs b/pow/src/plugin.rs similarity index 96% rename from grin/src/plugin.rs rename to pow/src/plugin.rs index bdd30f83e..ddd275369 100644 --- a/grin/src/plugin.rs +++ b/pow/src/plugin.rs @@ -19,13 +19,13 @@ use std::env; -use core::pow::cuckoo; -use core::pow::cuckoo::Error; -use core::pow::MiningWorker; +use cuckoo; +use cuckoo::Error; +use MiningWorker; use core::global; use core::core::Proof; -use types::{MinerConfig, ServerConfig}; +use types::MinerConfig; use std::sync::{Mutex}; @@ -64,12 +64,14 @@ impl Default for PluginMiner { impl PluginMiner { /// Init the plugin miner - pub fn init(&mut self, miner_config: MinerConfig, _server_config: ServerConfig){ + pub fn init(&mut self, miner_config: MinerConfig){ //Get directory of executable let mut exe_path=env::current_exe().unwrap(); exe_path.pop(); let exe_path=exe_path.to_str().unwrap(); + //println!("Plugin dir: {}", miner_config.clone().cuckoo_miner_plugin_dir.unwrap()); + let plugin_install_path = match miner_config.cuckoo_miner_plugin_dir { Some(s) => s, None => String::from(format!("{}/deps", exe_path)) @@ -86,7 +88,7 @@ impl PluginMiner { //when packaging is more//thought out let mut loaded_config_ref = LOADED_CONFIG.lock().unwrap(); - + //Load from here instead if let Some(ref c) = *loaded_config_ref { debug!("Not re-loading plugin or directory."); diff --git a/core/src/pow/siphash.rs b/pow/src/siphash.rs similarity index 100% rename from core/src/pow/siphash.rs rename to pow/src/siphash.rs diff --git a/pow/src/types.rs b/pow/src/types.rs new file mode 100644 index 000000000..6d61a0001 --- /dev/null +++ b/pow/src/types.rs @@ -0,0 +1,74 @@ +// Copyright 2017 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. + +//! Mining configuration type + +use std::collections::HashMap; + +/// Mining configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MinerConfig { + /// Whether to start the miner with the server + pub enable_mining: bool, + + /// Whether to use the cuckoo-miner crate and plugin for mining + pub use_cuckoo_miner: bool, + + /// Whether to use the async version of mining + pub cuckoo_miner_async_mode: Option, + + /// The location in which cuckoo miner plugins are stored + pub cuckoo_miner_plugin_dir: Option, + + /// The type of plugin to use (ends up filtering the filename) + pub cuckoo_miner_plugin_type: Option, + + /// Cuckoo-miner parameters... these vary according + /// to the plugin being loaded + pub cuckoo_miner_parameter_list: Option>, + + /// How long to wait before stopping the miner, recollecting transactions + /// and starting again + pub attempt_time_per_block: u32, + + /// Base address to the HTTP wallet receiver + pub wallet_receiver_url: String, + + /// Attributes the reward to a random private key instead of contacting the + /// wallet receiver. Mostly used for tests. + pub burn_reward: bool, + + /// a testing attribute for the time being that artifically slows down the + /// mining loop by adding a sleep to the thread + pub slow_down_in_millis: Option, + +} + +impl Default for MinerConfig { + fn default() -> MinerConfig { + MinerConfig { + enable_mining: false, + use_cuckoo_miner: false, + cuckoo_miner_async_mode: None, + cuckoo_miner_plugin_dir: None, + cuckoo_miner_plugin_type: None, + cuckoo_miner_parameter_list: None, + wallet_receiver_url: "http://localhost:13416".to_string(), + burn_reward: false, + slow_down_in_millis: Some(0), + attempt_time_per_block: 2, + } + } +} +