use num::{FromPrimitive, Zero};
use std::{marker, ptr};
use blockdata::block::{Block, BlockHeader};
use blockdata::transaction::Transaction;
use blockdata::constants::{DIFFCHANGE_INTERVAL, DIFFCHANGE_TIMESPAN,
TARGET_BLOCK_SPACING, max_target, genesis_block};
use network::constants::Network;
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
use network::serialize::{BitcoinHash, SimpleDecoder, SimpleEncoder};
use util::BitArray;
use util;
use util::Error::{BlockNotFound, DuplicateHash, PrevHashNotFound};
use util::uint::Uint256;
use util::hash::Sha256dHash;
use util::patricia_tree::PatriciaTree;
type BlockTree = PatriciaTree<Uint256, Box<BlockchainNode>>;
type NodePtr = *const BlockchainNode;
pub struct BlockchainNode {
pub block: Block,
pub total_work: Uint256,
pub required_difficulty: Uint256,
pub height: u32,
pub has_txdata: bool,
prev: NodePtr,
next: NodePtr
}
impl BlockchainNode {
fn is_on_main_chain(&self, chain: &Blockchain) -> bool {
if self.block.header == unsafe { (*chain.best_tip).block.header } {
true
} else {
unsafe {
let mut scan = self.next;
while !scan.is_null() {
if (*scan).block.header == (*chain.best_tip).block.header {
return true;
}
scan = (*scan).next;
}
}
false
}
}
}
impl<S: SimpleEncoder> ConsensusEncodable<S> for BlockchainNode {
#[inline]
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
try!(self.block.consensus_encode(s));
try!(self.total_work.consensus_encode(s));
try!(self.required_difficulty.consensus_encode(s));
try!(self.height.consensus_encode(s));
try!(self.has_txdata.consensus_encode(s));
Ok(())
}
}
impl<D: SimpleDecoder> ConsensusDecodable<D> for BlockchainNode {
#[inline]
fn consensus_decode(d: &mut D) -> Result<BlockchainNode, D::Error> {
Ok(BlockchainNode {
block: try!(ConsensusDecodable::consensus_decode(d)),
total_work: try!(ConsensusDecodable::consensus_decode(d)),
required_difficulty: try!(ConsensusDecodable::consensus_decode(d)),
height: try!(ConsensusDecodable::consensus_decode(d)),
has_txdata: try!(ConsensusDecodable::consensus_decode(d)),
prev: ptr::null(),
next: ptr::null()
})
}
}
impl BitcoinHash for BlockchainNode {
fn bitcoin_hash(&self) -> Sha256dHash {
self.block.header.bitcoin_hash()
}
}
pub struct Blockchain {
network: Network,
tree: BlockTree,
best_tip: NodePtr,
best_hash: Sha256dHash,
genesis_hash: Sha256dHash
}
impl<S: SimpleEncoder> ConsensusEncodable<S> for Blockchain {
#[inline]
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
try!(self.network.consensus_encode(s));
try!(self.tree.consensus_encode(s));
try!(self.best_hash.consensus_encode(s));
try!(self.genesis_hash.consensus_encode(s));
Ok(())
}
}
impl<D: SimpleDecoder> ConsensusDecodable<D> for Blockchain {
fn consensus_decode(d: &mut D) -> Result<Blockchain, D::Error> {
let network: Network = try!(ConsensusDecodable::consensus_decode(d));
let mut tree: BlockTree = try!(ConsensusDecodable::consensus_decode(d));
let best_hash: Sha256dHash = try!(ConsensusDecodable::consensus_decode(d));
let genesis_hash: Sha256dHash = try!(ConsensusDecodable::consensus_decode(d));
let best = match tree.lookup(&best_hash.into_le(), 256) {
Some(node) => &**node as NodePtr,
None => {
return Err(d.error(format!("best tip {:x} not in tree", best_hash)));
}
};
if tree.lookup(&genesis_hash.into_le(), 256).is_none() {
return Err(d.error(format!("genesis {:x} not in tree", genesis_hash)));
}
let raw_tree = &tree as *const BlockTree;
for node in tree.mut_iter() {
let hash = node.block.header.prev_blockhash.into_le();
let prevptr =
match unsafe { (*raw_tree).lookup(&hash, 256) } {
Some(node) => &**node as NodePtr,
None => ptr::null()
};
node.prev = prevptr;
}
unsafe {
let mut scan = best;
while !(*scan).prev.is_null() {
let prev = (*scan).prev as *mut BlockchainNode;
(*prev).next = scan;
scan = prev as NodePtr;
}
if (*scan).bitcoin_hash() != genesis_hash {
return Err(d.error(format!("no path from tip {:x} to genesis {:x}",
best_hash, genesis_hash)));
}
}
Ok(Blockchain {
network: network,
tree: tree,
best_tip: best,
best_hash: best_hash,
genesis_hash: genesis_hash
})
}
}
struct LocatorHashIter {
index: NodePtr,
count: usize,
skip: usize
}
impl LocatorHashIter {
fn new(init: NodePtr) -> LocatorHashIter {
LocatorHashIter { index: init, count: 0, skip: 1 }
}
}
impl Iterator for LocatorHashIter {
type Item = Sha256dHash;
fn next(&mut self) -> Option<Sha256dHash> {
if self.index.is_null() {
return None;
}
let ret = Some(unsafe { (*self.index).bitcoin_hash() });
self.index = unsafe { (*self.index).prev };
if !self.index.is_null() {
for _ in 1..self.skip {
unsafe {
if (*self.index).prev.is_null() {
break;
}
self.index = (*self.index).prev;
}
}
}
self.count += 1;
if self.count > 10 {
self.skip *= 2;
}
ret
}
}
pub struct BlockIter<'tree> {
index: NodePtr,
marker: marker::PhantomData<&'tree Blockchain>
}
pub struct RevBlockIter<'tree> {
index: NodePtr,
marker: marker::PhantomData<&'tree Blockchain>
}
pub struct RevStaleBlockIter<'tree> {
index: NodePtr,
chain: &'tree Blockchain
}
impl<'tree> Iterator for BlockIter<'tree> {
type Item = &'tree BlockchainNode;
fn next(&mut self) -> Option<&'tree BlockchainNode> {
if self.index.is_null() {
return None;
}
unsafe {
let ret = Some(&*self.index);
self.index = (*self.index).next;
ret
}
}
}
impl<'tree> Iterator for RevBlockIter<'tree> {
type Item = &'tree BlockchainNode;
fn next(&mut self) -> Option<&'tree BlockchainNode> {
if self.index.is_null() {
return None;
}
unsafe {
let ret = Some(&*self.index);
self.index = (*self.index).prev;
ret
}
}
}
impl<'tree> Iterator for RevStaleBlockIter<'tree> {
type Item = &'tree Block;
fn next(&mut self) -> Option<&'tree Block> {
if self.index.is_null() {
return None;
}
unsafe {
let ret = Some(&(*self.index).block);
let next_index = (*self.index).prev;
if !next_index.is_null() &&
(*next_index).next != self.index &&
(&*next_index).is_on_main_chain(self.chain) {
self.index = ptr::null();
} else {
self.index = next_index;
}
ret
}
}
}
fn satoshi_the_precision(n: Uint256) -> Uint256 {
let bits = 8 * ((n.bits() + 7) / 8 - 3);
let mut ret = n >> bits;
if ret.bit(23) {
ret = (ret >> 8) << 8;
}
ret << bits
}
impl Blockchain {
pub fn new(network: Network) -> Blockchain {
let genesis = genesis_block(network);
let genhash = genesis.header.bitcoin_hash();
let new_node = Box::new(BlockchainNode {
total_work: Zero::zero(),
required_difficulty: genesis.header.target(),
block: genesis,
height: 0,
has_txdata: true,
prev: ptr::null(),
next: ptr::null()
});
let raw_ptr = &*new_node as NodePtr;
Blockchain {
network: network,
tree: {
let mut pat = PatriciaTree::new();
pat.insert(&genhash.into_le(), 256, new_node);
pat
},
best_hash: genhash,
genesis_hash: genhash,
best_tip: raw_ptr
}
}
fn replace_txdata(&mut self, hash: &Uint256, txdata: Vec<Transaction>, has_txdata: bool) -> Result<(), util::Error> {
match self.tree.lookup_mut(hash, 256) {
Some(mut existing_block) => {
existing_block.block.txdata.clone_from(&txdata);
existing_block.has_txdata = has_txdata;
Ok(())
},
None => Err(BlockNotFound)
}
}
pub fn get_block(&self, hash: Sha256dHash) -> Option<&BlockchainNode> {
self.tree.lookup(&hash.into_le(), 256).map(|node| &**node)
}
pub fn add_txdata(&mut self, block: Block) -> Result<(), util::Error> {
self.replace_txdata(&block.header.bitcoin_hash().into_le(), block.txdata, true)
}
pub fn remove_txdata(&mut self, hash: Sha256dHash) -> Result<(), util::Error> {
self.replace_txdata(&hash.into_le(), vec![], false)
}
pub fn add_header(&mut self, header: BlockHeader) -> Result<(), util::Error> {
self.real_add_block(Block { header: header, txdata: vec![] }, false)
}
pub fn add_block(&mut self, block: Block) -> Result<(), util::Error> {
self.real_add_block(block, true)
}
fn real_add_block(&mut self, block: Block, has_txdata: bool) -> Result<(), util::Error> {
#[inline]
fn get_prev(chain: &Blockchain, hash: Sha256dHash) -> Option<NodePtr> {
if hash == chain.best_hash {
Some(chain.best_tip)
} else {
chain.tree.lookup(&hash.into_le(), 256).map(|boxptr| &**boxptr as NodePtr)
}
}
if self.tree.lookup(&block.header.bitcoin_hash().into_le(), 256).is_some() {
return Err(DuplicateHash);
}
let new_block = match get_prev(self, block.header.prev_blockhash) {
Some(prev) => {
let difficulty =
if (unsafe { (*prev).height } + 1) % DIFFCHANGE_INTERVAL == 0 {
let timespan = unsafe {
let mut scan = prev;
for _ in 0..(DIFFCHANGE_INTERVAL - 1) {
scan = (*scan).prev;
}
match (*prev).block.header.time - (*scan).block.header.time {
n if n < DIFFCHANGE_TIMESPAN / 4 => DIFFCHANGE_TIMESPAN / 4,
n if n > DIFFCHANGE_TIMESPAN * 4 => DIFFCHANGE_TIMESPAN * 4,
n => n
}
};
let mut target = unsafe { (*prev).block.header.target() };
target = target.mul_u32(timespan);
target = target / FromPrimitive::from_u64(DIFFCHANGE_TIMESPAN as u64).unwrap();
let max = max_target(self.network);
if target > max { target = max };
satoshi_the_precision(target)
} else if self.network == Network::Testnet &&
block.header.time > unsafe { (*prev).block.header.time } + 2*TARGET_BLOCK_SPACING {
max_target(self.network)
} else if self.network == Network::Testnet {
unsafe {
let mut scan = prev;
while (*scan).height % DIFFCHANGE_INTERVAL != 0 &&
(*scan).required_difficulty == max_target(self.network) {
scan = (*scan).prev;
}
(*scan).required_difficulty
}
} else {
unsafe { (*prev).required_difficulty }
};
let ret = Box::new(BlockchainNode {
total_work: block.header.work() + unsafe { (*prev).total_work },
block: block,
required_difficulty: difficulty,
height: unsafe { (*prev).height + 1 },
has_txdata: has_txdata,
prev: prev,
next: ptr::null()
});
unsafe {
let prev = prev as *mut BlockchainNode;
(*prev).next = &*ret as NodePtr;
}
ret
},
None => {
return Err(PrevHashNotFound);
}
};
try!(new_block.block.header.spv_validate(&new_block.required_difficulty));
let raw_ptr = &*new_block as NodePtr;
self.tree.insert(&new_block.block.header.bitcoin_hash().into_le(), 256, new_block);
if unsafe { (*raw_ptr).total_work > (*self.best_tip).total_work } {
self.set_best_tip(raw_ptr);
}
Ok(())
}
fn set_best_tip(&mut self, tip: NodePtr) {
unsafe {
let mut scan = self.best_tip;
while !(*scan).prev.is_null() {
if scan == self.best_tip { break; }
let prev = (*scan).prev as *mut BlockchainNode;
(*prev).next = scan;
scan = (*scan).prev;
}
}
self.best_hash = unsafe { (*tip).bitcoin_hash() };
self.best_tip = tip;
}
pub fn genesis_hash(&self) -> Sha256dHash {
self.genesis_hash
}
pub fn best_tip(&self) -> &Block {
unsafe { &(*self.best_tip).block }
}
pub fn best_tip_hash(&self) -> Sha256dHash {
self.best_hash
}
pub fn locator_hashes(&self) -> Vec<Sha256dHash> {
LocatorHashIter::new(self.best_tip).collect()
}
pub fn iter(&self, start_hash: Sha256dHash) -> BlockIter {
let start = match self.tree.lookup(&start_hash.into_le(), 256) {
Some(boxptr) => &**boxptr as NodePtr,
None => ptr::null()
};
BlockIter {
index: start,
marker: marker::PhantomData
}
}
pub fn rev_iter(&self, start_hash: Sha256dHash) -> RevBlockIter {
let start = match self.tree.lookup(&start_hash.into_le(), 256) {
Some(boxptr) => &**boxptr as NodePtr,
None => ptr::null()
};
RevBlockIter {
index: start,
marker: marker::PhantomData
}
}
pub fn rev_stale_iter(&self, start_hash: Sha256dHash) -> RevStaleBlockIter {
let start = match self.tree.lookup(&start_hash.into_le(), 256) {
Some(boxptr) => {
if boxptr.is_on_main_chain(self) {
ptr::null()
} else {
&**boxptr as NodePtr
}
}
None => ptr::null()
};
RevStaleBlockIter {
index: start,
chain: self
}
}
}
#[cfg(test)]
mod tests {
use blockdata::blockchain::Blockchain;
use blockdata::constants::genesis_block;
use network::constants::Network::Bitcoin;
use network::serialize::{BitcoinHash, deserialize, serialize};
#[test]
fn blockchain_serialize_test() {
let empty_chain = Blockchain::new(Bitcoin);
assert_eq!(empty_chain.best_tip().header.bitcoin_hash(),
genesis_block(Bitcoin).header.bitcoin_hash());
let serial = serialize(&empty_chain);
let deserial: Result<Blockchain, _> = deserialize(&serial.unwrap());
assert!(deserial.is_ok());
let read_chain = deserial.unwrap();
assert_eq!(read_chain.best_tip().header.bitcoin_hash(),
genesis_block(Bitcoin).header.bitcoin_hash());
}
}