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
This commit is contained in:
Yeastplume 2017-08-22 19:23:54 +01:00 committed by Ignotus Peverell
parent 6772d9f516
commit 045f5bb4da
27 changed files with 417 additions and 215 deletions

View file

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

View file

@ -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"

View file

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

View file

@ -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<Mutex<Tip>>,
block_process_lock: Arc<Mutex<bool>>,
orphans: Arc<Mutex<VecDeque<(Options, Block)>>>,
//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<ChainAdapter>,
gen_block: Option<Block>,
pow_verifier: fn(&BlockHeader, u32) -> bool,
) -> Result<Chain, Error> {
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(),
}
}

View file

@ -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<ChainAdapter>,
/// The head
pub head: Tip,
/// The POW verification function
pub pow_verifier: fn(&BlockHeader, u32) -> bool,
/// The lock
pub lock: Arc<Mutex<bool>>,
}
@ -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);
}
}

View file

@ -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),
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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)]

View file

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

View file

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

View file

@ -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"

View file

@ -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"

View file

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

View file

@ -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<Proof> {
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<Proof> {
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<T: MiningWorker>(&self,
miner:&mut T,
b:&mut Block,
cuckoo_size: u32,
head:&BlockHeader,
latest_hash:&mut Hash)
-> Option<Proof> {
miner:&mut T,
b:&mut Block,
cuckoo_size: u32,
head:&BlockHeader,
attempt_time_per_block: u32,
latest_hash:&mut Hash)
-> Option<Proof> {
// 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)

View file

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

View file

@ -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<p2p::P2PConfig>,
/// Configuration for the mining daemon
pub mining_config: Option<MinerConfig>,
}
/// 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<bool>,
/// The location in which cuckoo miner plugins are stored
pub cuckoo_miner_plugin_dir: Option<String>,
/// The type of plugin to use (ends up filtering the filename)
pub cuckoo_miner_plugin_type: Option<String>,
/// Cuckoo-miner parameters... these vary according
/// to the plugin being loaded
pub cuckoo_miner_parameter_list: Option<HashMap<String, 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<u64>,
pub mining_config: Option<pow::types::MinerConfig>,
}
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,
}

View file

@ -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,

View file

@ -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,

24
pow/Cargo.toml Normal file
View file

@ -0,0 +1,24 @@
[package]
name = "grin_pow"
version = "0.1.0"
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
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" }

View file

@ -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,

View file

@ -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<T: MiningWorker>(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<types::MinerConfig>)->Option<core::core::Block> {
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<MiningWorker> = 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<T: MiningWorker>(miner:&mut T, bh: &mut BlockHeader,
pub fn pow_size<T: MiningWorker + ?Sized>(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<T: MiningWorker>(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));

View file

@ -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.");

74
pow/src/types.rs Normal file
View file

@ -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<bool>,
/// The location in which cuckoo miner plugins are stored
pub cuckoo_miner_plugin_dir: Option<String>,
/// The type of plugin to use (ends up filtering the filename)
pub cuckoo_miner_plugin_type: Option<String>,
/// Cuckoo-miner parameters... these vary according
/// to the plugin being loaded
pub cuckoo_miner_parameter_list: Option<HashMap<String, u32>>,
/// 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<u64>,
}
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,
}
}
}