Initial import.

This commit is contained in:
Ignotus Peverell
2016-10-20 20:06:12 -04:00
commit f73a308bf3
144 changed files with 29870 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
//! The block chain itself, validates and accepts new blocks, handles reorgs.
#![deny(non_upper_case_globals)]
#![deny(non_camel_case_types)]
#![deny(non_snake_case)]
#![deny(unused_mut)]
#![warn(missing_docs)]
#[macro_use]
extern crate bitflags;
extern crate byteorder;
#[macro_use(try_m)]
extern crate grin_core as core;
extern crate grin_store;
extern crate secp256k1zkp as secp;
pub mod pipe;
pub mod store;
pub mod types;
// Re-export the base interface
pub use types::ChainStore;
pub use chain::Options;
pub use chain::process_block;
+118
View File
@@ -0,0 +1,118 @@
//! Implementation of the chain block acceptance (or refusal) pipeline.
use secp;
use core::core::{Hash, BlockHeader, Block, Proof};
use core::pow;
use types;
use types::{Tip, ChainStore};
use store;
bitflags! {
/// Options for block validation
pub flags Options: u32 {
/// Runs with the easier version of the Proof of Work, mostly to make testing easier.
const EASY_POW = 0b00000001,
}
}
/// Contextual information required to process a new block and either reject or
/// accept it.
pub struct BlockContext<'a> {
opts: Options,
store: &'a ChainStore,
head: Tip,
tip: Option<Tip>,
}
#[derive(Debug)]
pub enum Error {
/// The block doesn't fit anywhere in our chain
Unfit(String),
/// The proof of work is invalid
InvalidPow,
/// The block doesn't sum correctly or a tx signature is invalid
InvalidBlockProof(secp::Error),
/// Internal issue when trying to save the block
StoreErr(types::Error),
}
pub fn process_block(b: &Block, store: &ChainStore, opts: Options) -> Option<Error> {
// TODO should just take a promise for a block with a full header so we don't
// spend resources reading the full block when its header is invalid
let head = match store.head() {
Ok(head) => head,
Err(err) => return Some(Error::StoreErr(err)),
};
let mut ctx = BlockContext {
opts: opts,
store: store,
head: head,
tip: None,
};
try_m!(validate_header(&b, &mut ctx));
try_m!(set_tip(&b.header, &mut ctx));
try_m!(validate_block(b, &mut ctx));
try_m!(add_block(b, &mut ctx));
try_m!(update_tips(&mut ctx));
None
}
// block processing pipeline
// 1. is the header valid (time, PoW, etc.)
// 2. is it the next head, a new fork, or extension of a fork (not a too old
// fork tho)
// 3. ok fine, is all of it valid (txs, merkle, utxo merkle, etc.)
// 4. add the sucker to the head/fork
// 5. did we increase a fork difficulty over the head?
// 6. ok fine, swap them up (can be tricky, think addresses invalidation)
/// 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)
fn validate_header(b: &Block, ctx: &mut BlockContext) -> Option<Error> {
let header = &b.header;
println!("{} {}", header.height, ctx.head.height);
if header.height > ctx.head.height + 1 {
// TODO actually handle orphans and add them to a size-limited set
return Some(Error::Unfit("orphan".to_string()));
}
// TODO check time wrt to chain time, refuse older than 100 blocks or too far
// in future
// TODO maintain current difficulty
let diff_target = Proof(pow::MAX_TARGET);
if ctx.opts.intersects(EASY_POW) {
if !pow::verify20(b, diff_target) {
return Some(Error::InvalidPow);
}
} else if !pow::verify(b, diff_target) {
return Some(Error::InvalidPow);
}
None
}
fn set_tip(h: &BlockHeader, ctx: &mut BlockContext) -> Option<Error> {
ctx.tip = Some(ctx.head.clone());
None
}
fn validate_block(b: &Block, ctx: &mut BlockContext) -> Option<Error> {
// TODO check tx merkle tree
let curve = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
try_m!(b.verify(&curve).err().map(&Error::InvalidBlockProof));
None
}
fn add_block(b: &Block, ctx: &mut BlockContext) -> Option<Error> {
ctx.tip = ctx.tip.as_ref().map(|t| t.append(b.hash()));
ctx.store.save_block(b).map(&Error::StoreErr)
}
fn update_tips(ctx: &mut BlockContext) -> Option<Error> {
ctx.store.save_head(ctx.tip.as_ref().unwrap()).map(&Error::StoreErr)
}
+69
View File
@@ -0,0 +1,69 @@
//! Implements storage primitives required by the chain
use byteorder::{WriteBytesExt, BigEndian};
use types::*;
use core::core::Block;
use grin_store;
const STORE_PATH: &'static str = ".grin/chain";
const SEP: u8 = ':' as u8;
const BLOCK_PREFIX: u8 = 'B' as u8;
const TIP_PREFIX: u8 = 'T' as u8;
const HEAD_PREFIX: u8 = 'H' as u8;
/// An implementation of the ChainStore trait backed by a simple key-value
/// store.
pub struct ChainKVStore {
db: grin_store::Store,
}
impl ChainKVStore {
pub fn new() -> Result<ChainKVStore, Error> {
let db = try!(grin_store::Store::open(STORE_PATH).map_err(to_store_err));
Ok(ChainKVStore { db: db })
}
}
impl ChainStore for ChainKVStore {
fn head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]))
}
fn save_block(&self, b: &Block) -> Option<Error> {
self.db.put_ser(&to_key(BLOCK_PREFIX, &mut b.hash().to_vec())[..], b).map(&to_store_err)
}
fn save_head(&self, t: &Tip) -> Option<Error> {
try_m!(self.save_tip(t));
self.db.put_ser(&vec![HEAD_PREFIX], t).map(&to_store_err)
}
fn save_tip(&self, t: &Tip) -> Option<Error> {
let last_branch = t.lineage.last_branch();
let mut k = vec![TIP_PREFIX, SEP];
k.write_u32::<BigEndian>(last_branch);
self.db.put_ser(&mut k, t).map(&to_store_err)
}
}
fn to_key(prefix: u8, val: &mut Vec<u8>) -> &mut Vec<u8> {
val.insert(0, SEP);
val.insert(0, prefix);
val
}
fn to_store_err(e: grin_store::Error) -> Error {
Error::StorageErr(format!("{:?}", e))
}
/// unwraps the inner option by converting the none case to a not found error
fn option_to_not_found<T>(res: Result<Option<T>, grin_store::Error>) -> Result<T, Error> {
match res {
Ok(None) => Err(Error::NotFoundErr),
Ok(Some(o)) => Ok(o),
Err(e) => Err(to_store_err(e)),
}
}
+133
View File
@@ -0,0 +1,133 @@
//! Base types that the block chain pipeline requires.
use core::core::{Hash, Block};
use core::ser;
/// The lineage of a fork, defined as a series of numbers. Each new branch gets
/// a new number that gets added to a fork's ancestry to form a new fork.
/// Example:
/// head [1] -> fork1 [1, 2]
/// fork2 [1, 3]
#[derive(Debug, Clone)]
pub struct Lineage(Vec<u32>);
impl Lineage {
/// New lineage initialized just with branch 0
pub fn new() -> Lineage {
Lineage(vec![0])
}
/// The last branch that was added to the lineage. Also the only branch
/// that's
/// unique to this lineage.
pub fn last_branch(&self) -> u32 {
*self.0.last().unwrap()
}
}
/// Serialization for lineage, necessary to serialize fork tips.
impl ser::Writeable for Lineage {
fn write(&self, writer: &mut ser::Writer) -> Option<ser::Error> {
try_m!(writer.write_u32(self.0.len() as u32));
for num in &self.0 {
try_m!(writer.write_u32(*num));
}
None
}
}
/// Deserialization for lineage, necessary to deserialize fork tips.
impl ser::Readable<Lineage> for Lineage {
fn read(reader: &mut ser::Reader) -> Result<Lineage, ser::Error> {
let len = try!(reader.read_u32());
let mut branches = Vec::with_capacity(len as usize);
for _ in 0..len {
branches.push(try!(reader.read_u32()));
}
Ok(Lineage(branches))
}
}
/// The tip of a fork. A handle to the fork ancestry from its leaf in the
/// blockchain tree. References both the lineage of the fork as well as its max
/// height and its latest and previous blocks for convenience.
#[derive(Debug, Clone)]
pub struct Tip {
/// Height of the tip (max height of the fork)
pub height: u64,
/// Last block pushed to the fork
pub last_block_h: Hash,
/// Block previous to last
pub prev_block_h: Hash,
/// Lineage in branch numbers of the fork
pub lineage: Lineage,
}
impl Tip {
/// Creates a new tip at height zero and the provided genesis hash.
pub fn new(gbh: Hash) -> Tip {
Tip {
height: 0,
last_block_h: gbh,
prev_block_h: gbh,
lineage: Lineage::new(),
}
}
/// Append a new block hash to this tip, returning a new updated tip.
pub fn append(&self, bh: Hash) -> Tip {
Tip {
height: self.height + 1,
last_block_h: bh,
prev_block_h: self.last_block_h,
lineage: self.lineage.clone(),
}
}
}
/// Serialization of a tip, required to save to datastore.
impl ser::Writeable for Tip {
fn write(&self, writer: &mut ser::Writer) -> Option<ser::Error> {
try_m!(writer.write_u64(self.height));
try_m!(writer.write_fixed_bytes(&self.last_block_h));
try_m!(writer.write_fixed_bytes(&self.prev_block_h));
self.lineage.write(writer)
}
}
impl ser::Readable<Tip> for Tip {
fn read(reader: &mut ser::Reader) -> Result<Tip, ser::Error> {
let height = try!(reader.read_u64());
let last = try!(reader.read_fixed_bytes(32));
let prev = try!(reader.read_fixed_bytes(32));
let line = try!(Lineage::read(reader));
Ok(Tip {
height: height,
last_block_h: Hash::from_vec(last),
prev_block_h: Hash::from_vec(prev),
lineage: line,
})
}
}
#[derive(Debug)]
pub enum Error {
/// Couldn't find what we were looking for
NotFoundErr,
/// Error generated by the underlying storage layer
StorageErr(String),
}
/// 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<Tip, Error>;
/// Save the provided block in store
fn save_block(&self, b: &Block) -> Option<Error>;
/// Save the provided tip as the current head of our chain
fn save_head(&self, t: &Tip) -> Option<Error>;
/// Save the provided tip without setting it as head
fn save_tip(&self, t: &Tip) -> Option<Error>;
}