Files
grin-node/chain/src/types.rs
T
Antioch Peverell e22d025dc8 Simplify block marker metadata (#985)
* wip BlockMarker struct, get rid of PMMRMetadata
* use rewind to init the txhashet correctly on startup, we do not need to track pos via metadata (we have block markers), we do not need to open the txhashset with specific pos (we have rewind)
* better logging on init
* keep rewinding and validating on init, to find a good block
* use validate_roots on chain init
2018-04-24 19:53:01 +00:00

392 lines
12 KiB
Rust

// Copyright 2018 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 std::{error, fmt, io};
use util::secp;
use util::secp::pedersen::Commitment;
use core::core::hash::{Hash, Hashed};
use core::core::target::Difficulty;
use core::core::{block, transaction, Block, BlockHeader};
use core::ser::{self, Readable, Reader, Writeable, Writer};
use grin_store as store;
use grin_store;
use keychain;
bitflags! {
/// Options for block validation
pub struct Options: u32 {
/// No flags
const NONE = 0b00000000;
/// Runs without checking the Proof of Work, mostly to make testing easier.
const SKIP_POW = 0b00000001;
/// Adds block while in syncing mode.
const SYNC = 0b00000010;
/// Block validation on a block we mined ourselves
const MINE = 0b00000100;
}
}
/// A helper to hold the roots of the txhashset in order to keep them
/// readable
#[derive(Debug)]
pub struct TxHashSetRoots {
/// Output root
pub output_root: Hash,
/// Range Proof root
pub rproof_root: Hash,
/// Kernel root
pub kernel_root: Hash,
}
/// Errors
#[derive(Debug)]
pub enum Error {
/// The block doesn't fit anywhere in our chain
Unfit(String),
/// Special case of orphan blocks
Orphan,
/// Difficulty is too low either compared to ours or the block PoW hash
DifficultyTooLow,
/// Addition of difficulties on all previous block is wrong
WrongTotalDifficulty,
/// The proof of work is invalid
InvalidPow,
/// The block doesn't sum correctly or a tx signature is invalid
InvalidBlockProof(block::Error),
/// Block time is too old
InvalidBlockTime,
/// Block height is invalid (not previous + 1)
InvalidBlockHeight,
/// One of the root hashes in the block is invalid
InvalidRoot,
/// Something does not look right with the switch commitment
InvalidSwitchCommit,
/// Error from underlying keychain impl
Keychain(keychain::Error),
/// Error from underlying secp lib
Secp(secp::Error),
/// One of the inputs in the block has already been spent
AlreadySpent(Commitment),
/// An output with that commitment already exists (should be unique)
DuplicateCommitment(Commitment),
/// output not found
OutputNotFound,
/// output spent
OutputSpent,
/// Invalid block version, either a mistake or outdated software
InvalidBlockVersion(u16),
/// We've been provided a bad txhashset
InvalidTxHashSet(String),
/// Internal issue when trying to save or load data from store
StoreErr(grin_store::Error, String),
/// Internal issue when trying to save or load data from append only files
FileReadErr(String),
/// Error serializing or deserializing a type
SerErr(ser::Error),
/// Error with the txhashset
TxHashSetErr(String),
/// No chain exists and genesis block is required
GenesisBlockRequired,
/// Error from underlying tx handling
Transaction(transaction::Error),
/// Anything else
Other(String),
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
_ => "some kind of chain error",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
_ => write!(f, "some kind of chain error"),
}
}
}
impl From<grin_store::Error> for Error {
fn from(e: grin_store::Error) -> Error {
Error::StoreErr(e, "wrapped".to_owned())
}
}
impl From<ser::Error> for Error {
fn from(e: ser::Error) -> Error {
Error::SerErr(e)
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::TxHashSetErr(e.to_string())
}
}
impl From<keychain::Error> for Error {
fn from(e: keychain::Error) -> Error {
Error::Keychain(e)
}
}
impl From<secp::Error> for Error {
fn from(e: secp::Error) -> Error {
Error::Secp(e)
}
}
impl Error {
/// Whether the error is due to a block that was intrinsically wrong
pub fn is_bad_data(&self) -> bool {
// shorter to match on all the "not the block's fault" errors
match *self {
Error::Unfit(_)
| Error::Orphan
| Error::StoreErr(_, _)
| Error::SerErr(_)
| Error::TxHashSetErr(_)
| Error::GenesisBlockRequired
| Error::Other(_) => false,
_ => true,
}
}
}
impl From<transaction::Error> for Error {
fn from(e: transaction::Error) -> Error {
Error::Transaction(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<W: ser::Writer>(&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<Tip, ser::Error> {
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<Tip, store::Error>;
/// Block header for the chain head
fn head_header(&self) -> Result<BlockHeader, store::Error>;
/// 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<Block, store::Error>;
/// Check whether we have a block without reading it
fn block_exists(&self, h: &Hash) -> Result<bool, store::Error>;
/// Gets a block header by hash
fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, store::Error>;
/// Save the provided block in store
fn save_block(&self, b: &Block) -> Result<(), store::Error>;
/// Delete a full block. Does not delete any record associated with a block
/// header.
fn delete_block(&self, bh: &Hash) -> 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<Tip, store::Error>;
/// Save the provided tip as the current head of the block header chain
fn save_header_head(&self, t: &Tip) -> Result<(), store::Error>;
/// Get the tip of the current sync header chain
fn get_sync_head(&self) -> Result<Tip, store::Error>;
/// Save the provided tip as the current head of the sync header chain
fn save_sync_head(&self, t: &Tip) -> Result<(), store::Error>;
/// Initialize header_head if necessary and set sync_head to header_head.
fn init_head(&self) -> Result<(), store::Error>;
/// Reset header_head and sync_head to head of current body chain
fn reset_head(&self) -> Result<(), store::Error>;
/// Gets the block header at the provided height
fn get_header_by_height(&self, height: u64) -> Result<BlockHeader, store::Error>;
/// Save a header as associated with its height
fn save_header_height(&self, header: &BlockHeader) -> Result<(), store::Error>;
/// Delete the block header at the height
fn delete_header_by_height(&self, height: u64) -> Result<(), store::Error>;
/// Is the block header on the current chain?
/// Use the header_by_height index to verify the block header is where we
/// think it is.
fn is_on_current_chain(&self, header: &BlockHeader) -> Result<(), store::Error>;
/// Saves the position of an output, represented by its commitment, in the
/// Output MMR. Used as an index for spending and pruning.
fn save_output_pos(&self, commit: &Commitment, pos: u64) -> Result<(), store::Error>;
/// Gets the position of an output, represented by its commitment, in the
/// Output MMR. Used as an index for spending and pruning.
fn get_output_pos(&self, commit: &Commitment) -> Result<u64, store::Error>;
/// Deletes the MMR position of an output.
fn delete_output_pos(&self, commit: &[u8]) -> Result<(), store::Error>;
/// Saves a marker associated with a block recording the MMR positions of
/// its last elements.
fn save_block_marker(&self, bh: &Hash, marker: &BlockMarker) -> Result<(), store::Error>;
/// Retrieves a block marker from a block hash.
fn get_block_marker(&self, bh: &Hash) -> Result<BlockMarker, store::Error>;
/// Deletes a block marker associated with the provided hash
fn delete_block_marker(&self, bh: &Hash) -> Result<(), store::Error>;
/// 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, old_tip: &Tip) -> Result<(), store::Error>;
/// Similar to setup_height but without handling fork
fn build_by_height_index(&self, header: &BlockHeader, force: bool) -> 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, opts: Options);
}
/// Dummy adapter used as a placeholder for real implementations
pub struct NoopAdapter {}
impl ChainAdapter for NoopAdapter {
fn block_accepted(&self, _: &Block, _: Options) {}
}
/// The output and kernel positions that define the size of the MMRs for a
/// particular block.
#[derive(Debug, Clone)]
pub struct BlockMarker {
/// The output (and rangeproof) MMR position of the final output in the
/// block
pub output_pos: u64,
/// The kernel position of the final kernel in the block
pub kernel_pos: u64,
}
impl Writeable for BlockMarker {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_u64(self.output_pos)?;
writer.write_u64(self.kernel_pos)?;
Ok(())
}
}
impl Readable for BlockMarker {
fn read(reader: &mut Reader) -> Result<BlockMarker, ser::Error> {
Ok(BlockMarker {
output_pos: reader.read_u64()?,
kernel_pos: reader.read_u64()?,
})
}
}
impl Default for BlockMarker {
fn default() -> BlockMarker {
BlockMarker {
output_pos: 0,
kernel_pos: 0,
}
}
}