mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Miner facility, using the current chain state to build on top of.
This commit is contained in:
parent
1e5ff0eeff
commit
9795d9e452
10 changed files with 164 additions and 11 deletions
|
@ -6,6 +6,7 @@ authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "^0.7.0"
|
bitflags = "^0.7.0"
|
||||||
byteorder = "^0.5"
|
byteorder = "^0.5"
|
||||||
|
time = "^0.1"
|
||||||
|
|
||||||
grin_core = { path = "../core" }
|
grin_core = { path = "../core" }
|
||||||
grin_store = { path = "../store" }
|
grin_store = { path = "../store" }
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
extern crate time;
|
||||||
|
|
||||||
extern crate grin_core as core;
|
extern crate grin_core as core;
|
||||||
extern crate grin_store;
|
extern crate grin_store;
|
||||||
|
@ -34,6 +35,5 @@ pub mod types;
|
||||||
|
|
||||||
// Re-export the base interface
|
// Re-export the base interface
|
||||||
|
|
||||||
pub use types::ChainStore;
|
pub use types::{ChainStore, State, Tip};
|
||||||
pub use pipe::Options;
|
pub use pipe::{NONE, process_block};
|
||||||
pub use pipe::process_block;
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
//! Implementation of the chain block acceptance (or refusal) pipeline.
|
//! Implementation of the chain block acceptance (or refusal) pipeline.
|
||||||
|
|
||||||
use secp;
|
use secp;
|
||||||
|
use time;
|
||||||
|
|
||||||
use core::consensus;
|
use core::consensus;
|
||||||
use core::core::hash::Hash;
|
use core::core::hash::Hash;
|
||||||
|
@ -27,8 +28,9 @@ use store;
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Options for block validation
|
/// Options for block validation
|
||||||
pub flags Options: u32 {
|
pub flags Options: u32 {
|
||||||
|
const NONE = 0b00000001,
|
||||||
/// Runs with the easier version of the Proof of Work, mostly to make testing easier.
|
/// Runs with the easier version of the Proof of Work, mostly to make testing easier.
|
||||||
const EASY_POW = 0b00000001,
|
const EASY_POW = 0b00000010,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,18 +91,23 @@ 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
|
/// 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
|
/// 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.
|
/// 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> {
|
fn validate_header(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
|
||||||
let header = &b.header;
|
let header = &b.header;
|
||||||
if header.height > ctx.head.height + 1 {
|
if header.height > ctx.head.height + 1 {
|
||||||
// TODO actually handle orphans and add them to a size-limited set
|
// TODO actually handle orphans and add them to a size-limited set
|
||||||
return Err(Error::Unfit("orphan".to_string()));
|
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));
|
let prev = try!(ctx.store.get_block_header(&header.previous).map_err(&Error::StoreErr));
|
||||||
|
|
||||||
if header.timestamp <= prev.timestamp {
|
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);
|
return Err(Error::InvalidBlockTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,11 @@ impl ChainStore for ChainKVStore {
|
||||||
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]))
|
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn head_header(&self) -> Result<BlockHeader, Error> {
|
||||||
|
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> {
|
fn save_block(&self, b: &Block) -> Result<(), Error> {
|
||||||
try!(self.db
|
try!(self.db
|
||||||
.put_ser(&to_key(BLOCK_PREFIX, &mut b.hash().to_vec())[..], b)
|
.put_ser(&to_key(BLOCK_PREFIX, &mut b.hash().to_vec())[..], b)
|
||||||
|
|
|
@ -131,12 +131,21 @@ pub enum Error {
|
||||||
StorageErr(String),
|
StorageErr(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct State {
|
||||||
|
pub head: Tip,
|
||||||
|
pub forks: Vec<Tip>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Trait the chain pipeline requires an implementor for in order to process
|
/// Trait the chain pipeline requires an implementor for in order to process
|
||||||
/// blocks.
|
/// blocks.
|
||||||
pub trait ChainStore {
|
pub trait ChainStore {
|
||||||
/// Get the tip that's also the head of the chain
|
/// Get the tip that's also the head of the chain
|
||||||
fn head(&self) -> Result<Tip, Error>;
|
fn head(&self) -> Result<Tip, Error>;
|
||||||
|
|
||||||
|
/// Block header for the chain head
|
||||||
|
fn head_header(&self) -> Result<BlockHeader, Error>;
|
||||||
|
|
||||||
/// Gets a block header by hash
|
/// Gets a block header by hash
|
||||||
fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, Error>;
|
fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, Error>;
|
||||||
|
|
||||||
|
@ -149,3 +158,4 @@ pub trait ChainStore {
|
||||||
/// Save the provided tip without setting it as head
|
/// Save the provided tip without setting it as head
|
||||||
fn save_tip(&self, t: &Tip) -> Result<(), Error>;
|
fn save_tip(&self, t: &Tip) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,14 +76,14 @@ impl Cuckoo {
|
||||||
/// Generates a node in the cuckoo graph generated from our seed. A node is
|
/// 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
|
/// simply materialized as a u64 from a nonce and an offset (generally 0 or
|
||||||
/// 1).
|
/// 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;
|
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
|
/// 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
|
/// nonce. Generates two node coordinates from the nonce and links them
|
||||||
/// together.
|
/// together.
|
||||||
pub fn new_edge(&self, nonce: u64) -> Edge {
|
fn new_edge(&self, nonce: u64) -> Edge {
|
||||||
Edge {
|
Edge {
|
||||||
u: self.new_node(nonce, 0),
|
u: self.new_node(nonce, 0),
|
||||||
v: self.new_node(nonce, 1),
|
v: self.new_node(nonce, 1),
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
//! reference. It's not optimized for speed.
|
//! reference. It's not optimized for speed.
|
||||||
|
|
||||||
mod siphash;
|
mod siphash;
|
||||||
mod cuckoo;
|
pub mod cuckoo;
|
||||||
|
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ use ser::{Writeable, Writer};
|
||||||
/// difficulty (yet unknown). We also add the count of every variable length
|
/// difficulty (yet unknown). We also add the count of every variable length
|
||||||
/// elements in a header to make lying on those much harder.
|
/// elements in a header to make lying on those much harder.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PowHeader {
|
pub struct PowHeader {
|
||||||
pub nonce: u64,
|
pub nonce: u64,
|
||||||
pub height: u64,
|
pub height: u64,
|
||||||
pub previous: Hash,
|
pub previous: Hash,
|
||||||
|
@ -71,7 +71,7 @@ impl Writeable for PowHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PowHeader {
|
impl PowHeader {
|
||||||
fn from_block(b: &Block) -> PowHeader {
|
pub fn from_block(b: &Block) -> PowHeader {
|
||||||
let ref h = b.header;
|
let ref h = b.header;
|
||||||
PowHeader {
|
PowHeader {
|
||||||
nonce: h.nonce,
|
nonce: h.nonce,
|
||||||
|
|
|
@ -4,5 +4,12 @@ version = "0.1.0"
|
||||||
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
grin_chain = { path = "../chain" }
|
||||||
grin_core = { path = "../core" }
|
grin_core = { path = "../core" }
|
||||||
grin_store = { path = "../store" }
|
grin_store = { path = "../store" }
|
||||||
|
secp256k1zkp = { path = "../secp256k1zkp" }
|
||||||
|
|
||||||
|
env_logger="^0.3.5"
|
||||||
|
log = "^0.3"
|
||||||
|
time = "^0.1"
|
||||||
|
rand = "^0.3"
|
||||||
|
|
|
@ -21,13 +21,25 @@
|
||||||
#![deny(unused_mut)]
|
#![deny(unused_mut)]
|
||||||
#![warn(missing_docs)]
|
#![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_core as core;
|
||||||
extern crate grin_store as store;
|
extern crate grin_store as store;
|
||||||
|
extern crate secp256k1zkp as secp;
|
||||||
|
|
||||||
|
mod miner;
|
||||||
|
|
||||||
use store::Store;
|
use store::Store;
|
||||||
use core::genesis::genesis;
|
use core::genesis::genesis;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
env_logger::init().unwrap();
|
||||||
|
|
||||||
let gen = genesis();
|
let gen = genesis();
|
||||||
let db = Store::open("./store").unwrap();
|
let db = Store::open("./store").unwrap();
|
||||||
let mut key = "block:".to_string().into_bytes();
|
let mut key = "block:".to_string().into_bytes();
|
||||||
|
|
111
grin/src/miner.rs
Normal file
111
grin/src/miner.rs
Normal file
|
@ -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<Mutex<chain::State>>,
|
||||||
|
chain_store: Arc<Mutex<chain::ChainStore>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Miner {
|
||||||
|
/// Creates a new Miner. Needs references to the chain state and its storage.
|
||||||
|
pub fn new(chain_state: Arc<Mutex<chain::State>>, chain_store: Arc<Mutex<chain::ChainStore>>) -> 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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue