Error handling improvements (particularly in chain) (#1208)

* update error handling in chain and other modules to use error/errorkind

* sizeshift errorkind
This commit is contained in:
Yeastplume 2018-06-30 23:36:38 +01:00 committed by GitHub
parent f0d5406d0b
commit d2a84b7600
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 657 additions and 435 deletions

6
Cargo.lock generated
View file

@ -591,6 +591,8 @@ dependencies = [
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"croaring 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_core 0.2.0",
"grin_keychain 0.2.0",
"grin_store 0.2.0",
@ -626,6 +628,8 @@ dependencies = [
"blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"croaring 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_keychain 0.2.0",
"grin_util 0.2.0",
"grin_wallet 0.2.0",
@ -729,6 +733,8 @@ dependencies = [
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"croaring 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"grin_core 0.2.0",
"grin_util 0.2.0",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -9,6 +9,8 @@ publish = false
bitflags = "1"
byteorder = "1"
lmdb-zero = "0.4.4"
failure = "0.1"
failure_derive = "0.1"
croaring = "0.3"
slog = { version = "~2.2", features = ["max_level_trace", "release_max_level_trace"] }
serde = "1"

View file

@ -27,13 +27,14 @@ use core::core::merkle_proof::MerkleProof;
use core::core::target::Difficulty;
use core::core::{Block, BlockHeader, Output, OutputIdentifier, Transaction, TxKernel};
use core::global;
use error::{Error, ErrorKind};
use grin_store::Error::NotFoundErr;
use pipe;
use store;
use txhashset;
use types::{ChainAdapter, Error, Options, Tip};
use util::LOGGER;
use types::{ChainAdapter, Options, Tip};
use util::secp::pedersen::{Commitment, RangeProof};
use util::LOGGER;
/// Orphan pool size is limited by MAX_ORPHAN_SIZE
pub const MAX_ORPHAN_SIZE: usize = 200;
@ -210,9 +211,7 @@ impl Chain {
b: Block,
opts: Options,
) -> Result<(Option<Tip>, Option<Block>), Error> {
let head = self.store
.head()
.map_err(|e| Error::StoreErr(e, "chain load head".to_owned()))?;
let head = self.store.head()?;
let mut ctx = self.ctx_from_head(head, opts)?;
let res = pipe::process_block(&b, &mut ctx);
@ -252,47 +251,51 @@ impl Chain {
}
Ok((None, Some(b)))
}
Err(Error::Orphan) => {
let block_hash = b.hash();
let orphan = Orphan {
block: b,
opts: opts,
added: Instant::now(),
};
// In the case of a fork - it is possible to have multiple blocks
// that are children of a given block.
// We do not handle this currently for orphans (future enhancement?).
// We just assume "last one wins" for now.
&self.orphans.add(orphan);
debug!(
LOGGER,
"process_block: orphan: {:?}, # orphans {}",
block_hash,
self.orphans.len(),
);
Err(Error::Orphan)
}
Err(Error::Unfit(ref msg)) => {
debug!(
LOGGER,
"Block {} at {} is unfit at this time: {}",
b.hash(),
b.header.height,
msg
);
Err(Error::Unfit(msg.clone()))
}
Err(e) => {
info!(
LOGGER,
"Rejected block {} at {}: {:?}",
b.hash(),
b.header.height,
e
);
Err(e)
match e.kind() {
ErrorKind::Orphan => {
let block_hash = b.hash();
let orphan = Orphan {
block: b,
opts: opts,
added: Instant::now(),
};
// In the case of a fork - it is possible to have multiple blocks
// that are children of a given block.
// We do not handle this currently for orphans (future enhancement?).
// We just assume "last one wins" for now.
&self.orphans.add(orphan);
debug!(
LOGGER,
"process_block: orphan: {:?}, # orphans {}",
block_hash,
self.orphans.len(),
);
Err(ErrorKind::Orphan.into())
}
ErrorKind::Unfit(ref msg) => {
debug!(
LOGGER,
"Block {} at {} is unfit at this time: {}",
b.hash(),
b.header.height,
msg
);
Err(ErrorKind::Unfit(msg.clone()).into())
}
_ => {
info!(
LOGGER,
"Rejected block {} at {}: {:?}",
b.hash(),
b.header.height,
e
);
Err(ErrorKind::Other(format!("{:?}", e).to_owned()).into())
}
}
}
}
}
@ -409,7 +412,7 @@ impl Chain {
if tx.lock_height() <= height {
Ok(())
} else {
Err(Error::TxLockHeight)
Err(ErrorKind::TxLockHeight.into())
}
}
@ -529,7 +532,7 @@ impl Chain {
let head = self.head().unwrap();
let header_head = self.get_header_head().unwrap();
if header_head.height - head.height < global::cut_through_horizon() as u64 {
return Err(Error::InvalidTxHashSet("not needed".to_owned()));
return Err(ErrorKind::InvalidTxHashSet("not needed".to_owned()).into());
}
let header = self.store.get_block_header(&h)?;
@ -623,17 +626,21 @@ impl Chain {
batch.delete_block(&b.hash())?;
batch.delete_block_input_bitmap(&b.hash())?;
}
Err(NotFoundErr) => {
Err(NotFoundErr(_)) => {
break;
}
Err(e) => return Err(Error::StoreErr(e, "retrieving block to compact".to_owned())),
Err(e) => {
return Err(
ErrorKind::StoreErr(e, "retrieving block to compact".to_owned()).into(),
)
}
}
if current.height <= 1 {
break;
}
match self.store.get_block_header(&current.previous) {
Ok(h) => current = h,
Err(NotFoundErr) => break,
Err(NotFoundErr(_)) => break,
Err(e) => return Err(From::from(e)),
}
}
@ -671,9 +678,9 @@ impl Chain {
let outputs = txhashset.outputs_by_insertion_index(start_index, max);
let rangeproofs = txhashset.rangeproofs_by_insertion_index(start_index, max);
if outputs.0 != rangeproofs.0 || outputs.1.len() != rangeproofs.1.len() {
return Err(Error::TxHashSetErr(String::from(
return Err(ErrorKind::TxHashSetErr(String::from(
"Output and rangeproof sets don't match",
)));
)).into());
}
let mut output_vec: Vec<Output> = vec![];
for (ref x, &y) in outputs.1.iter().zip(rangeproofs.1.iter()) {
@ -704,9 +711,7 @@ impl Chain {
/// Reset header_head and sync_head to head of current body chain
pub fn reset_head(&self) -> Result<(), Error> {
let batch = self.store.batch()?;
batch
.reset_head()
.map_err(|e| Error::StoreErr(e, "chain reset_head".to_owned()))?;
batch.reset_head()?;
batch.commit()?;
Ok(())
}
@ -720,28 +725,28 @@ impl Chain {
pub fn head_header(&self) -> Result<BlockHeader, Error> {
self.store
.head_header()
.map_err(|e| Error::StoreErr(e, "chain head header".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "chain head header".to_owned()).into())
}
/// Gets a block header by hash
pub fn get_block(&self, h: &Hash) -> Result<Block, Error> {
self.store
.get_block(h)
.map_err(|e| Error::StoreErr(e, "chain get block".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "chain get block".to_owned()).into())
}
/// Gets a block header by hash
pub fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, Error> {
self.store
.get_block_header(h)
.map_err(|e| Error::StoreErr(e, "chain get header".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "chain get header".to_owned()).into())
}
/// Gets the block header at the provided height
pub fn get_header_by_height(&self, height: u64) -> Result<BlockHeader, Error> {
self.store
.get_header_by_height(height)
.map_err(|e| Error::StoreErr(e, "chain get header by height".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "chain get header by height".to_owned()).into())
}
/// Verifies the given block header is actually on the current chain.
@ -750,7 +755,7 @@ impl Chain {
pub fn is_on_current_chain(&self, header: &BlockHeader) -> Result<(), Error> {
self.store
.is_on_current_chain(header)
.map_err(|e| Error::StoreErr(e, "chain is_on_current_chain".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "chain is_on_current_chain".to_owned()).into())
}
/// Get the tip of the current "sync" header chain.
@ -758,14 +763,14 @@ impl Chain {
pub fn get_sync_head(&self) -> Result<Tip, Error> {
self.store
.get_sync_head()
.map_err(|e| Error::StoreErr(e, "chain get sync head".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "chain get sync head".to_owned()).into())
}
/// Get the tip of the header chain.
pub fn get_header_head(&self) -> Result<Tip, Error> {
self.store
.get_header_head()
.map_err(|e| Error::StoreErr(e, "chain get header head".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "chain get header head".to_owned()).into())
}
/// Builds an iterator on blocks starting from the current chain head and
@ -780,7 +785,7 @@ impl Chain {
pub fn block_exists(&self, h: Hash) -> Result<bool, Error> {
self.store
.block_exists(&h)
.map_err(|e| Error::StoreErr(e, "chain block exists".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "chain block exists".to_owned()).into())
}
}
@ -830,7 +835,7 @@ fn setup_head(
}
}
}
Err(NotFoundErr) => {
Err(NotFoundErr(_)) => {
let tip = Tip::from_block(&genesis.header);
batch.save_block(&genesis)?;
batch.setup_height(&genesis.header, &tip)?;
@ -844,7 +849,7 @@ fn setup_head(
head = tip;
info!(LOGGER, "chain: init: saved genesis: {:?}", genesis.hash());
}
Err(e) => return Err(Error::StoreErr(e, "chain init load head".to_owned())),
Err(e) => return Err(ErrorKind::StoreErr(e, "chain init load head".to_owned()))?,
};
// Initialize header_head and sync_head as necessary for chain init.

248
chain/src/error.rs Normal file
View file

@ -0,0 +1,248 @@
// 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.
//! Error types for chain
use failure::{Backtrace, Context, Fail};
use std::fmt::{self, Display};
use std::io;
use core::core::{block, committed, transaction};
use core::ser;
use grin_store as store;
use keychain;
use util::secp;
use util::secp::pedersen::Commitment;
/// Error definition
#[derive(Debug, Fail)]
pub struct Error {
inner: Context<ErrorKind>,
}
/// Chain error definitions
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
/// The block doesn't fit anywhere in our chain
#[fail(display = "Block is unfit: {}", _0)]
Unfit(String),
/// Special case of orphan blocks
#[fail(display = "Orphan")]
Orphan,
/// Difficulty is too low either compared to ours or the block PoW hash
#[fail(display = "Difficulty is too low compared to ours or the block PoW hash")]
DifficultyTooLow,
/// Addition of difficulties on all previous block is wrong
#[fail(display = "Addition of difficulties on all previous blocks is wrong")]
WrongTotalDifficulty,
/// Block header sizeshift is lower than our min
#[fail(display = "Cuckoo Size too Low")]
LowSizeshift,
/// The proof of work is invalid
#[fail(display = "Invalid PoW")]
InvalidPow,
/// The block doesn't sum correctly or a tx signature is invalid
#[fail(display = "Invalid Block Proof")]
InvalidBlockProof(block::Error),
/// Block time is too old
#[fail(display = "Invalid Block Time")]
InvalidBlockTime,
/// Block height is invalid (not previous + 1)
#[fail(display = "Invalid Block Height")]
InvalidBlockHeight,
/// One of the root hashes in the block is invalid
#[fail(display = "Invalid Root")]
InvalidRoot,
/// One of the MMR sizes in the block header is invalid
#[fail(display = "Invalid MMR Size")]
InvalidMMRSize,
/// Error from underlying keychain impl
#[fail(display = "Keychain Error")]
Keychain(keychain::Error),
/// Error from underlying secp lib
#[fail(display = "Secp Lib Error")]
Secp(secp::Error),
/// One of the inputs in the block has already been spent
#[fail(display = "Already Spent: {:?}", _0)]
AlreadySpent(Commitment),
/// An output with that commitment already exists (should be unique)
#[fail(display = "Dupliate Commitment: {:?}", _0)]
DuplicateCommitment(Commitment),
/// Attempt to spend a coinbase output before it sufficiently matures.
#[fail(display = "Attempt to spend immature coinbase")]
ImmatureCoinbase,
/// Error validating a Merkle proof (coinbase output)
#[fail(display = "Error validating merkle proof")]
MerkleProof,
/// output not found
#[fail(display = "Output not found")]
OutputNotFound,
/// output spent
#[fail(display = "Output is spent")]
OutputSpent,
/// Invalid block version, either a mistake or outdated software
#[fail(display = "Invalid Block Version: {}", _0)]
InvalidBlockVersion(u16),
/// We've been provided a bad txhashset
#[fail(display = "Invalid TxHashSet: {}", _0)]
InvalidTxHashSet(String),
/// Internal issue when trying to save or load data from store
#[fail(display = "Store Error: {}", _1)]
StoreErr(store::Error, String),
/// Internal issue when trying to save or load data from append only files
#[fail(display = "File Read Error: {}", _0)]
FileReadErr(String),
/// Error serializing or deserializing a type
#[fail(display = "Serialization Error")]
SerErr(ser::Error),
/// Error with the txhashset
#[fail(display = "TxHashSetErr: {}", _0)]
TxHashSetErr(String),
/// Tx not valid based on lock_height.
#[fail(display = "Transaction Lock Height")]
TxLockHeight,
/// No chain exists and genesis block is required
#[fail(display = "Genesis Block Required")]
GenesisBlockRequired,
/// Error from underlying tx handling
#[fail(display = "Transaction Error")]
Transaction(transaction::Error),
/// Anything else
#[fail(display = "Other Error: {}", _0)]
Other(String),
/// Error from summing and verifying kernel sums via committed trait.
#[fail(display = "Committed Trait: Error summing and verifying kernel sums")]
Committed(committed::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let cause = match self.cause() {
Some(c) => format!("{}", c),
None => String::from("Unknown"),
};
let backtrace = match self.backtrace() {
Some(b) => format!("{}", b),
None => String::from("Unknown"),
};
let output = format!(
"{} \n Cause: {} \n Backtrace: {}",
self.inner, cause, backtrace
);
Display::fmt(&output, f)
}
}
impl Error {
/// get kind
pub fn kind(&self) -> ErrorKind {
self.inner.get_context().clone()
}
/// get cause
pub fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
/// get backtrace
pub fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
/// 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.kind() {
ErrorKind::Unfit(_)
| ErrorKind::Orphan
| ErrorKind::StoreErr(_, _)
| ErrorKind::SerErr(_)
| ErrorKind::TxHashSetErr(_)
| ErrorKind::GenesisBlockRequired
| ErrorKind::Other(_) => false,
_ => true,
}
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner: inner }
}
}
impl From<block::Error> for Error {
fn from(error: block::Error) -> Error {
let ec = error.clone();
Error {
inner: error.context(ErrorKind::InvalidBlockProof(ec)),
}
}
}
impl From<store::Error> for Error {
fn from(error: store::Error) -> Error {
let ec = error.clone();
Error {
//inner: error.context();Context::new(ErrorKind::StoreErr(error.clone(),
// format!("{:?}", error))),
inner: error.context(ErrorKind::StoreErr(ec.clone(), format!("{:?}", ec))),
}
}
}
impl From<keychain::Error> for Error {
fn from(error: keychain::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Keychain(error)),
}
}
}
impl From<transaction::Error> for Error {
fn from(error: transaction::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Transaction(error)),
}
}
}
impl From<committed::Error> for Error {
fn from(error: committed::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Committed(error)),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error {
inner: Context::new(ErrorKind::TxHashSetErr(e.to_string())),
}
}
}
impl From<secp::Error> for Error {
fn from(e: secp::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Secp(e)),
}
}
}

