// 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. //! Base types that the block chain pipeline requires. use secp; use secp::pedersen::Commitment; use grin_store as store; use core::core::{Block, BlockHeader, Output}; use core::core::hash::{Hash, Hashed}; use core::core::target::Difficulty; use core::ser; use grin_store; bitflags! { /// Options for block validation pub flags Options: u32 { const NONE = 0b00000001, /// Runs without checking the Proof of Work, mostly to make testing easier. const SKIP_POW = 0b00000010, /// Runs PoW verification with a lower cycle size. const EASY_POW = 0b00000100, /// Adds block while in syncing mode. const SYNC = 0b00001000, } } #[derive(Debug)] pub enum Error { /// The block doesn't fit anywhere in our chain Unfit(String), /// Difficulty is too low either compared to ours or the block PoW hash DifficultyTooLow, /// Addition of difficulties on all previous block is wrong WrongTotalDifficulty, /// Size of the Cuckoo graph in block header doesn't match PoW requirements WrongCuckooSize, /// The proof of work is invalid InvalidPow, /// The block doesn't sum correctly or a tx signature is invalid InvalidBlockProof(secp::Error), /// Block time is too old InvalidBlockTime, /// Block height is invalid (not previous + 1) InvalidBlockHeight, /// Internal issue when trying to save or load data from store StoreErr(grin_store::Error), SerErr(ser::Error), Other(String), } impl From for Error { fn from(e: grin_store::Error) -> Error { Error::StoreErr(e) } } impl From for Error { fn from(e: ser::Error) -> Error { Error::SerErr(e) } } /// The tip of a fork. A handle to the fork ancestry from its leaf in the /// blockchain tree. References the max height and the latest and previous /// blocks /// for convenience and the total difficulty. #[derive(Serialize, Deserialize, 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, /// Total difficulty accumulated on that fork pub total_difficulty: Difficulty, } 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, total_difficulty: Difficulty::one(), } } /// Append a new block to this tip, returning a new updated tip. pub fn from_block(bh: &BlockHeader) -> Tip { Tip { height: bh.height, last_block_h: bh.hash(), prev_block_h: bh.previous, total_difficulty: bh.total_difficulty.clone(), } } } /// Serialization of a tip, required to save to datastore. impl ser::Writeable for Tip { fn write(&self, writer: &mut W) -> Result<(), ser::Error> { try!(writer.write_u64(self.height)); try!(writer.write_fixed_bytes(&self.last_block_h)); try!(writer.write_fixed_bytes(&self.prev_block_h)); self.total_difficulty.write(writer) } } impl ser::Readable for Tip { fn read(reader: &mut ser::Reader) -> Result { let height = try!(reader.read_u64()); let last = try!(Hash::read(reader)); let prev = try!(Hash::read(reader)); let diff = try!(Difficulty::read(reader)); Ok(Tip { height: height, last_block_h: last, prev_block_h: prev, total_difficulty: diff, }) } } /// Trait the chain pipeline requires an implementor for in order to process /// blocks. pub trait ChainStore: Send + Sync { /// 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; /// Save the provided tip as the current head of our chain fn save_head(&self, t: &Tip) -> Result<(), store::Error>; /// Save the provided tip as the current head of the body chain, leaving the /// header chain alone. fn save_body_head(&self, t: &Tip) -> Result<(), store::Error>; /// Gets a block header by hash fn get_block(&self, h: &Hash) -> Result; /// Gets a block header by hash fn get_block_header(&self, h: &Hash) -> Result; /// Checks whether a block has been been processed and saved fn check_block_exists(&self, h: &Hash) -> Result; /// Save the provided block in store fn save_block(&self, b: &Block) -> Result<(), store::Error>; /// Save the provided block header in store fn save_block_header(&self, bh: &BlockHeader) -> Result<(), store::Error>; /// Get the tip of the header chain fn get_header_head(&self) -> Result; /// Save the provided tip as the current head of the block header chain fn save_header_head(&self, t: &Tip) -> Result<(), store::Error>; /// Gets the block header at the provided height fn get_header_by_height(&self, height: u64) -> Result; /// Gets an output by its commitment fn get_output_by_commit(&self, commit: &Commitment) -> Result; /// Checks whether an output commitment exists and returns the output hash fn has_output_commit(&self, commit: &Commitment) -> Result; /// Saves the provided block header at the corresponding height. Also check /// the consistency of the height chain in store by assuring previous /// headers /// are also at their respective heights. fn setup_height(&self, bh: &BlockHeader) -> Result<(), store::Error>; } /// Bridge between the chain pipeline and the rest of the system. Handles /// downstream processing of valid blocks by the rest of the system, most /// importantly the broadcasting of blocks to our peers. pub trait ChainAdapter { /// The blockchain pipeline has accepted this block as valid and added /// it to our chain. fn block_accepted(&self, b: &Block); } pub struct NoopAdapter { } impl ChainAdapter for NoopAdapter { fn block_accepted(&self, b: &Block) {} }