diff --git a/chain/Cargo.toml b/chain/Cargo.toml index 6be06655..73f8df93 100644 --- a/chain/Cargo.toml +++ b/chain/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Ignotus Peverell "] [dependencies] bitflags = "^0.7.0" byteorder = "^0.5" +time = "^0.1" grin_core = { path = "../core" } grin_store = { path = "../store" } diff --git a/chain/src/lib.rs b/chain/src/lib.rs index c4cd00b2..28f5b960 100644 --- a/chain/src/lib.rs +++ b/chain/src/lib.rs @@ -23,6 +23,7 @@ #[macro_use] extern crate bitflags; extern crate byteorder; +extern crate time; extern crate grin_core as core; extern crate grin_store; @@ -34,6 +35,5 @@ pub mod types; // Re-export the base interface -pub use types::ChainStore; -pub use pipe::Options; -pub use pipe::process_block; +pub use types::{ChainStore, State, Tip}; +pub use pipe::{NONE, process_block}; diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index 89900dfd..30677cfb 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -15,6 +15,7 @@ //! Implementation of the chain block acceptance (or refusal) pipeline. use secp; +use time; use core::consensus; use core::core::hash::Hash; @@ -27,8 +28,9 @@ use store; bitflags! { /// Options for block validation pub flags Options: u32 { + const NONE = 0b00000001, /// Runs with the easier version of the Proof of Work, mostly to make testing easier. - const EASY_POW = 0b00000001, + const EASY_POW = 0b00000010, } } @@ -89,20 +91,25 @@ pub fn process_block(b: &Block, store: &ChainStore, opts: Options) -> Result<(), /// First level of black validation that only needs to act on the block header /// to make it as cheap as possible. The different validations are also /// arranged by order of cost to have as little DoS surface as possible. -/// TODO actually require only the block header (with length information) +/// TODO require only the block header (with length information) fn validate_header(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> { let header = &b.header; if header.height > ctx.head.height + 1 { // TODO actually handle orphans and add them to a size-limited set return Err(Error::Unfit("orphan".to_string())); } - // TODO check time wrt to chain time, refuse too far in future let prev = try!(ctx.store.get_block_header(&header.previous).map_err(&Error::StoreErr)); if header.timestamp <= prev.timestamp { + // prevent time warp attacks and some timestamp manipulations by forcing strict time progression return Err(Error::InvalidBlockTime); } + if header.timestamp > time::now() + time::Duration::seconds(12*(consensus::BLOCK_TIME_SEC as i64)) { + // refuse blocks too far in future, constant of 12 comes from bitcoin (2h worth of blocks) + // TODO add warning in p2p code if local time is too different from peers + return Err(Error::InvalidBlockTime); + } let (diff_target, cuckoo_sz) = consensus::next_target(header.timestamp.to_timespec().sec, prev.timestamp.to_timespec().sec, diff --git a/chain/src/store.rs b/chain/src/store.rs index 9bc1a2da..7866f863 100644 --- a/chain/src/store.rs +++ b/chain/src/store.rs @@ -48,6 +48,11 @@ impl ChainStore for ChainKVStore { option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX])) } + fn head_header(&self) -> Result { + let head: Tip = try!(option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]))); + self.get_block_header(&head.last_block_h) + } + fn save_block(&self, b: &Block) -> Result<(), Error> { try!(self.db .put_ser(&to_key(BLOCK_PREFIX, &mut b.hash().to_vec())[..], b) diff --git a/chain/src/types.rs b/chain/src/types.rs index 9acbeff2..62048088 100644 --- a/chain/src/types.rs +++ b/chain/src/types.rs @@ -131,12 +131,21 @@ pub enum Error { StorageErr(String), } +#[derive(Debug, Clone)] +pub struct State { + pub head: Tip, + pub forks: Vec, +} + /// Trait the chain pipeline requires an implementor for in order to process /// blocks. pub trait ChainStore { /// Get the tip that's also the head of the chain fn head(&self) -> Result; + /// Block header for the chain head + fn head_header(&self) -> Result; + /// Gets a block header by hash fn get_block_header(&self, h: &Hash) -> Result; @@ -149,3 +158,4 @@ pub trait ChainStore { /// Save the provided tip without setting it as head fn save_tip(&self, t: &Tip) -> Result<(), Error>; } + diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index 1673c36e..9aab4f8b 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -76,14 +76,14 @@ impl Cuckoo { /// Generates a node in the cuckoo graph generated from our seed. A node is /// simply materialized as a u64 from a nonce and an offset (generally 0 or /// 1). - pub fn new_node(&self, nonce: u64, uorv: u64) -> u64 { + fn new_node(&self, nonce: u64, uorv: u64) -> u64 { return ((siphash24(self.v, 2 * nonce + uorv) & self.mask) << 1) | uorv; } /// Creates a new edge in the cuckoo graph generated by our seed from a /// nonce. Generates two node coordinates from the nonce and links them /// together. - pub fn new_edge(&self, nonce: u64) -> Edge { + fn new_edge(&self, nonce: u64) -> Edge { Edge { u: self.new_node(nonce, 0), v: self.new_node(nonce, 1), diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 08255760..31559095 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -23,7 +23,7 @@ //! reference. It's not optimized for speed. mod siphash; -mod cuckoo; +pub mod cuckoo; use time; @@ -41,7 +41,7 @@ use ser::{Writeable, Writer}; /// 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)] -struct PowHeader { +pub struct PowHeader { pub nonce: u64, pub height: u64, pub previous: Hash, @@ -71,7 +71,7 @@ impl Writeable for PowHeader { } impl PowHeader { - fn from_block(b: &Block) -> PowHeader { + pub fn from_block(b: &Block) -> PowHeader { let ref h = b.header; PowHeader { nonce: h.nonce, diff --git a/grin/Cargo.toml b/grin/Cargo.toml index f8018ecb..9d593148 100644 --- a/grin/Cargo.toml +++ b/grin/Cargo.toml @@ -4,5 +4,12 @@ version = "0.1.0" authors = ["Ignotus Peverell "] [dependencies] +grin_chain = { path = "../chain" } grin_core = { path = "../core" } grin_store = { path = "../store" } +secp256k1zkp = { path = "../secp256k1zkp" } + +env_logger="^0.3.5" +log = "^0.3" +time = "^0.1" +rand = "^0.3" diff --git a/grin/src/main.rs b/grin/src/main.rs index 8e6a261a..bf261317 100644 --- a/grin/src/main.rs +++ b/grin/src/main.rs @@ -21,13 +21,25 @@ #![deny(unused_mut)] #![warn(missing_docs)] +#[macro_use] +extern crate log; +extern crate env_logger; +extern crate rand; +extern crate time; + +extern crate grin_chain as chain; extern crate grin_core as core; extern crate grin_store as store; +extern crate secp256k1zkp as secp; + +mod miner; use store::Store; use core::genesis::genesis; fn main() { + env_logger::init().unwrap(); + let gen = genesis(); let db = Store::open("./store").unwrap(); let mut key = "block:".to_string().into_bytes(); diff --git a/grin/src/miner.rs b/grin/src/miner.rs new file mode 100644 index 00000000..76f67e4a --- /dev/null +++ b/grin/src/miner.rs @@ -0,0 +1,111 @@ +// 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. + +//! Mining service, gathers transactions from the pool, assemble them in a block and mine the block to produce a valid header with its proof-of-work. + +use rand::{self, Rng}; +use std::sync::{Arc, Mutex}; +use std::ops::Deref; +use time; + +use core::consensus; +use core::core; +use core::core::hash::{Hash, Hashed}; +use core::pow; +use core::pow::cuckoo; +use chain; +use secp; + +pub struct Miner { + chain_state: Arc>, + chain_store: Arc>, +} + +impl Miner { + /// Creates a new Miner. Needs references to the chain state and its storage. + pub fn new(chain_state: Arc>, chain_store: Arc>) -> Miner { + Miner{chain_state: chain_state, chain_store: chain_store} + } + + /// 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) { + loop { + // get the latest chain state and build a block on top of it + let head: core::BlockHeader; + let mut latest_hash: Hash; + { + head = self.chain_store.lock().unwrap().head_header().unwrap(); + latest_hash = self.chain_state.lock().unwrap().head.last_block_h; + } + let b = self.build_block(&head); + let mut pow_header = pow::PowHeader::from_block(&b); + + // 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 mut sol = None; + while head.hash() == latest_hash && time::get_time().sec < deadline { + let pow_hash = pow_header.hash(); + let mut miner = cuckoo::Miner::new(pow_hash.to_slice(), consensus::EASINESS, b.header.cuckoo_len as u32); + if let Ok(proof) = miner.mine() { + if proof.to_target() <= b.header.target { + sol = Some(proof); + break; + } + } + pow_header.nonce += 1; + { + latest_hash = self.chain_state.lock().unwrap().head.last_block_h; + } + } + + // if we found a solution, push our block out + if let Some(proof) = sol { + if let Err(e) = chain::process_block(&b, self.chain_store.lock().unwrap().deref(), chain::NONE) { + error!("Error validating mined block: {:?}", e); + } + } + } + } + + /// Builds a new block with the chain head as previous and eligible transactions from the pool. + fn build_block(&self, head: &core::BlockHeader) -> core::Block { + let mut now_sec = time::get_time().sec; + let head_sec = head.timestamp.to_timespec().sec; + if now_sec == head_sec { + now_sec += 1; + } + let (target, cuckoo_len) = consensus::next_target(now_sec, head_sec, head.target, head.cuckoo_len); + + let mut rng = rand::OsRng::new().unwrap(); + let secp_inst = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); + // TODO get a new key from the user's wallet or something + let skey = secp::key::SecretKey::new(&secp_inst, &mut rng); + + // TODO populate inputs and outputs from pool transactions + core::Block { + header: core::BlockHeader { + height: head.height+1, + previous: head.hash(), + timestamp: time::at(time::Timespec::new(now_sec, 0)), + cuckoo_len: cuckoo_len, + target: target, + nonce: rng.gen(), + ..Default::default() + }, + inputs: vec![], + outputs: vec![], + proofs: vec![], + } + } +}