Skip to content

Commit

Permalink
Initial import.
Browse files Browse the repository at this point in the history
  • Loading branch information
ignopeverell committed Oct 21, 2016
0 parents commit f73a308
Show file tree
Hide file tree
Showing 144 changed files with 29,870 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.swp
target
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Grin

Grin is an in-progress implementation of the MimbleWimble protocol. Many characteristics are still undefined but the following constitutes a first set of choices:

* Clean and minimal implementation, aiming to stay as such.
* Follows the MimbleWimble protocol, which provides great anonymity and scaling characteristics.
* Cuckoo Cycle proof of work (at least to start with).
* Relatively fast block time (a minute or less, possibly decreasing as networks improve).
* Fixed block reward, both over time and in blocks (fees are not additive).
* Transaction fees are based on the number of UTXO created/destroyed and total transaction size.
* Smooth curve for difficulty adjustments.

## Status

Grin is still an infant, much is left to be done and contributions are welcome (see below). Our [status file](TODO.md) may help.

## Contributing

Find an area you can help with and do it. Open source is about collaboration and open participation. Try to make your code look like what already exists and submit a pull request. If you're looking for additional ideas, the code includes TODO comments for minor to major improvements. Grep is your friend.

Additional tests are rewarded with an immense amount of positive karma. So is documentation.

Find us on Gitter: https://gitter.im/grin_community

## Philosophy

Grin likes itself small and easy on the eyes. It wants to be inclusive and welcoming for all walks of life, without judgement. Grin is terribly ambitious, but not at the detriment of others, rather to further us all. It may have strong opinions to stay in line with its objectives, which doesn't mean disrepect of others' ideas.

We believe in pull requests, data and scientific research. We do not believe in unfounded beliefs.

## Credits

Tom Elvis Jedusor for the first formulation of MimbleWimble.
Andrew Poelstra for his related work and improvements.
John Tromp for the Cuckoo Cycles proof of work.
J.K. Rowling for making it despite extraordinary adversity.

## License

Apache License v2.0.
16 changes: 16 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Rolling up our sleeves

Grin is still an infant, much is left to be done and contributions are welcome. Here's a (non exhaustive) list of what's left to implement:

* Transaction signatures aggregation.
* Proper API to author transactions.
* Protocol layer (handshake, send blocks, ask for blocks, send txs, ask for them, sync, etc.).
* Maintenance of the UTXO set and its Merkle tree.
* Transaction pool and related validation.
* Chain logic and related validation.
* Efficient miner (as a distinct project).
* User-friendly wallet (as a distinct project).
* Figure out if the rangeproofs can be eliminated under some cicrumstances while keeping security guarantes.
* Website, logo design and all the cool stuff.

Don't worry, we'll get there, with your help.
15 changes: 15 additions & 0 deletions chain/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "grin_chain"
version = "0.1.0"
authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]

[dependencies]
bitflags = "^0.7.0"
byteorder = "^0.5"

grin_core = { path = "../core" }
grin_store = { path = "../store" }
secp256k1zkp = { path = "../secp256k1zkp" }

[dev-dependencies]
rand = "^0.3"
3 changes: 3 additions & 0 deletions chain/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hard_tabs = true
wrap_comments = true
write_mode = "Overwrite"
26 changes: 26 additions & 0 deletions chain/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 118 additions & 0 deletions chain/src/pipe.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 69 additions & 0 deletions chain/src/store.rs
Original file line number Diff line number Diff line change
@@ -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)),
}
}
Loading

0 comments on commit f73a308

Please sign in to comment.