View file

@ -31,7 +31,10 @@ extern crate serde;
extern crate serde_derive;
#[macro_use]
extern crate slog;
extern crate failure;
extern crate time;
#[macro_use]
extern crate failure_derive;
extern crate grin_core as core;
extern crate grin_keychain as keychain;
@ -39,6 +42,7 @@ extern crate grin_store;
extern crate grin_util as util;
mod chain;
mod error;
pub mod pipe;
pub mod store;
pub mod txhashset;
@ -47,5 +51,6 @@ pub mod types;
// Re-export the base interface
pub use chain::{Chain, MAX_ORPHAN_SIZE};
pub use error::{Error, ErrorKind};
pub use store::ChainStore;
pub use types::{ChainAdapter, Error, Options, Tip};
pub use types::{ChainAdapter, Options, Tip};

View file

@ -23,12 +23,15 @@ use core::core::hash::{Hash, Hashed};
use core::core::target::Difficulty;
use core::core::{Block, BlockHeader};
use core::global;
use error::{Error, ErrorKind};
use grin_store;
use store;
use txhashset;
use types::{Error, Options, Tip};
use types::{Options, Tip};
use util::LOGGER;
use failure::ResultExt;
/// Contextual information required to process a new block and either reject or
/// accept it.
pub struct BlockContext {
@ -75,10 +78,10 @@ pub fn process_block(b: &Block, ctx: &mut BlockContext) -> Result<Option<Tip>, E
match ctx.store.block_exists(&b.header.previous) {
Ok(true) => {}
Ok(false) => {
return Err(Error::Orphan);
return Err(ErrorKind::Orphan.into());
}
Err(e) => {
return Err(Error::StoreErr(e, "pipe get previous".to_owned()));
return Err(ErrorKind::StoreErr(e, "pipe get previous".to_owned()).into());
}
}
}
@ -93,9 +96,7 @@ pub fn process_block(b: &Block, ctx: &mut BlockContext) -> Result<Option<Tip>, E
let mut txhashset = local_txhashset.write().unwrap();
// update head now that we're in the lock
ctx.head = ctx.store
.head()
.map_err(|e| Error::StoreErr(e, "pipe reload head".to_owned()))?;
ctx.head = ctx.store.head()?;
let mut batch = ctx.store.batch()?;
@ -176,13 +177,13 @@ pub fn process_block_header(bh: &BlockHeader, ctx: &mut BlockContext) -> Result<
fn check_header_known(bh: Hash, ctx: &mut BlockContext) -> Result<(), Error> {
// TODO ring buffer of the last few blocks that came through here
if bh == ctx.head.last_block_h || bh == ctx.head.prev_block_h {
return Err(Error::Unfit("already known".to_string()));
return Err(ErrorKind::Unfit("already known".to_string()).into());
}
if let Ok(h) = ctx.store.get_block_header(&bh) {
// there is a window where a block header can be saved but the chain head not
// updated yet, we plug that window here by re-accepting the block
if h.total_difficulty <= ctx.head.total_difficulty {
return Err(Error::Unfit("already in store".to_string()));
return Err(ErrorKind::Unfit("already in store".to_string()).into());
}
}
Ok(())
@ -193,13 +194,13 @@ fn check_header_known(bh: Hash, ctx: &mut BlockContext) -> Result<(), Error> {
fn check_known(bh: Hash, ctx: &mut BlockContext) -> Result<(), Error> {
// TODO ring buffer of the last few blocks that came through here
if bh == ctx.head.last_block_h || bh == ctx.head.prev_block_h {
return Err(Error::Unfit("already known".to_string()));
return Err(ErrorKind::Unfit("already known".to_string()).into());
}
if let Ok(b) = ctx.store.get_block(&bh) {
// there is a window where a block can be saved but the chain head not
// updated yet, we plug that window here by re-accepting the block
if b.header.total_difficulty <= ctx.head.total_difficulty {
return Err(Error::Unfit("already in store".to_string()));
return Err(ErrorKind::Unfit("already in store".to_string()).into());
}
}
Ok(())
@ -216,7 +217,7 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
LOGGER,
"Invalid block header version received ({}), maybe update Grin?", header.version
);
return Err(Error::InvalidBlockVersion(header.version));
return Err(ErrorKind::InvalidBlockVersion(header.version).into());
}
// TODO: remove CI check from here somehow
@ -226,12 +227,12 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
{
// refuse blocks more than 12 blocks intervals in future (as in bitcoin)
// TODO add warning in p2p code if local time is too different from peers
return Err(Error::InvalidBlockTime);
return Err(ErrorKind::InvalidBlockTime.into());
}
if !ctx.opts.contains(Options::SKIP_POW) {
if global::min_sizeshift() > header.pow.cuckoo_sizeshift {
return Err(Error::LowSizeshift);
return Err(ErrorKind::LowSizeshift.into());
}
if !(ctx.pow_verifier)(header, header.pow.cuckoo_sizeshift) {
error!(
@ -239,31 +240,32 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
"pipe: validate_header failed for cuckoo shift size {}",
header.pow.cuckoo_sizeshift,
);
return Err(Error::InvalidPow);
return Err(ErrorKind::InvalidPow.into());
}
}
// first I/O cost, better as late as possible
let prev = match ctx.store.get_block_header(&header.previous) {
Ok(prev) => Ok(prev),
Err(grin_store::Error::NotFoundErr) => Err(Error::Orphan),
Err(e) => Err(Error::StoreErr(
e,
format!("previous header {}", header.previous),
)),
}?;
Ok(prev) => prev,
Err(grin_store::Error::NotFoundErr(_)) => return Err(ErrorKind::Orphan.into()),
Err(e) => {
return Err(
ErrorKind::StoreErr(e, format!("previous header {}", header.previous)).into(),
)
}
};
// make sure this header has a height exactly one higher than the previous
// header
if header.height != prev.height + 1 {
return Err(Error::InvalidBlockHeight);
return Err(ErrorKind::InvalidBlockHeight.into());
}
// TODO - get rid of the automated testing mode check here somehow
if header.timestamp <= prev.timestamp && !global::is_automated_testing_mode() {
// prevent time warp attacks and some timestamp manipulations by forcing strict
// time progression (but not in CI mode)
return Err(Error::InvalidBlockTime);
return Err(ErrorKind::InvalidBlockTime.into());
}
// verify the proof of work and related parameters
@ -274,27 +276,27 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
// as the target difficulty
if !ctx.opts.contains(Options::SKIP_POW) {
if header.total_difficulty.clone() <= prev.total_difficulty.clone() {
return Err(Error::DifficultyTooLow);
return Err(ErrorKind::DifficultyTooLow.into());
}
let target_difficulty = header.total_difficulty.clone() - prev.total_difficulty.clone();
if header.pow.to_difficulty() < target_difficulty {
return Err(Error::DifficultyTooLow);
return Err(ErrorKind::DifficultyTooLow.into());
}
// explicit check to ensure we are not below the minimum difficulty
// we will also check difficulty based on next_difficulty later on
if target_difficulty < Difficulty::one() {
return Err(Error::DifficultyTooLow);
return Err(ErrorKind::DifficultyTooLow.into());
}
// explicit check to ensure total_difficulty has increased by exactly
// the _network_ difficulty of the previous block
// (during testnet1 we use _block_ difficulty here)
let diff_iter = store::DifficultyIter::from(header.previous, ctx.store.clone());
let network_difficulty =
consensus::next_difficulty(diff_iter).map_err(|e| Error::Other(e.to_string()))?;
let network_difficulty = consensus::next_difficulty(diff_iter)
.context(ErrorKind::Other("network difficulty".to_owned()))?;
if target_difficulty != network_difficulty.clone() {
error!(
LOGGER,
@ -302,7 +304,7 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
target_difficulty.to_num(),
prev.total_difficulty.to_num() + network_difficulty.to_num()
);
return Err(Error::WrongTotalDifficulty);
return Err(ErrorKind::WrongTotalDifficulty.into());
}
}
@ -312,7 +314,7 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E
fn validate_block(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
let prev = ctx.store.get_block_header(&b.header.previous)?;
b.validate(&prev.total_kernel_offset, &prev.total_kernel_sum)
.map_err(&Error::InvalidBlockProof)?;
.map_err(|e| ErrorKind::InvalidBlockProof(e))?;
Ok(())
}
@ -329,7 +331,8 @@ fn validate_block_via_txhashset(b: &Block, ext: &mut txhashset::Extension) -> Re
ext.apply_block(&b)?;
let roots = ext.roots();
if roots.output_root != b.header.output_root || roots.rproof_root != b.header.range_proof_root
if roots.output_root != b.header.output_root
|| roots.rproof_root != b.header.range_proof_root
|| roots.kernel_root != b.header.kernel_root
{
ext.dump(false);
@ -353,11 +356,11 @@ fn validate_block_via_txhashset(b: &Block, ext: &mut txhashset::Extension) -> Re
b.header.kernel_root,
);
return Err(Error::InvalidRoot);
return Err(ErrorKind::InvalidRoot.into());
}
let sizes = ext.sizes();
if b.header.output_mmr_size != sizes.0 || b.header.kernel_mmr_size != sizes.2 {
return Err(Error::InvalidMMRSize);
return Err(ErrorKind::InvalidMMRSize.into());
}
Ok(())
@ -371,7 +374,7 @@ fn add_block(
) -> Result<(), Error> {
batch
.save_block(b)
.map_err(|e| Error::StoreErr(e, "pipe save block".to_owned()))?;
.map_err(|e| ErrorKind::StoreErr(e, "pipe save block".to_owned()))?;
let bitmap = store.build_and_cache_block_input_bitmap(&b)?;
batch.save_block_input_bitmap(&b.hash(), &bitmap)?;
Ok(())
@ -381,7 +384,7 @@ fn add_block(
fn add_block_header(bh: &BlockHeader, batch: &mut store::Batch) -> Result<(), Error> {
batch
.save_block_header(bh)
.map_err(|e| Error::StoreErr(e, "pipe save header".to_owned()))
.map_err(|e| ErrorKind::StoreErr(e, "pipe save header".to_owned()).into())
}
/// Directly updates the head if we've just appended a new block to it or handle
@ -394,7 +397,7 @@ fn update_head(b: &Block, ctx: &BlockContext, batch: &store::Batch) -> Result<Op
// update the block height index
batch
.setup_height(&b.header, &ctx.head)
.map_err(|e| Error::StoreErr(e, "pipe setup height".to_owned()))?;
.map_err(|e| ErrorKind::StoreErr(e, "pipe setup height".to_owned()))?;
// in sync mode, only update the "body chain", otherwise update both the
// "header chain" and "body chain", updating the header chain in sync resets
@ -403,11 +406,11 @@ fn update_head(b: &Block, ctx: &BlockContext, batch: &store::Batch) -> Result<Op
if ctx.opts.contains(Options::SYNC) {
batch
.save_body_head(&tip)
.map_err(|e| Error::StoreErr(e, "pipe save body".to_owned()))?;
.map_err(|e| ErrorKind::StoreErr(e, "pipe save body".to_owned()))?;
} else {
batch
.save_head(&tip)
.map_err(|e| Error::StoreErr(e, "pipe save head".to_owned()))?;
.map_err(|e| ErrorKind::StoreErr(e, "pipe save head".to_owned()))?;
}
debug!(
LOGGER,
@ -436,7 +439,7 @@ fn update_sync_head(
let tip = Tip::from_block(bh);
batch
.save_sync_head(&tip)
.map_err(|e| Error::StoreErr(e, "pipe save sync head".to_owned()))?;
.map_err(|e| ErrorKind::StoreErr(e, "pipe save sync head".to_owned()))?;
ctx.head = tip.clone();
debug!(LOGGER, "sync head {} @ {}", bh.hash(), bh.height);
Ok(Some(tip))
@ -451,7 +454,7 @@ fn update_header_head(
if tip.total_difficulty > ctx.head.total_difficulty {
batch
.save_header_head(&tip)
.map_err(|e| Error::StoreErr(e, "pipe save header head".to_owned()))?;
.map_err(|e| ErrorKind::StoreErr(e, "pipe save header head".to_owned()))?;
ctx.head = tip.clone();
debug!(LOGGER, "header head {} @ {}", bh.hash(), bh.height);
Ok(Some(tip))
@ -509,7 +512,7 @@ pub fn rewind_and_apply_fork(
for (_, h) in fork_hashes {
let fb = store
.get_block(&h)
.map_err(|e| Error::StoreErr(e, format!("getting forked blocks")))?;
.map_err(|e| ErrorKind::StoreErr(e, format!("getting forked blocks")))?;
ext.apply_block(&fb)?;
}
Ok(())

View file

@ -27,7 +27,7 @@ use core::core::hash::{Hash, Hashed};
use core::core::target::Difficulty;
use core::core::{Block, BlockHeader};
use grin_store as store;
use grin_store::{option_to_not_found, to_key, Error, u64_to_key};
use grin_store::{option_to_not_found, to_key, u64_to_key, Error};
use types::Tip;
const STORE_SUBPATH: &'static str = "chain";
@ -63,7 +63,7 @@ impl ChainStore {
#[allow(missing_docs)]
impl ChainStore {
pub fn head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]))
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]), "HEAD")
}
pub fn head_header(&self) -> Result<BlockHeader, Error> {
@ -71,15 +71,18 @@ impl ChainStore {
}
pub fn get_header_head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![HEADER_HEAD_PREFIX]))
option_to_not_found(self.db.get_ser(&vec![HEADER_HEAD_PREFIX]), "HEADER_HEAD")
}
pub fn get_sync_head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![SYNC_HEAD_PREFIX]))
option_to_not_found(self.db.get_ser(&vec![SYNC_HEAD_PREFIX]), "SYNC_HEAD")
}
pub fn get_block(&self, h: &Hash) -> Result<Block, Error> {
option_to_not_found(self.db.get_ser(&to_key(BLOCK_PREFIX, &mut h.to_vec())))
option_to_not_found(
self.db.get_ser(&to_key(BLOCK_PREFIX, &mut h.to_vec())),
&format!("BLOCK: {} ", h),
)
}
pub fn block_exists(&self, h: &Hash) -> Result<bool, Error> {
@ -99,6 +102,7 @@ impl ChainStore {
let header: Result<BlockHeader, Error> = option_to_not_found(
self.db
.get_ser(&to_key(BLOCK_HEADER_PREFIX, &mut h.to_vec())),
&format!("BLOCK HEADER: {}", h),
);
// cache miss - so adding to the cache for next time
@ -121,26 +125,33 @@ impl ChainStore {
// check we are not out ahead of the current head
if header.height > head.height {
return Err(Error::NotFoundErr);
return Err(Error::NotFoundErr(String::from(
"header.height > head.height",
)));
}
let header_at_height = self.get_header_by_height(header.height)?;
if header.hash() == header_at_height.hash() {
Ok(())
} else {
Err(Error::NotFoundErr)
Err(Error::NotFoundErr(String::from(
"header.hash == header_at_height.hash",
)))
}
}
pub fn get_header_by_height(&self, height: u64) -> Result<BlockHeader, Error> {
option_to_not_found(self.db.get_ser(&u64_to_key(HEADER_HEIGHT_PREFIX, height)))
.and_then(|hash| self.get_block_header(&hash))
option_to_not_found(
self.db.get_ser(&u64_to_key(HEADER_HEIGHT_PREFIX, height)),
&format!("Header at height: {}", height),
).and_then(|hash| self.get_block_header(&hash))
}
pub fn get_output_pos(&self, commit: &Commitment) -> Result<u64, Error> {
option_to_not_found(
self.db
.get_ser(&to_key(COMMIT_POS_PREFIX, &mut commit.as_ref().to_vec())),
&format!("Output position for: {:?}", commit),
)
}
@ -239,7 +250,7 @@ impl<'a> Batch<'a> {
pub fn init_sync_head(&self, t: &Tip) -> Result<(), Error> {
let header_tip = match self.store.get_header_head() {
Ok(hh) => hh,
Err(store::Error::NotFoundErr) => {
Err(store::Error::NotFoundErr(_)) => {
self.save_header_head(t)?;
t.clone()
}
@ -257,7 +268,10 @@ impl<'a> Batch<'a> {
/// get block
pub fn get_block(&self, h: &Hash) -> Result<Block, Error> {
option_to_not_found(self.db.get_ser(&to_key(BLOCK_PREFIX, &mut h.to_vec())))
option_to_not_found(
self.db.get_ser(&to_key(BLOCK_PREFIX, &mut h.to_vec())),
&format!("Block with hash: {}", h),
)
}
/// Save the block and its header
@ -303,6 +317,7 @@ impl<'a> Batch<'a> {
option_to_not_found(
self.db
.get_ser(&to_key(COMMIT_POS_PREFIX, &mut commit.as_ref().to_vec())),
&format!("Output position for commit: {:?}", commit),
)
}
@ -315,6 +330,7 @@ impl<'a> Batch<'a> {
option_to_not_found(
self.db
.get_ser(&to_key(BLOCK_HEADER_PREFIX, &mut h.to_vec())),
&format!("Block header for block: {}", h),
)
}

View file

@ -31,16 +31,18 @@ use core::core::hash::{Hash, Hashed};
use core::core::merkle_proof::MerkleProof;
use core::core::pmmr;
use core::core::pmmr::PMMR;
use core::core::{Block, BlockHeader, Input, Output, OutputFeatures, OutputIdentifier, Transaction,
TxKernel};
use core::core::{
Block, BlockHeader, Input, Output, OutputFeatures, OutputIdentifier, Transaction, TxKernel,
};
use core::global;
use core::ser::{PMMRIndexHashable, PMMRable};
use error::{Error, ErrorKind};
use grin_store;
use grin_store::pmmr::PMMRBackend;
use grin_store::types::prune_noop;
use store::{Batch, ChainStore};
use types::{Error, TxHashSetRoots};
use types::TxHashSetRoots;
use util::{secp_static, zip, LOGGER};
const TXHASHSET_SUBDIR: &'static str = "txhashset";
@ -138,14 +140,14 @@ impl TxHashSet {
if hash == output_id.hash_with_index(pos - 1) {
Ok(hash)
} else {
Err(Error::TxHashSetErr(format!("txhashset hash mismatch")))
Err(ErrorKind::TxHashSetErr(format!("txhashset hash mismatch")).into())
}
} else {
Err(Error::OutputNotFound)
Err(ErrorKind::OutputNotFound.into())
}
}
Err(grin_store::Error::NotFoundErr) => Err(Error::OutputNotFound),
Err(e) => Err(Error::StoreErr(e, format!("txhashset unspent check"))),
Err(grin_store::Error::NotFoundErr(_)) => Err(ErrorKind::OutputNotFound.into()),
Err(e) => Err(ErrorKind::StoreErr(e, format!("txhashset unspent check")).into()),
}
}
@ -338,7 +340,7 @@ where
Err(e) => {
debug!(
LOGGER,
"Error returned, discarding txhashset extension: {:?}", e
"Error returned, discarding txhashset extension: {}", e
);
trees.output_pmmr_h.backend.discard();
trees.rproof_pmmr_h.backend.discard();
@ -577,7 +579,7 @@ impl<'a> Extension<'a> {
// If we have not yet reached 1,000 blocks then
// we can fail immediately as coinbase cannot be mature.
if height < global::coinbase_maturity() {
return Err(Error::ImmatureCoinbase);
return Err(ErrorKind::ImmatureCoinbase.into());
}
// Find the "cutoff" pos in the output MMR based on the
@ -586,11 +588,10 @@ impl<'a> Extension<'a> {
let cutoff_header = self.commit_index.get_header_by_height(cutoff_height)?;
let cutoff_pos = cutoff_header.output_mmr_size;
// If any output pos exceeed the cutoff_pos
// we know they have not yet sufficiently matured.
if pos > cutoff_pos {
return Err(Error::ImmatureCoinbase);
return Err(ErrorKind::ImmatureCoinbase.into());
}
}
@ -641,7 +642,9 @@ impl<'a> Extension<'a> {
.expect("no output at pos")
.hash_with_index(pos - 1);
if output_id_hash != read_hash || output_id_hash != read_elem_hash {
return Err(Error::TxHashSetErr(format!("output pmmr hash mismatch")));
return Err(
ErrorKind::TxHashSetErr(format!("output pmmr hash mismatch")).into(),
);
}
}
@ -652,13 +655,13 @@ impl<'a> Extension<'a> {
Ok(true) => {
self.rproof_pmmr
.prune(pos)
.map_err(|s| Error::TxHashSetErr(s))?;
.map_err(|s| ErrorKind::TxHashSetErr(s))?;
}
Ok(false) => return Err(Error::AlreadySpent(commit)),
Err(s) => return Err(Error::TxHashSetErr(s)),
Ok(false) => return Err(ErrorKind::AlreadySpent(commit).into()),
Err(s) => return Err(ErrorKind::TxHashSetErr(s).into()),
}
} else {
return Err(Error::AlreadySpent(commit));
return Err(ErrorKind::AlreadySpent(commit).into());
}
Ok(())
}
@ -677,21 +680,21 @@ impl<'a> Extension<'a> {
// We may be on a fork which may result in the entry at that pos being
// different to the one we expect.
if hash == OutputIdentifier::from_output(out).hash_with_index(pos - 1) {
return Err(Error::DuplicateCommitment(commit));
return Err(ErrorKind::DuplicateCommitment(commit).into());
}
}
}
// push new outputs in their MMR and save them in the index
let pos = self.output_pmmr
.push(OutputIdentifier::from_output(out))
.map_err(&Error::TxHashSetErr)?;
.map_err(&ErrorKind::TxHashSetErr)?;
self.batch.save_output_pos(&out.commitment(), pos)?;
self.new_output_commits.insert(out.commitment(), pos);
// push range proofs in their MMR and file
self.rproof_pmmr
.push(out.proof)
.map_err(&Error::TxHashSetErr)?;
.map_err(&ErrorKind::TxHashSetErr)?;
Ok(())
}
@ -699,7 +702,7 @@ impl<'a> Extension<'a> {
// push kernels in their MMR and file
self.kernel_pmmr
.push(kernel.clone())
.map_err(&Error::TxHashSetErr)?;
.map_err(&ErrorKind::TxHashSetErr)?;
Ok(())
}
@ -729,7 +732,7 @@ impl<'a> Extension<'a> {
let pos = self.batch.get_output_pos(&output.commit)?;
let merkle_proof = self.output_pmmr
.merkle_proof(pos)
.map_err(&Error::TxHashSetErr)?;
.map_err(&ErrorKind::TxHashSetErr)?;
Ok(merkle_proof)
}
@ -742,10 +745,10 @@ impl<'a> Extension<'a> {
pub fn snapshot(&mut self, header: &BlockHeader) -> Result<(), Error> {
self.output_pmmr
.snapshot(header)
.map_err(|e| Error::Other(e))?;
.map_err(|e| ErrorKind::Other(e))?;
self.rproof_pmmr
.snapshot(header)
.map_err(|e| Error::Other(e))?;
.map_err(|e| ErrorKind::Other(e))?;
Ok(())
}
@ -820,17 +823,17 @@ impl<'a> Extension<'a> {
if rewind_utxo {
self.output_pmmr
.rewind(output_pos, rewind_add_pos, rewind_rm_pos)
.map_err(&Error::TxHashSetErr)?;
.map_err(&ErrorKind::TxHashSetErr)?;
}
if rewind_rproof {
self.rproof_pmmr
.rewind(output_pos, rewind_add_pos, rewind_rm_pos)
.map_err(&Error::TxHashSetErr)?;
.map_err(&ErrorKind::TxHashSetErr)?;
}
if rewind_kernel {
self.kernel_pmmr
.rewind(kernel_pos, rewind_add_pos, rewind_rm_pos)
.map_err(&Error::TxHashSetErr)?;
.map_err(&ErrorKind::TxHashSetErr)?;
}
Ok(())
@ -863,10 +866,11 @@ impl<'a> Extension<'a> {
}
let roots = self.roots();
if roots.output_root != header.output_root || roots.rproof_root != header.range_proof_root
if roots.output_root != header.output_root
|| roots.rproof_root != header.range_proof_root
|| roots.kernel_root != header.kernel_root
{
return Err(Error::InvalidRoot);
return Err(ErrorKind::InvalidRoot.into());
}
Ok(())
}
@ -876,13 +880,13 @@ impl<'a> Extension<'a> {
// validate all hashes and sums within the trees
if let Err(e) = self.output_pmmr.validate() {
return Err(Error::InvalidTxHashSet(e));
return Err(ErrorKind::InvalidTxHashSet(e).into());
}
if let Err(e) = self.rproof_pmmr.validate() {
return Err(Error::InvalidTxHashSet(e));
return Err(ErrorKind::InvalidTxHashSet(e).into());
}
if let Err(e) = self.kernel_pmmr.validate() {
return Err(Error::InvalidTxHashSet(e));
return Err(ErrorKind::InvalidTxHashSet(e).into());
}
debug!(
@ -1017,7 +1021,7 @@ impl<'a> Extension<'a> {
out.into_output(rp).verify_proof()?;
} else {
// TODO - rangeproof not found
return Err(Error::OutputNotFound);
return Err(ErrorKind::OutputNotFound.into());
}
proof_count += 1;
@ -1056,10 +1060,10 @@ impl<'a> Extension<'a> {
// rewinding further and further back
self.rewind(&current, &head_header, false, true, false)?;
if self.kernel_pmmr.root() != current.kernel_root {
return Err(Error::InvalidTxHashSet(format!(
return Err(ErrorKind::InvalidTxHashSet(format!(
"Kernel root at {} does not match",
current.height
)));
)).into());
}
}
Ok(())
@ -1075,7 +1079,7 @@ pub fn zip_read(root_dir: String) -> Result<File, Error> {
// create the zip archive
{
zip::compress(&txhashset_path, &File::create(zip_path.clone())?)
.map_err(|ze| Error::Other(ze.to_string()))?;
.map_err(|ze| ErrorKind::Other(ze.to_string()))?;
}
// open it again to read it back
@ -1089,7 +1093,8 @@ pub fn zip_write(root_dir: String, txhashset_data: File) -> Result<(), Error> {
let txhashset_path = Path::new(&root_dir).join(TXHASHSET_SUBDIR);
fs::create_dir_all(txhashset_path.clone())?;
zip::decompress(txhashset_data, &txhashset_path).map_err(|ze| Error::Other(ze.to_string()))
zip::decompress(txhashset_data, &txhashset_path)
.map_err(|ze| ErrorKind::Other(ze.to_string()).into())
}
/// Given a block header to rewind to and the block header at the

View file

@ -14,18 +14,10 @@
//! Base types that the block chain pipeline requires.
use std::{error, fmt, io};
use util::secp;
use util::secp::pedersen::Commitment;
use core::core::committed;
use core::core::hash::{Hash, Hashed};
use core::core::target::Difficulty;
use core::core::{block, transaction, Block, BlockHeader};
use core::core::{Block, BlockHeader};
use core::ser;
use grin_store as store;
use keychain;
bitflags! {
/// Options for block validation
@ -53,146 +45,6 @@ pub struct TxHashSetRoots {
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,
/// Block header sizeshift is lower than our min
LowSizeshift,
/// 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,
/// One of the MMR sizes in the block header is invalid
InvalidMMRSize,
/// 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),
/// Attempt to spend a coinbase output before it sufficiently matures.
ImmatureCoinbase,
/// Error validating a Merkle proof (coinbase output)
MerkleProof,
/// 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(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),
/// Tx not valid based on lock_height.
TxLockHeight,
/// No chain exists and genesis block is required
GenesisBlockRequired,
/// Error from underlying tx handling
Transaction(transaction::Error),
/// Anything else
Other(String),
/// Error from summing and verifying kernel sums via committed trait.
Committed(committed::Error),
}
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<store::Error> for Error {
fn from(e: 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 From<committed::Error> for Error {
fn from(e: committed::Error) -> Error {
Error::Committed(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

View file

@ -24,7 +24,8 @@ extern crate time;
use std::fs;
use std::sync::Arc;
use chain::types::{Error, NoopAdapter};
use chain::types::NoopAdapter;
use chain::{Error, ErrorKind};
use core::core::target::Difficulty;
use core::core::{transaction, OutputIdentifier};
use core::global::{self, ChainTypes};
@ -126,8 +127,11 @@ fn test_coinbase_maturity() {
// Confirm the tx attempting to spend the coinbase output
// is not valid at the current block height given the current chain state.
match chain.verify_coinbase_maturity(&coinbase_txn) {
Err(Error::ImmatureCoinbase) => {}
_ => panic!("Expected transaction error with immature coinbase."),
Ok(_) => {}
Err(e) => match e.kind() {
ErrorKind::ImmatureCoinbase => {}
_ => panic!("Expected transaction error with immature coinbase."),
},
}
pow::pow_size(

View file

@ -10,6 +10,8 @@ bitflags = "1"
blake2-rfc = "0.2"
byteorder = "1"
croaring = "0.3"
failure = "0.1"
failure_derive = "0.1"
lazy_static = "1"
num-bigint = "0.2"
rand = "0.3"

View file

@ -103,7 +103,8 @@ pub const MAX_TX_KERNELS: u64 = 2048;
/// Whether a block exceeds the maximum acceptable weight
pub fn exceeds_weight(input_len: usize, output_len: usize, kernel_len: usize) -> bool {
input_len * BLOCK_INPUT_WEIGHT + output_len * BLOCK_OUTPUT_WEIGHT
input_len * BLOCK_INPUT_WEIGHT
+ output_len * BLOCK_OUTPUT_WEIGHT
+ kernel_len * BLOCK_KERNEL_WEIGHT > MAX_BLOCK_WEIGHT || input_len > MAX_BLOCK_INPUTS
}
@ -159,14 +160,20 @@ pub const DAMP_FACTOR: u64 = 3;
pub const INITIAL_DIFFICULTY: u64 = 1_000_000;
/// Consensus errors
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, Fail)]
pub enum Error {
/// Inputs/outputs/kernels must be sorted lexicographically.
SortError,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Sort Error")
}
}
/// Error when computing the next difficulty adjustment.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Fail)]
pub struct TargetError(pub String);
impl fmt::Display for TargetError {

View file

@ -16,6 +16,7 @@
use rand::{thread_rng, Rng};
use std::collections::HashSet;
use std::fmt;
use std::iter::FromIterator;
use time;
@ -24,15 +25,17 @@ use core::committed::{self, Committed};
use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH};
use core::id::ShortIdentifiable;
use core::target::Difficulty;
use core::{transaction, Commitment, Input, KernelFeatures, Output, OutputFeatures, Proof, ShortId,
Transaction, TxKernel};
use core::{
transaction, Commitment, Input, KernelFeatures, Output, OutputFeatures, Proof, ShortId,
Transaction, TxKernel,
};
use global;
use keychain::{self, BlindingFactor};
use ser::{self, read_and_verify_sorted, Readable, Reader, Writeable, WriteableSorted, Writer};
use util::{secp, secp_static, static_secp_instance, LOGGER};
/// Errors thrown by Block validation
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Fail)]
pub enum Error {
/// The sum of output minus input commitments does not
/// match the sum of kernel commitments
@ -95,6 +98,12 @@ impl From<consensus::Error> for Error {
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Block Error (display needs implementation")
}
}
/// Block header, fairly standard compared to other blockchains.
#[derive(Clone, Debug, PartialEq)]
pub struct BlockHeader {

View file

@ -14,8 +14,8 @@
//! short ids for compact blocks
use std::cmp::Ordering;
use std::cmp::min;
use std::cmp::Ordering;
use byteorder::{ByteOrder, LittleEndian};
use siphasher::sip::SipHasher24;

View file

@ -39,9 +39,9 @@ use std::marker;
use croaring::Bitmap;
use core::BlockHeader;
use core::hash::Hash;
use core::merkle_proof::MerkleProof;
use core::BlockHeader;
use ser::{PMMRIndexHashable, PMMRable};
use util::LOGGER;

View file

@ -14,8 +14,8 @@
//! Transactions
use std::cmp::Ordering;
use std::cmp::max;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::{error, fmt};
@ -27,8 +27,9 @@ use consensus::{self, VerifySortOrder};
use core::hash::Hashed;
use core::{committed, Committed};
use keychain::{self, BlindingFactor};
use ser::{self, read_and_verify_sorted, PMMRable, Readable, Reader, Writeable,
WriteableSorted, Writer};
use ser::{
self, read_and_verify_sorted, PMMRable, Readable, Reader, Writeable, WriteableSorted, Writer,
};
use util;
bitflags! {
@ -250,7 +251,9 @@ pub struct Transaction {
/// PartialEq
impl PartialEq for Transaction {
fn eq(&self, tx: &Transaction) -> bool {
self.inputs == tx.inputs && self.outputs == tx.outputs && self.kernels == tx.kernels
self.inputs == tx.inputs
&& self.outputs == tx.outputs
&& self.kernels == tx.kernels
&& self.offset == tx.offset
}
}
@ -290,7 +293,8 @@ impl Readable for Transaction {
let (input_len, output_len, kernel_len) =
ser_multiread!(reader, read_u64, read_u64, read_u64);
if input_len > consensus::MAX_TX_INPUTS || output_len > consensus::MAX_TX_OUTPUTS
if input_len > consensus::MAX_TX_INPUTS
|| output_len > consensus::MAX_TX_OUTPUTS
|| kernel_len > consensus::MAX_TX_KERNELS
{
return Err(ser::Error::CorruptedData);
@ -436,16 +440,12 @@ impl Transaction {
/// Calculate transaction weight
pub fn tx_weight(&self) -> u32 {
Transaction::weight(
self.inputs.len(),
self.outputs.len(),
)
Transaction::weight(self.inputs.len(), self.outputs.len())
}
/// Calculate transaction weight from transaction details
pub fn weight(input_len: usize, output_len: usize) -> u32 {
let mut tx_weight =
-1 * (input_len as i32) + (4 * output_len as i32) + 1;
let mut tx_weight = -1 * (input_len as i32) + (4 * output_len as i32) + 1;
if tx_weight < 1 {
tx_weight = 1;
}
@ -669,14 +669,8 @@ impl Readable for Input {
impl Input {
/// Build a new input from the data required to identify and verify an
/// output being spent.
pub fn new(
features: OutputFeatures,
commit: Commitment,
) -> Input {
Input {
features,
commit,
}
pub fn new(features: OutputFeatures, commit: Commitment) -> Input {
Input { features, commit }
}
/// The input commitment which _partially_ identifies the output being

View file

@ -17,9 +17,11 @@
//! should be used sparingly.
use consensus::TargetError;
use consensus::{BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON, DEFAULT_MIN_SIZESHIFT,
DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, MEDIAN_TIME_WINDOW, PROOFSIZE,
REFERENCE_SIZESHIFT};
use consensus::{
BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON, DEFAULT_MIN_SIZESHIFT,
DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, MEDIAN_TIME_WINDOW, PROOFSIZE,
REFERENCE_SIZESHIFT,
};
use core::target::Difficulty;
/// An enum collecting sets of parameters used throughout the
/// code wherever mining is needed. This should allow for
@ -184,7 +186,8 @@ pub fn is_user_testing_mode() -> bool {
/// Are we in production mode (a live public network)?
pub fn is_production_mode() -> bool {
let param_ref = CHAIN_TYPE.read().unwrap();
ChainTypes::Testnet1 == *param_ref || ChainTypes::Testnet2 == *param_ref
ChainTypes::Testnet1 == *param_ref
|| ChainTypes::Testnet2 == *param_ref
|| ChainTypes::Mainnet == *param_ref
}

View file

@ -38,7 +38,10 @@ extern crate serde_derive;
extern crate siphasher;
#[macro_use]
extern crate slog;
extern crate failure;
extern crate time;
#[macro_use]
extern crate failure_derive;
#[macro_use]
pub mod macros;

View file

@ -171,7 +171,13 @@ impl Miner {
let size = 1 << sizeshift;
let graph = vec![0; size + 1];
let easiness = (ease as u64) * (size as u64) / 100;
Miner{easiness, cuckoo, graph, proof_size, sizeshift}
Miner {
easiness,
cuckoo,
graph,
proof_size,
sizeshift,
}
}
/// Searches for a solution
@ -298,8 +304,13 @@ impl Miner {
/// Utility to transform a 8 bytes of a byte array into a u64.
fn u8_to_u64(p: &[u8], i: usize) -> u64 {
(p[i] as u64) | (p[i + 1] as u64) << 8 | (p[i + 2] as u64) << 16 | (p[i + 3] as u64) << 24
| (p[i + 4] as u64) << 32 | (p[i + 5] as u64) << 40 | (p[i + 6] as u64) << 48
(p[i] as u64)
| (p[i + 1] as u64) << 8
| (p[i + 2] as u64) << 16
| (p[i + 3] as u64) << 24
| (p[i + 4] as u64) << 32
| (p[i + 5] as u64) << 40
| (p[i + 6] as u64) << 48
| (p[i + 7] as u64) << 56
}
@ -400,7 +411,9 @@ mod test {
#[test]
fn validate_fail() {
// edge checks
assert!(!Cuckoo::from_hash(blake2(&[49]).as_bytes(), 20).verify(&Proof::new(vec![0; 42]), 75));
assert!(
!Cuckoo::from_hash(blake2(&[49]).as_bytes(), 20).verify(&Proof::new(vec![0; 42]), 75)
);
assert!(!Cuckoo::from_hash(blake2(&[49]).as_bytes(), 20)
.verify(&Proof::new(vec![0xffff; 42]), 75));
// wrong data for proof

View file

@ -25,16 +25,17 @@ use core::hash::{Hash, Hashed};
use keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE};
use std::io::{self, Read, Write};
use std::{cmp, error, fmt, mem};
use util::secp::Signature;
use util::secp::constants::{AGG_SIGNATURE_SIZE, MAX_PROOF_SIZE, PEDERSEN_COMMITMENT_SIZE,
SECRET_KEY_SIZE};
use util::secp::constants::{
AGG_SIGNATURE_SIZE, MAX_PROOF_SIZE, PEDERSEN_COMMITMENT_SIZE, SECRET_KEY_SIZE,
};
use util::secp::pedersen::{Commitment, RangeProof};
use util::secp::Signature;
/// Possible errors deriving from serializing or deserializing.
#[derive(Debug)]
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Error {
/// Wraps an io error produced when reading or writing
IOErr(io::Error),
IOErr(String, io::ErrorKind),
/// Expected a given value that wasn't found
UnexpectedData {
/// What we wanted
@ -54,7 +55,7 @@ pub enum Error {
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::IOErr(e)
Error::IOErr(format!("{}", e), e.kind())
}
}
@ -67,7 +68,7 @@ impl From<consensus::Error> for Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::IOErr(ref e) => write!(f, "{}", e),
Error::IOErr(ref e, ref _k) => write!(f, "{}", e),
Error::UnexpectedData {
expected: ref e,
received: ref r,
@ -83,14 +84,14 @@ impl fmt::Display for Error {
impl error::Error for Error {
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::IOErr(ref e) => Some(e),
Error::IOErr(ref _e, ref _k) => Some(self),
_ => None,
}
}
fn description(&self) -> &str {
match *self {
Error::IOErr(ref e) => error::Error::description(e),
Error::IOErr(ref e, _) => e,
Error::UnexpectedData {
expected: _,
received: _,
@ -265,26 +266,30 @@ struct BinReader<'a> {
source: &'a mut Read,
}
fn map_io_err(err: io::Error) -> Error {
Error::IOErr(format!("{}", err), err.kind())
}
/// Utility wrapper for an underlying byte Reader. Defines higher level methods
/// to read numbers, byte vectors, hashes, etc.
impl<'a> Reader for BinReader<'a> {
fn read_u8(&mut self) -> Result<u8, Error> {
self.source.read_u8().map_err(Error::IOErr)
self.source.read_u8().map_err(map_io_err)
}
fn read_u16(&mut self) -> Result<u16, Error> {
self.source.read_u16::<BigEndian>().map_err(Error::IOErr)
self.source.read_u16::<BigEndian>().map_err(map_io_err)
}
fn read_u32(&mut self) -> Result<u32, Error> {
self.source.read_u32::<BigEndian>().map_err(Error::IOErr)
self.source.read_u32::<BigEndian>().map_err(map_io_err)
}
fn read_i32(&mut self) -> Result<i32, Error> {
self.source.read_i32::<BigEndian>().map_err(Error::IOErr)
self.source.read_i32::<BigEndian>().map_err(map_io_err)
}
fn read_u64(&mut self) -> Result<u64, Error> {
self.source.read_u64::<BigEndian>().map_err(Error::IOErr)
self.source.read_u64::<BigEndian>().map_err(map_io_err)
}
fn read_i64(&mut self) -> Result<i64, Error> {
self.source.read_i64::<BigEndian>().map_err(Error::IOErr)
self.source.read_i64::<BigEndian>().map_err(map_io_err)
}
/// Read a variable size vector from the underlying Read. Expects a usize
fn read_vec(&mut self) -> Result<Vec<u8>, Error> {
@ -306,7 +311,7 @@ impl<'a> Reader for BinReader<'a> {
self.source
.read_exact(&mut buf)
.map(move |_| buf)
.map_err(Error::IOErr)
.map_err(map_io_err)
}
fn expect_u8(&mut self, val: u8) -> Result<u8, Error> {
@ -459,7 +464,7 @@ where
let elem = T::read(reader);
match elem {
Ok(e) => buf.push(e),
Err(Error::IOErr(ref ioerr)) if ioerr.kind() == io::ErrorKind::UnexpectedEof => {
Err(Error::IOErr(ref _d, ref kind)) if *kind == io::ErrorKind::UnexpectedEof => {
break
}
Err(e) => return Err(e),

View file

@ -118,7 +118,10 @@ impl PeerStore {
}
pub fn get_peer(&self, peer_addr: SocketAddr) -> Result<PeerData, Error> {
option_to_not_found(self.db.get_ser(&peer_key(peer_addr)[..]))
option_to_not_found(
self.db.get_ser(&peer_key(peer_addr)[..]),
&format!("Peer at address: {}", peer_addr),
)
}
pub fn exists_peer(&self, peer_addr: SocketAddr) -> Result<bool, Error> {

View file

@ -216,28 +216,32 @@ impl p2p::ChainAdapter for NetToChainAdapter {
Ok(_) => {
added_hs.push(bh.hash());
}
Err(chain::Error::Unfit(s)) => {
info!(
LOGGER,
"Received unfit block header {} at {}: {}.",
bh.hash(),
bh.height,
s
);
}
Err(chain::Error::StoreErr(e, explanation)) => {
error!(
LOGGER,
"Store error processing block header {}: in {} {:?}",
bh.hash(),
explanation,
e
);
return;
}
Err(e) => {
info!(LOGGER, "Invalid block header {}: {:?}.", bh.hash(), e);
// TODO penalize peer somehow
match e.kind() {
chain::ErrorKind::Unfit(s) => {
info!(
LOGGER,
"Received unfit block header {} at {}: {}.",
bh.hash(),
bh.height,
s
);
}
chain::ErrorKind::StoreErr(e, explanation) => {
error!(
LOGGER,
"Store error processing block header {}: in {} {:?}",
bh.hash(),
explanation,
e
);
return;
}
_ => {
info!(LOGGER, "Invalid block header {}: {:?}.", bh.hash(), e);
// TODO penalize peer somehow
}
}
}
}
}
@ -269,11 +273,13 @@ impl p2p::ChainAdapter for NetToChainAdapter {
let header = w(&self.chain).get_header_by_height(h);
match header {
Ok(head) => headers.push(head),
Err(chain::Error::StoreErr(store::Error::NotFoundErr, _)) => break,
Err(e) => {
error!(LOGGER, "Could not build header locator: {:?}", e);
return vec![];
}
Err(e) => match e.kind() {
chain::ErrorKind::StoreErr(store::Error::NotFoundErr(_), _) => break,
_ => {
error!(LOGGER, "Could not build header locator: {:?}", e);
return vec![];
}
},
}
}
@ -331,7 +337,7 @@ impl p2p::ChainAdapter for NetToChainAdapter {
if let Err(e) =
w(&self.chain).txhashset_write(h, rewind_to_output, rewind_to_kernel, txhashset_data)
{
error!(LOGGER, "Failed to save txhashset archive: {:?}", e);
error!(LOGGER, "Failed to save txhashset archive: {}", e);
!e.is_bad_data()
} else {
info!(LOGGER, "Received valid txhashset data for {}.", h);
@ -391,13 +397,15 @@ impl NetToChainAdapter {
self.find_common_header(locator[1..].to_vec())
}
}
Err(chain::Error::StoreErr(store::Error::NotFoundErr, _)) => {
self.find_common_header(locator[1..].to_vec())
}
Err(e) => {
error!(LOGGER, "Could not build header locator: {:?}", e);
None
}
Err(e) => match e.kind() {
chain::ErrorKind::StoreErr(store::Error::NotFoundErr(_), _) => {
self.find_common_header(locator[1..].to_vec())
}
_ => {
error!(LOGGER, "Could not build header locator: {:?}", e);
None
}
},
}
}
@ -413,14 +421,6 @@ impl NetToChainAdapter {
self.check_compact(tip);
true
}
Err(chain::Error::Orphan) => {
// make sure we did not miss the parent block
if !chain.is_orphan(&prev_hash) && !self.currently_syncing.load(Ordering::Relaxed) {
debug!(LOGGER, "adapter: process_block: received an orphan block, checking the parent: {:}", prev_hash);
self.request_block_by_hash(prev_hash, &addr)
}
true
}
Err(ref e) if e.is_bad_data() => {
debug!(
LOGGER,
@ -435,11 +435,25 @@ impl NetToChainAdapter {
false
}
Err(e) => {
debug!(
LOGGER,
"adapter: process_block: block {} refused by chain: {:?}", bhash, e
);
true
match e.kind() {
chain::ErrorKind::Orphan => {
// make sure we did not miss the parent block
if !chain.is_orphan(&prev_hash)
&& !self.currently_syncing.load(Ordering::Relaxed)
{
debug!(LOGGER, "adapter: process_block: received an orphan block, checking the parent: {:}", prev_hash);
self.request_block_by_hash(prev_hash, &addr)
}
true
}
_ => {
debug!(
LOGGER,
"adapter: process_block: block {} refused by chain: {:?}", bhash, e
);
true
}
}
}
}
}
@ -451,7 +465,8 @@ impl NetToChainAdapter {
// down as soon as possible.
// Skip this if we are currently syncing (too slow).
let chain = w(&self.chain);
if chain.head().unwrap().height > 0 && !self.currently_syncing.load(Ordering::Relaxed)
if chain.head().unwrap().height > 0
&& !self.currently_syncing.load(Ordering::Relaxed)
&& self.config.chain_validation_mode == ChainValidationMode::EveryBlock
{
let now = Instant::now();

View file

@ -92,12 +92,17 @@ pub fn get_block(
);
while let Err(e) = result {
match e {
self::Error::Chain(chain::Error::DuplicateCommitment(_)) => {
debug!(
LOGGER,
"Duplicate commit for potential coinbase detected. Trying next derivation."
);
}
self::Error::Chain(c) => match c.kind() {
chain::ErrorKind::DuplicateCommitment(_) => {
debug!(
LOGGER,
"Duplicate commit for potential coinbase detected. Trying next derivation."
);
}
_ => {
error!(LOGGER, "Chain Error: {}", c);
}
},
self::Error::Wallet(_) => {
error!(
LOGGER,
@ -187,17 +192,23 @@ fn build_block(
// If it's a duplicate commitment, it's likely trying to use
// a key that's already been derived but not in the wallet
// for some reason, allow caller to retry
Err(chain::Error::DuplicateCommitment(e)) => {
Err(Error::Chain(chain::Error::DuplicateCommitment(e)))
}
//Some other issue, possibly duplicate kernel
Err(e) => {
error!(
LOGGER,
"Error setting txhashset root to build a block: {:?}", e
);
Err(Error::Chain(chain::Error::Other(format!("{:?}", e))))
match e.kind() {
chain::ErrorKind::DuplicateCommitment(e) => Err(Error::Chain(
chain::ErrorKind::DuplicateCommitment(e).into(),
)),
//Some other issue, possibly duplicate kernel
_ => {
error!(
LOGGER,
"Error setting txhashset root to build a block: {:?}", e
);
Err(Error::Chain(
chain::ErrorKind::Other(format!("{:?}", e)).into(),
))
}
}
}
}
}

View file

@ -10,6 +10,8 @@ byteorder = "1"
croaring = "0.3"
env_logger = "0.5"
libc = "0.2"
failure = "0.1"
failure_derive = "0.1"
lmdb-zero = "0.4.4"
memmap = { git = "https://github.com/danburkert/memmap-rs", tag = "0.6.2" }
serde = "1"

View file

@ -21,9 +21,9 @@ use std::path::Path;
use croaring::Bitmap;
use core::core::BlockHeader;
use core::core::hash::Hashed;
use core::core::pmmr;
use core::core::BlockHeader;
use prune_list::PruneList;
use util::LOGGER;

View file

@ -29,6 +29,9 @@ extern crate memmap;
extern crate serde;
#[macro_use]
extern crate slog;
extern crate failure;
#[macro_use]
extern crate failure_derive;
#[macro_use]
extern crate grin_core as core;

View file

@ -19,21 +19,24 @@ use std::marker;
use std::sync::Arc;
use lmdb_zero as lmdb;
use lmdb_zero::LmdbResultExt;
use lmdb_zero::traits::CreateCursor;
use lmdb_zero::LmdbResultExt;
use core::ser;
/// Main error type for this lmdb
#[derive(Debug)]
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum Error {
/// Couldn't find what we were looking for
NotFoundErr,
#[fail(display = "DB Not Found Error: {}", _0)]
NotFoundErr(String),
/// Wraps an error originating from RocksDB (which unfortunately returns
/// string errors).
#[fail(display = "LMDB error")]
LmdbErr(lmdb::error::Error),
/// Wraps a serialization error for Writeable or Readable
SerErr(ser::Error),
#[fail(display = "Serialization Error")]
SerErr(String),
}
impl From<lmdb::error::Error> for Error {
@ -43,9 +46,9 @@ impl From<lmdb::error::Error> for Error {
}
/// unwraps the inner option by converting the none case to a not found error
pub fn option_to_not_found<T>(res: Result<Option<T>, Error>) -> Result<T, Error> {
pub fn option_to_not_found<T>(res: Result<Option<T>, Error>, field_name: &str) -> Result<T, Error> {
match res {
Ok(None) => Err(Error::NotFoundErr),
Ok(None) => Err(Error::NotFoundErr(field_name.to_owned())),
Ok(Some(o)) => Ok(o),
Err(e) => Err(e),
}
@ -117,9 +120,9 @@ impl Store {
) -> Result<Option<T>, Error> {
let res: lmdb::error::Result<&[u8]> = access.get(&self.db, key);
match res.to_opt() {
Ok(Some(mut res)) => match ser::deserialize(&mut res).map_err(Error::SerErr) {
Ok(Some(mut res)) => match ser::deserialize(&mut res) {
Ok(res) => Ok(Some(res)),
Err(e) => Err(From::from(e)),
Err(e) => Err(Error::SerErr(format!("{}", e))),
},
Ok(None) => Ok(None),
Err(e) => Err(From::from(e)),
@ -179,7 +182,7 @@ impl<'a> Batch<'a> {
let ser_value = ser::ser_vec(value);
match ser_value {
Ok(data) => self.put(key, data),
Err(err) => Err(Error::SerErr(err)),
Err(err) => Err(Error::SerErr(format!("{}", err))),
}
}

View file

@ -96,7 +96,7 @@ where
fn get(&self, id: &Identifier) -> Result<OutputData, Error> {
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
option_to_not_found(self.db.get_ser(&key)).map_err(|e| e.into())
option_to_not_found(self.db.get_ser(&key), &format!("Key Id: {}", id)).map_err(|e| e.into())
}
fn iter<'a>(&'a self) -> Box<Iterator<Item = OutputData> + 'a> {
@ -166,7 +166,10 @@ impl<'a, K> WalletOutputBatch for Batch<'a, K> {
fn get(&self, id: &Identifier) -> Result<OutputData, Error> {
let key = to_key(OUTPUT_PREFIX, &mut id.to_bytes().to_vec());
option_to_not_found(self.db.borrow().as_ref().unwrap().get_ser(&key)).map_err(|e| e.into())
option_to_not_found(
self.db.borrow().as_ref().unwrap().get_ser(&key),
&format!("Key ID: {}", id),
).map_err(|e| e.into())
}
fn iter<'b>(&'b self) -> Box<Iterator<Item = OutputData> + 'b> {