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::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

View File

@ -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<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) {
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<BlockchainNode>> {
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)

View File

@ -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 {

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
pub mod error;
pub mod hash;
pub mod iter;
pub mod misc;