Introduce `BitcoinResult`, use it instead of boolean returns in blockchain

This commit is contained in:
Andrew Poelstra 2014-07-18 12:39:11 -07:00
parent 426e1fc556
commit 8f826a959d
6 changed files with 61 additions and 19 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
target

View File

@ -23,6 +23,7 @@
use std::io::IoResult; use std::io::IoResult;
use std::num::{Zero, from_u64}; use std::num::{Zero, from_u64};
use util::error::{BitcoinResult, SpvBadTarget, SpvBadProofOfWork};
use util::hash::Sha256dHash; use util::hash::Sha256dHash;
use util::uint::Uint256; use util::uint::Uint256;
use network::serialize::{Serializable, SerializeIter, VarInt}; 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 /// 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 /// is correct, but does not verify that the transactions are valid or encoded
/// correctly. /// correctly.
pub fn spv_validate(&self, required_target: &Uint256) -> bool { pub fn spv_validate(&self, required_target: &Uint256) -> BitcoinResult<()> {
let ref target = self.target(); let ref target = self.target();
if target != required_target { if target != required_target {
return false; return Err(SpvBadTarget);
} }
let ref hash = self.hash().as_uint256(); let ref hash = self.hash().as_uint256();
hash <= target if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) }
} }
/// Returns the total work of the block /// Returns the total work of the block

View File

@ -33,6 +33,7 @@ use blockdata::transaction::Transaction;
use blockdata::constants::{DIFFCHANGE_INTERVAL, DIFFCHANGE_TIMESPAN, max_target}; use blockdata::constants::{DIFFCHANGE_INTERVAL, DIFFCHANGE_TIMESPAN, max_target};
use network::serialize::{Serializable, SerializeIter}; use network::serialize::{Serializable, SerializeIter};
use util::BitArray; use util::BitArray;
use util::error::{BitcoinResult, BlockNotFound, DuplicateHash, PrevHashNotFound};
use util::uint::Uint256; use util::uint::Uint256;
use util::hash::Sha256dHash; use util::hash::Sha256dHash;
use util::misc::prepend_err; use util::misc::prepend_err;
@ -389,7 +390,7 @@ impl Blockchain {
} }
} }
fn replace_txdata(&mut self, hash: &Uint256, txdata: Vec<Transaction>, has_txdata: bool) -> bool { fn replace_txdata(&mut self, hash: &Uint256, txdata: Vec<Transaction>, has_txdata: bool) -> BitcoinResult<()> {
match self.tree.lookup_mut(hash, 256) { match self.tree.lookup_mut(hash, 256) {
Some(existing_block) => { Some(existing_block) => {
unsafe { unsafe {
@ -417,33 +418,33 @@ impl Blockchain {
*mutable_bool = has_txdata; *mutable_bool = has_txdata;
forget(mutable_bool); forget(mutable_bool);
} }
return true Ok(())
}, },
None => return false None => Err(BlockNotFound)
} }
} }
/// Locates a block in the chain and overwrites its txdata /// 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) self.replace_txdata(&block.header.hash().as_uint256(), block.txdata, true)
} }
/// Locates a block in the chain and removes its txdata /// 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) self.replace_txdata(&hash.as_uint256(), vec![], false)
} }
/// Adds a block header to the chain /// 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) self.real_add_block(Block { header: header, txdata: vec![] }, false)
} }
/// Adds a block to the chain /// 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) 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 // 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<BlockchainNode>> { fn get_prev<'a>(chain: &'a Blockchain, hash: Sha256dHash) -> Option<&'a Rc<BlockchainNode>> {
if hash == chain.best_hash { return Some(&chain.best_tip); } 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. // and this may also happen in case of a reorg.
if self.tree.lookup(&block.header.hash().as_uint256(), 256).is_some() { if self.tree.lookup(&block.header.hash().as_uint256(), 256).is_some() {
println!("Warning: tried to add block {} twice!", block.header.hash()); println!("Warning: tried to add block {} twice!", block.header.hash());
return true; return Err(DuplicateHash);
} }
// Construct node, if possible // Construct node, if possible
let rc_block = match get_prev(self, block.header.prev_blockhash) { let rc_block = match get_prev(self, block.header.prev_blockhash) {
@ -500,15 +501,12 @@ impl Blockchain {
ret ret
}, },
None => { None => {
println!("TODO: couldn't add block"); return Err(PrevHashNotFound);
return false;
} }
}; };
// spv validate the block // spv validate the block
if !rc_block.block.header.spv_validate(&rc_block.required_difficulty) { try!(rc_block.block.header.spv_validate(&rc_block.required_difficulty));
return false;
}
// Insert the new block // Insert the new block
self.tree.insert(&rc_block.block.header.hash().as_uint256(), 256, rc_block.clone()); 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 { if rc_block.total_work > self.best_tip.total_work {
self.set_best_tip(rc_block); self.set_best_tip(rc_block);
} }
return true; Ok(())
} }
/// Sets the best tip (not public) /// Sets the best tip (not public)

View File

@ -23,7 +23,6 @@ use std::io::timer;
use network::message::{NetworkMessage, Verack}; use network::message::{NetworkMessage, Verack};
use network::socket::Socket; use network::socket::Socket;
use network::constants;
/// A message which can be sent on the Bitcoin network /// A message which can be sent on the Bitcoin network
pub trait Listener { pub trait Listener {

41
src/util/error.rs Normal file
View File

@ -0,0 +1,41 @@
// Rust Bitcoin Library
// Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net>
//
// 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 <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! # Error codes
//!
//! Various utility functions
use std::io::IoError;
/// A success/failure return value
pub type BitcoinResult<T> = Result<T, BitcoinError>;
/// 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
}

View File

@ -16,6 +16,7 @@
//! //!
//! Functions needed by all parts of the Bitcoin library //! Functions needed by all parts of the Bitcoin library
pub mod error;
pub mod hash; pub mod hash;
pub mod iter; pub mod iter;
pub mod misc; pub mod misc;