2016-10-22 21:35:48 +03:00
|
|
|
// 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.
|
|
|
|
|
2016-10-21 03:06:12 +03:00
|
|
|
//! The proof of work needs to strike a balance between fast header
|
|
|
|
//! verification to avoid DoS attacks and difficulty for block verifiers to
|
|
|
|
//! build new blocks. In addition, mining new blocks should also be as
|
|
|
|
//! difficult on high end custom-made hardware (ASICs) as on commodity hardware
|
2016-10-22 01:02:20 +03:00
|
|
|
//! or smartphones. For this reason we use Cuckoo Cycle (see the cuckoo
|
2016-10-21 03:06:12 +03:00
|
|
|
//! module for more information).
|
|
|
|
//!
|
|
|
|
//! Note that this miner implementation is here mostly for tests and
|
|
|
|
//! reference. It's not optimized for speed.
|
|
|
|
|
2017-08-22 21:23:54 +03:00
|
|
|
#![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;
|
|
|
|
|
2016-10-21 03:06:12 +03:00
|
|
|
mod siphash;
|
2017-08-22 21:23:54 +03:00
|
|
|
pub mod plugin;
|
2016-11-27 23:31:15 +03:00
|
|
|
pub mod cuckoo;
|
2017-08-22 21:23:54 +03:00
|
|
|
pub mod types;
|
2016-10-21 03:06:12 +03:00
|
|
|
|
2017-08-22 21:23:54 +03:00
|
|
|
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};
|
2017-07-11 20:11:03 +03:00
|
|
|
|
|
|
|
/// Should be implemented by anything providing mining services
|
|
|
|
///
|
|
|
|
|
|
|
|
pub trait MiningWorker {
|
2017-07-20 17:22:40 +03:00
|
|
|
/// This only sets parameters and does initialisation work now
|
2017-09-29 21:44:25 +03:00
|
|
|
fn new(ease: u32, sizeshift: u32, proof_size: usize) -> Self
|
|
|
|
where
|
|
|
|
Self: Sized;
|
2017-08-10 03:54:10 +03:00
|
|
|
|
2017-07-20 17:22:40 +03:00
|
|
|
/// Actually perform a mining attempt on the given input and
|
|
|
|
/// return a proof if found
|
2017-07-11 20:11:03 +03:00
|
|
|
fn mine(&mut self, header: &[u8]) -> Result<Proof, Error>;
|
|
|
|
}
|
|
|
|
|
2017-04-10 09:17:23 +03:00
|
|
|
/// Validates the proof of work of a given header, and that the proof of work
|
|
|
|
/// satisfies the requirements of the header.
|
2017-02-08 00:48:11 +03:00
|
|
|
pub fn verify_size(bh: &BlockHeader, cuckoo_sz: u32) -> bool {
|
2016-12-27 02:39:31 +03:00
|
|
|
// make sure the pow hash shows a difficulty at least as large as the target
|
|
|
|
// difficulty
|
2017-08-09 19:40:23 +03:00
|
|
|
if bh.difficulty > bh.pow.clone().to_difficulty() {
|
2016-10-21 03:06:12 +03:00
|
|
|
return false;
|
|
|
|
}
|
2017-08-22 21:23:54 +03:00
|
|
|
Cuckoo::new(&bh.hash()[..], cuckoo_sz).verify(bh.pow.clone(), consensus::EASINESS as u64)
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
|
2017-06-19 18:59:56 +03:00
|
|
|
/// Uses the much easier Cuckoo20 (mostly for
|
2016-10-21 03:06:12 +03:00
|
|
|
/// tests).
|
2017-09-29 21:44:25 +03:00
|
|
|
pub fn pow20<T: MiningWorker>(
|
|
|
|
miner: &mut T,
|
|
|
|
bh: &mut BlockHeader,
|
|
|
|
diff: Difficulty,
|
|
|
|
) -> Result<(), Error> {
|
2017-07-18 23:57:09 +03:00
|
|
|
pow_size(miner, bh, diff, 20)
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
|
2017-09-29 21:44:25 +03:00
|
|
|
/// Mines a genesis block, using the config specified miner if specified.
|
|
|
|
/// Otherwise,
|
2017-08-22 21:23:54 +03:00
|
|
|
/// uses the internal miner
|
|
|
|
///
|
|
|
|
|
2017-09-29 21:44:25 +03:00
|
|
|
pub fn mine_genesis_block(miner_config: Option<types::MinerConfig>) -> Option<core::core::Block> {
|
2017-08-22 21:23:54 +03:00
|
|
|
info!("Starting miner loop for Genesis Block");
|
|
|
|
let mut gen = genesis::genesis();
|
|
|
|
let diff = gen.header.difficulty.clone();
|
2017-09-29 21:44:25 +03:00
|
|
|
|
2017-08-22 21:23:54 +03:00
|
|
|
let sz = global::sizeshift() as u32;
|
|
|
|
let proof_size = global::proofsize();
|
|
|
|
|
2017-09-29 21:44:25 +03:00
|
|
|
let mut miner: Box<MiningWorker> = match miner_config {
|
2017-08-22 21:23:54 +03:00
|
|
|
Some(c) => {
|
2017-09-29 21:44:25 +03:00
|
|
|
if c.use_cuckoo_miner {
|
2017-08-22 21:23:54 +03:00
|
|
|
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))
|
2017-09-29 21:44:25 +03:00
|
|
|
}
|
|
|
|
}
|
2017-08-22 21:23:54 +03:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2017-09-29 21:44:25 +03:00
|
|
|
/// 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 + ?Sized>(
|
|
|
|
miner: &mut T,
|
|
|
|
bh: &mut BlockHeader,
|
|
|
|
diff: Difficulty,
|
|
|
|
_: u32,
|
|
|
|
) -> Result<(), Error> {
|
2017-02-08 00:48:11 +03:00
|
|
|
let start_nonce = bh.nonce;
|
2016-10-21 03:06:12 +03:00
|
|
|
|
2017-08-22 21:23:54 +03:00
|
|
|
// 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(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 03:06:12 +03:00
|
|
|
// try to find a cuckoo cycle on that header hash
|
|
|
|
loop {
|
|
|
|
// can be trivially optimized by avoiding re-serialization every time but this
|
|
|
|
// is not meant as a fast miner implementation
|
2017-02-08 00:48:11 +03:00
|
|
|
let pow_hash = bh.hash();
|
2016-10-21 03:06:12 +03:00
|
|
|
|
2016-12-27 02:39:31 +03:00
|
|
|
// if we found a cycle (not guaranteed) and the proof hash is higher that the
|
|
|
|
// diff, we're all good
|
2017-07-18 23:57:09 +03:00
|
|
|
|
|
|
|
if let Ok(proof) = miner.mine(&pow_hash[..]) {
|
2017-08-09 19:40:23 +03:00
|
|
|
if proof.clone().to_difficulty() >= diff {
|
2017-02-08 00:48:11 +03:00
|
|
|
bh.pow = proof;
|
2017-01-09 23:07:38 +03:00
|
|
|
return Ok(());
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise increment the nonce
|
2017-02-08 00:48:11 +03:00
|
|
|
bh.nonce += 1;
|
2016-10-21 03:06:12 +03:00
|
|
|
|
|
|
|
// and if we're back where we started, update the time (changes the hash as
|
|
|
|
// well)
|
2017-02-08 00:48:11 +03:00
|
|
|
if bh.nonce == start_nonce {
|
|
|
|
bh.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 });
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2017-08-10 03:54:10 +03:00
|
|
|
use global;
|
2017-08-22 21:23:54 +03:00
|
|
|
use core::core::target::Difficulty;
|
|
|
|
use core::genesis;
|
2017-09-29 21:44:25 +03:00
|
|
|
use core::consensus::MINIMUM_DIFFICULTY;
|
2017-08-22 21:23:54 +03:00
|
|
|
use core::global::MiningParameterMode;
|
2017-08-10 03:54:10 +03:00
|
|
|
|
2016-10-21 03:06:12 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn genesis_pow() {
|
2017-09-29 21:44:25 +03:00
|
|
|
global::set_mining_mode(MiningParameterMode::AutomatedTesting);
|
2016-10-21 03:06:12 +03:00
|
|
|
let mut b = genesis::genesis();
|
2017-01-10 02:16:44 +03:00
|
|
|
b.header.nonce = 310;
|
2017-09-29 21:44:25 +03:00
|
|
|
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();
|
2017-01-09 23:07:38 +03:00
|
|
|
assert!(b.header.nonce != 310);
|
2017-08-09 19:40:23 +03:00
|
|
|
assert!(b.header.pow.clone().to_difficulty() >= Difficulty::from_num(MINIMUM_DIFFICULTY));
|
|
|
|
assert!(verify_size(&b.header, global::sizeshift() as u32));
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
}
|