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.
|
|
|
|
|
|
|
|
mod siphash;
|
2016-11-27 23:31:15 +03:00
|
|
|
pub mod cuckoo;
|
2016-10-21 03:06:12 +03:00
|
|
|
|
|
|
|
use time;
|
|
|
|
|
2016-11-16 01:37:49 +03:00
|
|
|
use consensus::EASINESS;
|
2016-11-10 02:51:24 +03:00
|
|
|
use core::{Block, Proof};
|
2016-10-23 01:08:53 +03:00
|
|
|
use core::hash::{Hash, Hashed};
|
2016-11-16 01:37:49 +03:00
|
|
|
use core::target::Target;
|
2016-10-21 03:06:12 +03:00
|
|
|
use pow::cuckoo::{Cuckoo, Miner, Error};
|
|
|
|
|
|
|
|
use ser;
|
2016-11-01 04:44:11 +03:00
|
|
|
use ser::{Writeable, Writer};
|
2016-10-21 03:06:12 +03:00
|
|
|
|
|
|
|
/// Subset of a block header that goes into hashing for proof of work.
|
|
|
|
/// Basically the whole thing minus the PoW solution itself and the total
|
|
|
|
/// difficulty (yet unknown). We also add the count of every variable length
|
|
|
|
/// elements in a header to make lying on those much harder.
|
|
|
|
#[derive(Debug)]
|
2016-11-27 23:31:15 +03:00
|
|
|
pub struct PowHeader {
|
2016-10-21 03:06:12 +03:00
|
|
|
pub nonce: u64,
|
|
|
|
pub height: u64,
|
|
|
|
pub previous: Hash,
|
|
|
|
pub timestamp: time::Tm,
|
|
|
|
pub utxo_merkle: Hash,
|
|
|
|
pub tx_merkle: Hash,
|
|
|
|
pub n_in: u64,
|
|
|
|
pub n_out: u64,
|
|
|
|
pub n_proofs: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The binary definition of a PoW header is material for consensus as that's
|
|
|
|
/// the data that gets hashed for PoW calculation. The nonce is written first
|
|
|
|
/// to make incrementing from the serialized form trivial.
|
|
|
|
impl Writeable for PowHeader {
|
2016-11-01 01:17:53 +03:00
|
|
|
fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> {
|
|
|
|
try!(writer.write_u64(self.nonce));
|
|
|
|
try!(writer.write_u64(self.height));
|
|
|
|
try!(writer.write_fixed_bytes(&self.previous));
|
|
|
|
try!(writer.write_i64(self.timestamp.to_timespec().sec));
|
|
|
|
try!(writer.write_fixed_bytes(&self.utxo_merkle));
|
|
|
|
try!(writer.write_fixed_bytes(&self.tx_merkle));
|
|
|
|
try!(writer.write_u64(self.n_in));
|
|
|
|
try!(writer.write_u64(self.n_out));
|
2016-10-21 03:06:12 +03:00
|
|
|
writer.write_u64(self.n_proofs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PowHeader {
|
2016-11-27 23:31:15 +03:00
|
|
|
pub fn from_block(b: &Block) -> PowHeader {
|
2016-10-21 03:06:12 +03:00
|
|
|
let ref h = b.header;
|
|
|
|
PowHeader {
|
|
|
|
nonce: h.nonce,
|
|
|
|
height: h.height,
|
|
|
|
previous: h.previous,
|
|
|
|
timestamp: h.timestamp,
|
|
|
|
utxo_merkle: h.utxo_merkle,
|
|
|
|
tx_merkle: h.tx_merkle,
|
|
|
|
n_in: b.inputs.len() as u64,
|
|
|
|
n_out: b.outputs.len() as u64,
|
|
|
|
n_proofs: b.proofs.len() as u64,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Validates the proof of work of a given header.
|
2016-11-17 01:08:46 +03:00
|
|
|
pub fn verify(b: &Block) -> bool {
|
|
|
|
verify_size(b, b.header.cuckoo_len as u32)
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Same as default verify function but uses the much easier Cuckoo20 (mostly
|
|
|
|
/// for tests).
|
2016-11-17 01:08:46 +03:00
|
|
|
pub fn verify20(b: &Block) -> bool {
|
|
|
|
verify_size(b, 20)
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
|
2016-11-17 01:08:46 +03:00
|
|
|
pub fn verify_size(b: &Block, sizeshift: u32) -> bool {
|
2016-10-21 03:06:12 +03:00
|
|
|
let hash = PowHeader::from_block(b).hash();
|
|
|
|
// make sure the hash is smaller than our target before going into more
|
|
|
|
// expensive validation
|
2016-11-17 01:08:46 +03:00
|
|
|
if b.header.target < b.header.pow.to_target() {
|
2016-10-21 03:06:12 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Cuckoo::new(hash.to_slice(), sizeshift).verify(b.header.pow, EASINESS as u64)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Runs a naive single-threaded proof of work computation over the provided
|
|
|
|
/// block, until the required difficulty target is reached. May take a
|
|
|
|
/// while for a low target...
|
2016-11-16 01:37:49 +03:00
|
|
|
pub fn pow(b: &Block, target: Target) -> Result<(Proof, u64), Error> {
|
|
|
|
pow_size(b, target, b.header.cuckoo_len as u32)
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Same as default pow function but uses the much easier Cuckoo20 (mostly for
|
|
|
|
/// tests).
|
2016-11-16 01:37:49 +03:00
|
|
|
pub fn pow20(b: &Block, target: Target) -> Result<(Proof, u64), Error> {
|
2016-10-21 03:06:12 +03:00
|
|
|
pow_size(b, target, 20)
|
|
|
|
}
|
|
|
|
|
2016-11-30 05:46:28 +03:00
|
|
|
pub fn pow_size(b: &Block, target: Target, sizeshift: u32) -> Result<(Proof, u64), Error> {
|
2016-10-21 03:06:12 +03:00
|
|
|
let mut pow_header = PowHeader::from_block(b);
|
|
|
|
let start_nonce = pow_header.nonce;
|
|
|
|
|
|
|
|
// 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
|
|
|
|
let pow_hash = pow_header.hash();
|
|
|
|
|
|
|
|
// if we found a cycle (not guaranteed) and the proof is lower that the target,
|
|
|
|
// we're all good
|
|
|
|
if let Ok(proof) = Miner::new(pow_hash.to_slice(), EASINESS, sizeshift).mine() {
|
2016-11-16 01:37:49 +03:00
|
|
|
if proof.to_target() <= target {
|
2016-10-21 03:06:12 +03:00
|
|
|
return Ok((proof, pow_header.nonce));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise increment the nonce
|
|
|
|
pow_header.nonce += 1;
|
|
|
|
|
|
|
|
// and if we're back where we started, update the time (changes the hash as
|
|
|
|
// well)
|
|
|
|
if pow_header.nonce == start_nonce {
|
|
|
|
pow_header.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2016-11-10 02:51:24 +03:00
|
|
|
use consensus::MAX_TARGET;
|
2016-11-01 04:44:11 +03:00
|
|
|
use core::Proof;
|
2016-10-21 03:06:12 +03:00
|
|
|
use genesis;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn genesis_pow() {
|
|
|
|
let mut b = genesis::genesis();
|
2016-11-16 01:37:49 +03:00
|
|
|
let (proof, nonce) = pow20(&b, MAX_TARGET).unwrap();
|
2016-10-21 03:06:12 +03:00
|
|
|
assert!(nonce > 0);
|
2016-11-16 01:37:49 +03:00
|
|
|
assert!(proof.to_target() < MAX_TARGET);
|
2016-10-21 03:06:12 +03:00
|
|
|
b.header.pow = proof;
|
|
|
|
b.header.nonce = nonce;
|
2016-11-17 01:08:46 +03:00
|
|
|
assert!(verify20(&b));
|
2016-10-21 03:06:12 +03:00
|
|
|
}
|
|
|
|
}
|