From 8f826a959d9bd3a6fc4218aa17189e94997de29a Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 18 Jul 2014 12:39:11 -0700 Subject: [PATCH] Introduce `BitcoinResult`, use it instead of boolean returns in blockchain --- .gitignore | 2 ++ src/blockdata/block.rs | 7 ++++--- src/blockdata/blockchain.rs | 28 ++++++++++++------------- src/network/listener.rs | 1 - src/util/error.rs | 41 +++++++++++++++++++++++++++++++++++++ src/util/mod.rs | 1 + 6 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 .gitignore create mode 100644 src/util/error.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d913617 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target + diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs index 7dc8aad..529b3f3 100644 --- a/src/blockdata/block.rs +++ b/src/blockdata/block.rs @@ -23,6 +23,7 @@ use std::io::IoResult; use std::num::{Zero, from_u64}; +use util::error::{BitcoinResult, SpvBadTarget, SpvBadProofOfWork}; use util::hash::Sha256dHash; use util::uint::Uint256; use network::serialize::{Serializable, SerializeIter, VarInt}; @@ -95,13 +96,13 @@ impl BlockHeader { /// Performs an SPV validation of a block, which confirms that the proof-of-work /// is correct, but does not verify that the transactions are valid or encoded /// correctly. - pub fn spv_validate(&self, required_target: &Uint256) -> bool { + pub fn spv_validate(&self, required_target: &Uint256) -> BitcoinResult<()> { let ref target = self.target(); if target != required_target { - return false; + return Err(SpvBadTarget); } let ref hash = self.hash().as_uint256(); - hash <= target + if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) } } /// Returns the total work of the block diff --git a/src/blockdata/blockchain.rs b/src/blockdata/blockchain.rs index ed427d1..3fffbf4 100644 --- a/src/blockdata/blockchain.rs +++ b/src/blockdata/blockchain.rs @@ -33,6 +33,7 @@ use blockdata::transaction::Transaction; use blockdata::constants::{DIFFCHANGE_INTERVAL, DIFFCHANGE_TIMESPAN, max_target}; use network::serialize::{Serializable, SerializeIter}; use util::BitArray; +use util::error::{BitcoinResult, BlockNotFound, DuplicateHash, PrevHashNotFound}; use util::uint::Uint256; use util::hash::Sha256dHash; use util::misc::prepend_err; @@ -389,7 +390,7 @@ impl Blockchain { } } - fn replace_txdata(&mut self, hash: &Uint256, txdata: Vec, has_txdata: bool) -> bool { + fn replace_txdata(&mut self, hash: &Uint256, txdata: Vec, has_txdata: bool) -> BitcoinResult<()> { match self.tree.lookup_mut(hash, 256) { Some(existing_block) => { unsafe { @@ -417,33 +418,33 @@ impl Blockchain { *mutable_bool = has_txdata; forget(mutable_bool); } - return true + Ok(()) }, - None => return false + None => Err(BlockNotFound) } } /// Locates a block in the chain and overwrites its txdata - pub fn add_txdata(&mut self, block: Block) -> bool { + pub fn add_txdata(&mut self, block: Block) -> BitcoinResult<()> { self.replace_txdata(&block.header.hash().as_uint256(), block.txdata, true) } /// Locates a block in the chain and removes its txdata - pub fn remove_txdata(&mut self, hash: Sha256dHash) -> bool { + pub fn remove_txdata(&mut self, hash: Sha256dHash) -> BitcoinResult<()> { self.replace_txdata(&hash.as_uint256(), vec![], false) } /// Adds a block header to the chain - pub fn add_header(&mut self, header: BlockHeader) -> bool { + pub fn add_header(&mut self, header: BlockHeader) -> BitcoinResult<()> { self.real_add_block(Block { header: header, txdata: vec![] }, false) } /// Adds a block to the chain - pub fn add_block(&mut self, block: Block) -> bool { + pub fn add_block(&mut self, block: Block) -> BitcoinResult<()> { self.real_add_block(block, true) } - fn real_add_block(&mut self, block: Block, has_txdata: bool) -> bool { + fn real_add_block(&mut self, block: Block, has_txdata: bool) -> BitcoinResult<()> { // get_prev optimizes the common case where we are extending the best tip fn get_prev<'a>(chain: &'a Blockchain, hash: Sha256dHash) -> Option<&'a Rc> { if hash == chain.best_hash { return Some(&chain.best_tip); } @@ -454,7 +455,7 @@ impl Blockchain { // and this may also happen in case of a reorg. if self.tree.lookup(&block.header.hash().as_uint256(), 256).is_some() { println!("Warning: tried to add block {} twice!", block.header.hash()); - return true; + return Err(DuplicateHash); } // Construct node, if possible let rc_block = match get_prev(self, block.header.prev_blockhash) { @@ -500,15 +501,12 @@ impl Blockchain { ret }, None => { - println!("TODO: couldn't add block"); - return false; + return Err(PrevHashNotFound); } }; // spv validate the block - if !rc_block.block.header.spv_validate(&rc_block.required_difficulty) { - return false; - } + try!(rc_block.block.header.spv_validate(&rc_block.required_difficulty)); // Insert the new block self.tree.insert(&rc_block.block.header.hash().as_uint256(), 256, rc_block.clone()); @@ -516,7 +514,7 @@ impl Blockchain { if rc_block.total_work > self.best_tip.total_work { self.set_best_tip(rc_block); } - return true; + Ok(()) } /// Sets the best tip (not public) diff --git a/src/network/listener.rs b/src/network/listener.rs index 4f87152..d5747e7 100644 --- a/src/network/listener.rs +++ b/src/network/listener.rs @@ -23,7 +23,6 @@ use std::io::timer; use network::message::{NetworkMessage, Verack}; use network::socket::Socket; -use network::constants; /// A message which can be sent on the Bitcoin network pub trait Listener { diff --git a/src/util/error.rs b/src/util/error.rs new file mode 100644 index 0000000..fafb5d2 --- /dev/null +++ b/src/util/error.rs @@ -0,0 +1,41 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Error codes +//! +//! Various utility functions + +use std::io::IoError; + +/// A success/failure return value +pub type BitcoinResult = Result; + +/// A general error code +#[deriving(PartialEq, Eq, Show, Clone)] +pub enum BitcoinError { + /// An I/O error + IoError(IoError), + /// An object was attempted to be added twice + DuplicateHash, + /// Some operation was attempted on a block (or blockheader) that doesn't exist + BlockNotFound, + /// An object was added but it does not link into existing history + PrevHashNotFound, + /// The `target` field of a block header did not match the expected difficulty + SpvBadTarget, + /// The header hash is not below the target + SpvBadProofOfWork +} + + diff --git a/src/util/mod.rs b/src/util/mod.rs index ce4a654..8f3ab16 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -16,6 +16,7 @@ //! //! Functions needed by all parts of the Bitcoin library +pub mod error; pub mod hash; pub mod iter; pub mod misc;