From 77ce6f18d0c278c13473dce23f1019c51e25945a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 9 Mar 2018 17:22:31 +0100 Subject: [PATCH] Moved blockchain and patricia_tree to rust-memblocks --- src/blockdata/blockchain.rs | 621 ------------------------------- src/blockdata/mod.rs | 1 - src/util/mod.rs | 1 - src/util/patricia_tree.rs | 715 ------------------------------------ 4 files changed, 1338 deletions(-) delete mode 100644 src/blockdata/blockchain.rs delete mode 100644 src/util/patricia_tree.rs diff --git a/src/blockdata/blockchain.rs b/src/blockdata/blockchain.rs deleted file mode 100644 index c7441f2..0000000 --- a/src/blockdata/blockchain.rs +++ /dev/null @@ -1,621 +0,0 @@ -// 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 . -// - -//! # Bitcoin Blockchain -//! -//! This module provides the structures and functions to maintain the -//! blockchain. -//! - -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>; -type NodePtr = *const BlockchainNode; - -/// A link in the blockchain -pub struct BlockchainNode { - /// The actual block - pub block: Block, - /// Total work from genesis to this point - pub total_work: Uint256, - /// Expected value of `block.header.bits` for this block; only changes every - /// `blockdata::constants::DIFFCHANGE_INTERVAL;` blocks - pub required_difficulty: Uint256, - /// Height above genesis - pub height: u32, - /// Whether the transaction data is stored - pub has_txdata: bool, - /// Pointer to block's parent - prev: NodePtr, - /// Pointer to block's child - next: NodePtr -} - -impl BlockchainNode { - /// Is the node on the main chain? - pub 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 ConsensusEncodable 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)); - // Don't serialize the prev or next pointers - Ok(()) - } -} - -impl ConsensusDecodable for BlockchainNode { - #[inline] - fn consensus_decode(d: &mut D) -> Result { - 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() - } -} - -/// The blockchain -pub struct Blockchain { - network: Network, - tree: BlockTree, - best_tip: NodePtr, - best_hash: Sha256dHash, - genesis_hash: Sha256dHash -} - -impl ConsensusEncodable 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 ConsensusDecodable for Blockchain { - fn consensus_decode(d: &mut D) -> Result { - 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)); - - // Lookup best tip - 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))); - } - }; - // Lookup genesis - if tree.lookup(&genesis_hash.into_le(), 256).is_none() { - return Err(d.error(format!("genesis {:x} not in tree", genesis_hash))); - } - // Reconnect all prev pointers - 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; - } - // Reconnect next pointers on the main chain - unsafe { - let mut scan = best; - while !(*scan).prev.is_null() { - let prev = (*scan).prev as *mut BlockchainNode; - (*prev).next = scan; - scan = prev as NodePtr; - } - - // Check that "genesis" is the genesis - if (*scan).bitcoin_hash() != genesis_hash { - return Err(d.error(format!("no path from tip {:x} to genesis {:x}", - best_hash, genesis_hash))); - } - } - - // Return the chain - Ok(Blockchain { - network: network, - tree: tree, - best_tip: best, - best_hash: best_hash, - genesis_hash: genesis_hash - }) - } -} - -// TODO: this should maybe be public, in which case it needs to be tagged -// with a PhantomData marker tying it to the tree's lifetime. -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 { - if self.index.is_null() { - return None; - } - let ret = Some(unsafe { (*self.index).bitcoin_hash() }); - - // Rewind once (if we are at the genesis, this will set self.index to None) - self.index = unsafe { (*self.index).prev }; - // If we are not at the genesis, rewind `self.skip` times, or until we are. - 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 - } -} - -/// An iterator over blocks in blockheight order -pub struct BlockIter<'tree> { - index: NodePtr, - // Note: we don't actually touch the blockchain. But we need - // to keep it borrowed to prevent it being mutated, since some - // mutable blockchain methods call .mut_borrow() on the block - // links, which would blow up if the iterator did a regular - // borrow at the same time. - marker: marker::PhantomData<&'tree Blockchain> -} - -/// An iterator over blocks in reverse blockheight order. Note that this -/// is essentially the same as if we'd implemented `DoubleEndedIterator` -/// on `BlockIter` --- but we can't do that since if `BlockIter` is started -/// off the main chain, it will not reach the best tip, so the iterator -/// and its `.rev()` would be iterators over different chains! To avoid -/// this suprising behaviour we simply use separate iterators. -pub struct RevBlockIter<'tree> { - index: NodePtr, - // See comment in BlockIter for why we need this - marker: marker::PhantomData<&'tree Blockchain> -} - -/// An iterator over blocks in reverse blockheight order, which yielding only -/// stale blocks (ending at the point where it would've returned a block on -/// the main chain). It does this by checking if the `next` pointer of the -/// next-to-by-yielded block matches the currently-yielded block. If not, scan -/// forward from next-to-be-yielded block. If we hit the best tip, set the -/// next-to-by-yielded block to None instead. -/// -/// So to handle reorgs, you create a `RevStaleBlockIter` starting from the last -/// known block, and play it until it runs out, rewinding every block except for -/// the last one. Since the `UtxoSet` `rewind` function sets its `last_hash()` to -/// the prevblockhash of the rewinded block (which will be on the main chain at -/// the end of the iteration), you can then sync it up same as if you were doing -/// a plain old fast-forward. -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; - // Check if the next block is going to be on the main chain - 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 - } - } -} - -/// This function emulates the `GetCompact(SetCompact(n))` in the satoshi code, -/// which drops the precision to something that can be encoded precisely in -/// the nBits block header field. Savour the perversity. This is in Bitcoin -/// consensus code. What. Gaah! -fn satoshi_the_precision(n: Uint256) -> Uint256 { - // Shift by B bits right then left to turn the low bits to zero - let bits = 8 * ((n.bits() + 7) / 8 - 3); - let mut ret = n >> bits; - // Oh, did I say B was that fucked up formula? I meant sometimes also + 8. - if ret.bit(23) { - ret = (ret >> 8) << 8; - } - ret << bits -} - -impl Blockchain { - /// Constructs a new 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: Default::default(), - 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, has_txdata: bool) -> Result<(), util::Error> { - match self.tree.lookup_mut(hash, 256) { - Some(existing_block) => { - existing_block.block.txdata.clone_from(&txdata); - existing_block.has_txdata = has_txdata; - Ok(()) - }, - None => Err(BlockNotFound) - } - } - - /// Looks up a block in the chain and returns the BlockchainNode containing it - pub fn get_block(&self, hash: Sha256dHash) -> Option<&BlockchainNode> { - self.tree.lookup(&hash.into_le(), 256).map(|node| &**node) - } - - /// Locates a block in the chain and overwrites its txdata - pub fn add_txdata(&mut self, block: Block) -> Result<(), util::Error> { - self.replace_txdata(&block.header.bitcoin_hash().into_le(), block.txdata, true) - } - - /// Locates a block in the chain and removes its txdata - pub fn remove_txdata(&mut self, hash: Sha256dHash) -> Result<(), util::Error> { - self.replace_txdata(&hash.into_le(), vec![], false) - } - - /// Adds a block header to the chain - pub fn add_header(&mut self, header: BlockHeader) -> Result<(), util::Error> { - self.real_add_block(Block { header: header, txdata: vec![] }, false) - } - - /// Adds a block to the chain - 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> { - // get_prev optimizes the common case where we are extending the best tip - #[inline] - fn get_prev(chain: &Blockchain, hash: Sha256dHash) -> Option { - if hash == chain.best_hash { - Some(chain.best_tip) - } else { - chain.tree.lookup(&hash.into_le(), 256).map(|boxptr| &**boxptr as NodePtr) - } - } - // Check for multiple inserts (bitcoind from c9a09183 to 3c85d2ec doesn't - // handle locator hashes properly and may return blocks multiple times, - // and this may also happen in case of a reorg. - if self.tree.lookup(&block.header.bitcoin_hash().into_le(), 256).is_some() { - return Err(DuplicateHash); - } - // Construct node, if possible - let new_block = match get_prev(self, block.header.prev_blockhash) { - Some(prev) => { - let difficulty = - // Compute required difficulty if this is a diffchange block - if (unsafe { (*prev).height } + 1) % DIFFCHANGE_INTERVAL == 0 { - let timespan = unsafe { - // Scan back DIFFCHANGE_INTERVAL blocks - let mut scan = prev; - for _ in 0..(DIFFCHANGE_INTERVAL - 1) { - scan = (*scan).prev; - } - // Get clamped timespan between first and last blocks - 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 - } - }; - // Compute new target - let mut target = unsafe { (*prev).block.header.target() }; - target = target.mul_u32(timespan); - target = target / Uint256::from_u64(DIFFCHANGE_TIMESPAN as u64).unwrap(); - // Clamp below MAX_TARGET (difficulty 1) - let max = max_target(self.network); - if target > max { target = max }; - // Compactify (make expressible in the 8+24 nBits float format - satoshi_the_precision(target) - // On non-diffchange blocks, Testnet has a rule that any 20-minute-long - // block intervals result the difficulty - } else if self.network == Network::Testnet && - block.header.time > unsafe { (*prev).block.header.time } + 2*TARGET_BLOCK_SPACING { - max_target(self.network) - // On the other hand, if we are in Testnet and the block interval is less - // than 20 minutes, we need to scan backward to find a block for which the - // previous rule did not apply, to find the "real" difficulty. - } else if self.network == Network::Testnet { - // Scan back DIFFCHANGE_INTERVAL blocks - unsafe { - let mut scan = prev; - while (*scan).height % DIFFCHANGE_INTERVAL != 0 && - (*scan).required_difficulty == max_target(self.network) { - scan = (*scan).prev; - } - (*scan).required_difficulty - } - // Otherwise just use the last block's difficulty - } else { - unsafe { (*prev).required_difficulty } - }; - // Create node - 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); - } - }; - - // spv validate the block - try!(new_block.block.header.spv_validate(&new_block.required_difficulty)); - - // Insert the new block - let raw_ptr = &*new_block as NodePtr; - self.tree.insert(&new_block.block.header.bitcoin_hash().into_le(), 256, new_block); - // Replace the best tip if necessary - if unsafe { (*raw_ptr).total_work > (*self.best_tip).total_work } { - self.set_best_tip(raw_ptr); - } - Ok(()) - } - - /// Sets the best tip (not public) - fn set_best_tip(&mut self, tip: NodePtr) { - // Fix next links - unsafe { - let mut scan = self.best_tip; - // Scan backward - while !(*scan).prev.is_null() { - // If we hit the old best, there is no need to reorg. - if scan == self.best_tip { break; } - // Otherwise set the next-ptr and carry on - let prev = (*scan).prev as *mut BlockchainNode; - (*prev).next = scan; - scan = (*scan).prev; - } - } - // Set best - self.best_hash = unsafe { (*tip).bitcoin_hash() }; - self.best_tip = tip; - } - - /// Returns the genesis block's blockhash - pub fn genesis_hash(&self) -> Sha256dHash { - self.genesis_hash - } - - /// Returns the best tip - pub fn best_tip(&self) -> &Block { - unsafe { &(*self.best_tip).block } - } - - /// Returns the best tip height - pub fn best_tip_height(&self) -> u32 { - unsafe { (*self.best_tip).height } - } - - /// Returns the best tip's blockhash - pub fn best_tip_hash(&self) -> Sha256dHash { - self.best_hash - } - - /// Returns an array of locator hashes used in `getheaders` messages - pub fn locator_hashes(&self) -> Vec { - LocatorHashIter::new(self.best_tip).collect() - } - - /// An iterator over all blocks in the chain starting from `start_hash` - 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 - } - } - - /// An iterator over all blocks in reverse order to the genesis, starting with `start_hash` - 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 - } - } - - /// An iterator over all blocks -not- in the best chain, in reverse order, starting from `start_hash` - pub fn rev_stale_iter(&self, start_hash: Sha256dHash) -> RevStaleBlockIter { - let start = match self.tree.lookup(&start_hash.into_le(), 256) { - Some(boxptr) => { - // If we are already on the main chain, we have a dead iterator - 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 = 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()); - } -} - - - diff --git a/src/blockdata/mod.rs b/src/blockdata/mod.rs index a37522d..a4f9d3c 100644 --- a/src/blockdata/mod.rs +++ b/src/blockdata/mod.rs @@ -23,7 +23,6 @@ pub mod opcodes; #[cfg(not(feature="broken_consensus_code"))] pub mod script; pub mod transaction; pub mod block; -pub mod blockchain; #[cfg(feature="broken_consensus_code")] /// # Script -- including consensus code diff --git a/src/util/mod.rs b/src/util/mod.rs index 6fb1d55..1a83a6d 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -25,7 +25,6 @@ pub mod decimal; pub mod hash; pub mod iter; pub mod misc; -pub mod patricia_tree; pub mod uint; use std::{error, fmt, io}; diff --git a/src/util/patricia_tree.rs b/src/util/patricia_tree.rs deleted file mode 100644 index 7d397eb..0000000 --- a/src/util/patricia_tree.rs +++ /dev/null @@ -1,715 +0,0 @@ -// 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 . -// - -//! # Patricia/Radix Trie -//! -//! A Patricia trie is a trie in which nodes with only one child are -//! merged with the child, giving huge space savings for sparse tries. -//! A radix tree is more general, working with keys that are arbitrary -//! strings; a Patricia tree uses bitstrings. -//! - -use std::fmt::Debug; -use std::marker; -use std::{cmp, fmt, ops, ptr}; - -use network::encodable::{ConsensusDecodable, ConsensusEncodable}; -use network::serialize::{SimpleDecoder, SimpleEncoder}; -use util::BitArray; - -/// Patricia troo -pub struct PatriciaTree { - data: Option, - child_l: Option>>, - child_r: Option>>, - skip_prefix: K, - skip_len: u8 -} - -impl PatriciaTree - where K: Copy + BitArray + cmp::Eq + - ops::BitXor + - ops::Add + - ops::Shr + - ops::Shl -{ - /// Constructs a new Patricia tree - pub fn new() -> PatriciaTree { - PatriciaTree { - data: None, - child_l: None, - child_r: None, - skip_prefix: BitArray::zero(), - skip_len: 0 - } - } - - /// Lookup a value by exactly matching `key` and return a referenc - pub fn lookup_mut(&mut self, key: &K, key_len: usize) -> Option<&mut V> { - // Caution: `lookup_mut` never modifies its self parameter (in fact its - // internal recursion uses a non-mutable self, so we are OK to just - // transmute our self pointer into a mutable self before passing it in. - use std::mem::transmute; - unsafe { transmute(self.lookup(key, key_len)) } - } - - /// Lookup a value by exactly matching `key` and return a mutable reference - pub fn lookup(&self, key: &K, key_len: usize) -> Option<&V> { - let mut node = self; - let mut key_idx = 0; - - loop { - // If the search key is shorter than the node prefix, there is no - // way we can match, so fail. - if key_len - key_idx < node.skip_len as usize { - return None; - } - - // Key fails to match prefix --- no match - if node.skip_prefix != key.bit_slice(key_idx, key_idx + node.skip_len as usize) { - return None; - } - - // Key matches prefix: if they are an exact match, return the data - if node.skip_len as usize == key_len - key_idx { - return node.data.as_ref(); - } else { - // Key matches prefix: search key longer than node key, recurse - key_idx += 1 + node.skip_len as usize; - let subtree = if key.bit(key_idx - 1) { &node.child_r } else { &node.child_l }; - match *subtree { - Some(ref bx) => { - node = &**bx; // bx is a &Box here, so &**bx gets &U - } - None => { return None; } - } - } - } // end loop - } - - /// Inserts a value with key `key`, returning true on success. If a value is already - /// stored against `key`, do nothing and return false. - #[inline] - pub fn insert(&mut self, key: &K, key_len: usize, value: V) -> bool { - self.real_insert(key, key_len, value, false) - } - - /// Inserts a value with key `key`, returning true on success. If a value is already - /// stored against `key`, overwrite it and return false. - #[inline] - pub fn insert_or_update(&mut self, key: &K, key_len: usize, value: V) -> bool { - self.real_insert(key, key_len, value, true) - } - - fn real_insert(&mut self, key: &K, key_len: usize, value: V, overwrite: bool) -> bool { - let mut node = self; - let mut idx = 0; - loop { - // Mask in case search key is shorter than node key - let slice_len = cmp::min(node.skip_len as usize, key_len - idx); - let masked_prefix = node.skip_prefix.mask(slice_len); - let key_slice = key.bit_slice(idx, idx + slice_len); - - // Prefixes do not match: split key - if masked_prefix != key_slice { - let diff = (masked_prefix ^ key_slice).trailing_zeros(); - - // Remove the old node's children - let child_l = node.child_l.take(); - let child_r = node.child_r.take(); - let value_neighbor = node.data.take(); - let tmp = node; // borrowck hack - let (insert, neighbor) = if key_slice.bit(diff) - { (&mut tmp.child_r, &mut tmp.child_l) } - else { (&mut tmp.child_l, &mut tmp.child_r) }; - *insert = Some(Box::new(PatriciaTree { - data: None, - child_l: None, - child_r: None, - skip_prefix: key.bit_slice(idx + diff + 1, key_len), - skip_len: (key_len - idx - diff - 1) as u8 - })); - *neighbor = Some(Box::new(PatriciaTree { - data: value_neighbor, - child_l: child_l, - child_r: child_r, - skip_prefix: tmp.skip_prefix >> (diff + 1), - skip_len: tmp.skip_len - diff as u8 - 1 - })); - // Chop the prefix down - tmp.skip_len = diff as u8; - tmp.skip_prefix = tmp.skip_prefix.mask(diff); - // Recurse - idx += 1 + diff; - node = &mut **insert.as_mut().unwrap(); - } - // Prefixes match - else { - let slice_len = key_len - idx; - // Search key is shorter than skip prefix: truncate the prefix and attach - // the old data as a child - if node.skip_len as usize > slice_len { - // Remove the old node's children - let child_l = node.child_l.take(); - let child_r = node.child_r.take(); - let value_neighbor = node.data.take(); - // Put the old data in a new child, with the remainder of the prefix - let new_child = if node.skip_prefix.bit(slice_len) - { &mut node.child_r } else { &mut node.child_l }; - *new_child = Some(Box::new(PatriciaTree { - data: value_neighbor, - child_l: child_l, - child_r: child_r, - skip_prefix: node.skip_prefix >> (slice_len + 1), - skip_len: node.skip_len - slice_len as u8 - 1 - })); - // Chop the prefix down and put the new data in place - node.skip_len = slice_len as u8; - node.skip_prefix = key_slice; - node.data = Some(value); - return true; - } - // If we have an exact match, great, insert it - else if node.skip_len as usize == slice_len { - if node.data.is_none() { - node.data = Some(value); - return true; - } - if overwrite { - node.data = Some(value); - } - return false; - } - // Search key longer than node key, recurse - else { - let tmp = node; // hack to appease borrowck - idx += tmp.skip_len as usize + 1; - let subtree = if key.bit(idx - 1) - { &mut tmp.child_r } else { &mut tmp.child_l }; - // Recurse, adding a new node if necessary - if subtree.is_none() { - *subtree = Some(Box::new(PatriciaTree { - data: None, - child_l: None, - child_r: None, - skip_prefix: key.bit_slice(idx, key_len), - skip_len: (key_len - idx) as u8 - })); - } - // subtree.get_mut_ref is a &mut Box here, so &mut ** gets a &mut U - node = &mut **subtree.as_mut().unwrap(); - } // end search_len vs prefix len - } // end if prefixes match - } // end loop - } - - /// Deletes a value with key `key`, returning it on success. If no value with - /// the given key is found, return None - pub fn delete(&mut self, key: &K, key_len: usize) -> Option { - /// Return value is (deletable, actual return value), where `deletable` is true - /// is true when the entire node can be deleted (i.e. it has no children) - fn recurse(tree: &mut PatriciaTree, key: &K, key_len: usize) -> (bool, Option) - where K: Copy + BitArray + cmp::Eq + - ops::Add + - ops::Shr + - ops::Shl - { - // If the search key is shorter than the node prefix, there is no - // way we can match, so fail. - if key_len < tree.skip_len as usize { - return (false, None); - } - - // Key fails to match prefix --- no match - if tree.skip_prefix != key.mask(tree.skip_len as usize) { - return (false, None); - } - - // If we are here, the key matches the prefix - if tree.skip_len as usize == key_len { - // Exact match -- delete and return - let ret = tree.data.take(); - let bit = tree.child_r.is_some(); - // First try to consolidate if there is only one child - if tree.child_l.is_some() && tree.child_r.is_some() { - // Two children means we cannot consolidate or delete - return (false, ret); - } - match (tree.child_l.take(), tree.child_r.take()) { - (Some(_), Some(_)) => unreachable!(), - (Some(child), None) | (None, Some(child)) => { - let child = *child; /* workaround for rustc #28536 */ - let PatriciaTree { data, child_l, child_r, skip_len, skip_prefix } = child; - tree.data = data; - tree.child_l = child_l; - tree.child_r = child_r; - let new_bit = if bit { let ret: K = BitArray::one(); - ret << (tree.skip_len as usize) } - else { BitArray::zero() }; - tree.skip_prefix = tree.skip_prefix + - new_bit + - (skip_prefix << (1 + tree.skip_len as usize)); - tree.skip_len += 1 + skip_len; - return (false, ret); - } - // No children means this node is deletable - (None, None) => { return (true, ret); } - } - } - - // Otherwise, the key is longer than the prefix and we need to recurse - let next_bit = key.bit(tree.skip_len as usize); - // Recursively get the return value. This awkward scope is required - // to shorten the time we mutably borrow the node's children -- we - // might want to borrow the sibling later, so the borrow needs to end. - let ret = { - let target = if next_bit { &mut tree.child_r } else { &mut tree.child_l }; - - // If we can't recurse, fail - if target.is_none() { - return (false, None); - } - // Otherwise, do it - let (delete_child, ret) = recurse(&mut **target.as_mut().unwrap(), - &(*key >> (tree.skip_len as usize + 1)), - key_len - tree.skip_len as usize - 1); - if delete_child { - target.take(); - } - ret - }; - - // The above block may have deleted the target. If we now have only one - // child, merge it into the parent. (If we have no children, mark this - // node for deletion.) - if tree.data.is_some() { - // First though, if this is a data node, we can neither delete nor - // consolidate it. - return (false, ret); - } - - match (tree.child_r.is_some(), tree.child_l.take(), tree.child_r.take()) { - // Two children? Can't do anything, just sheepishly put them back - (_, Some(child_l), Some(child_r)) => { - tree.child_l = Some(child_l); - tree.child_r = Some(child_r); - (false, ret) - } - // One child? Consolidate - (bit, Some(child), None) | (bit, None, Some(child)) => { - let child = *child; /* workaround for rustc #28536 */ - let PatriciaTree { data, child_l, child_r, skip_len, skip_prefix } = child; - tree.data = data; - tree.child_l = child_l; - tree.child_r = child_r; - let new_bit = if bit { let ret: K = BitArray::one(); - ret << (tree.skip_len as usize) } - else { BitArray::zero() }; - tree.skip_prefix = tree.skip_prefix + - new_bit + - (skip_prefix << (1 + tree.skip_len as usize)); - tree.skip_len += 1 + skip_len; - (false, ret) - } - // No children? Delete - (_, None, None) => { - (true, ret) - } - } - } - let (_, ret) = recurse(self, key, key_len); - ret - } - - /// Count all the nodes - pub fn node_count(&self) -> usize { - fn recurse(node: &Option>>) -> usize { - match *node { - Some(ref node) => { 1 + recurse(&node.child_l) + recurse(&node.child_r) } - None => 0 - } - } - 1 + recurse(&self.child_l) + recurse(&self.child_r) - } - - /// Returns an iterator over all elements in the tree - pub fn iter(&self) -> Items { - Items { - node: Some(self), - parents: vec![], - started: false - } - } - - /// Returns a mutable iterator over all elements in the tree - pub fn mut_iter(&mut self) -> MutItems { - MutItems { - node: self as *mut _, - parents: vec![], - started: false, - marker: marker::PhantomData - } - } -} - -impl Debug for PatriciaTree { - /// Print the entire tree - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - fn recurse(tree: &PatriciaTree, f: &mut fmt::Formatter, depth: usize) -> Result<(), fmt::Error> - where K: Copy + BitArray, V: Debug - { - for i in 0..tree.skip_len as usize { - try!(write!(f, "{:}", if tree.skip_prefix.bit(i) { 1 } else { 0 })); - } - try!(writeln!(f, ": {:?}", tree.data)); - // left gets no indentation - if let Some(ref t) = tree.child_l { - for _ in 0..(depth + tree.skip_len as usize) { - try!(write!(f, "-")); - } - try!(write!(f, "0")); - try!(recurse(&**t, f, depth + tree.skip_len as usize + 1)); - } - // right one gets indentation - if let Some(ref t) = tree.child_r { - for _ in 0..(depth + tree.skip_len as usize) { - try!(write!(f, "_")); - } - try!(write!(f, "1")); - try!(recurse(&**t, f, depth + tree.skip_len as usize + 1)); - } - Ok(()) - } - recurse(self, f, 0) - } -} - -impl ConsensusEncodable for PatriciaTree - where S: SimpleEncoder, - K: Copy + ConsensusEncodable, - V: ConsensusEncodable -{ - fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> { - // Depth-first serialization: serialize self, then children - try!(self.skip_prefix.consensus_encode(s)); - try!(self.skip_len.consensus_encode(s)); - try!(self.data.consensus_encode(s)); - try!(self.child_l.consensus_encode(s)); - try!(self.child_r.consensus_encode(s)); - Ok(()) - } -} - -impl ConsensusDecodable for PatriciaTree - where D: SimpleDecoder, - K: Copy + ConsensusDecodable, - V: ConsensusDecodable -{ - fn consensus_decode(d: &mut D) -> Result, D::Error> { - Ok(PatriciaTree { - skip_prefix: try!(ConsensusDecodable::consensus_decode(d)), - skip_len: try!(ConsensusDecodable::consensus_decode(d)), - data: try!(ConsensusDecodable::consensus_decode(d)), - child_l: try!(ConsensusDecodable::consensus_decode(d)), - child_r: try!(ConsensusDecodable::consensus_decode(d)) - }) - } -} - -/// Iterator -pub struct Items<'tree, K: Copy + 'tree, V: 'tree> { - started: bool, - node: Option<&'tree PatriciaTree>, - parents: Vec<&'tree PatriciaTree> -} - -/// Mutable iterator -pub struct MutItems<'tree, K: Copy + 'tree, V: 'tree> { - started: bool, - node: *mut PatriciaTree, - parents: Vec<*mut PatriciaTree>, - marker: marker::PhantomData<&'tree PatriciaTree> -} - -impl<'a, K: Copy, V> Iterator for Items<'a, K, V> { - type Item = &'a V; - - fn next(&mut self) -> Option<&'a V> { - fn borrow_opt(opt_ptr: &Option>>) -> Option<&PatriciaTree> { - opt_ptr.as_ref().map(|b| &**b) - } - - // If we haven't started, maybe return the "last" return value, - // which will be the root node. - if !self.started { - if self.node.is_some() && (**self.node.as_ref().unwrap()).data.is_some() { - return self.node.unwrap().data.as_ref(); - } - self.started = true; - } - - // Find next data-containing node - while self.node.is_some() { - let mut node = self.node.take(); - // Try to go left - let child_l = borrow_opt(&node.unwrap().child_l); - if child_l.is_some() { - self.parents.push(node.unwrap()); - self.node = child_l; - // Try to go right, going back up the tree if necessary - } else { - while node.is_some() { - let child_r = borrow_opt(&node.unwrap().child_r); - if child_r.is_some() { - self.node = child_r; - break; - } - node = self.parents.pop(); - } - } - // Stop if we've found data. - if self.node.is_some() && self.node.unwrap().data.is_some() { - break; - } - } // end loop - // Return data - self.node.and_then(|node| node.data.as_ref()) - } -} - -impl<'a, K: Copy, V> Iterator for MutItems<'a, K, V> { - type Item = &'a mut V; - - fn next(&mut self) -> Option<&'a mut V> { - fn borrow_opt(opt_ptr: &Option>>) -> *mut PatriciaTree { - match *opt_ptr { - Some(ref data) => &**data as *const _ as *mut _, - None => ptr::null_mut() - } - } - - // If we haven't started, maybe return the "last" return value, - // which will be the root node. - if !self.started { - unsafe { - if !self.node.is_null() && (*self.node).data.is_some() { - return (*self.node).data.as_mut(); - } - } - self.started = true; - } - - // Find next data-containing node - while !self.node.is_null() { - // Try to go left - let child_l = unsafe { borrow_opt(&(*self.node).child_l) }; - if !child_l.is_null() { - self.parents.push(self.node); - self.node = child_l; - // Try to go right, going back up the tree if necessary - } else { - while !self.node.is_null() { - let child_r = unsafe { borrow_opt(&(*self.node).child_r) }; - if !child_r.is_null() { - self.node = child_r; - break; - } - self.node = self.parents.pop().unwrap_or(ptr::null_mut()); - } - } - // Stop if we've found data. - if !self.node.is_null() && unsafe { (*self.node).data.is_some() } { - break; - } - } // end loop - // Return data - if !self.node.is_null() { - unsafe { (*self.node).data.as_mut() } - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use network::serialize::{deserialize, serialize}; - use util::hash::Sha256dHash; - use util::uint::Uint128; - use util::uint::Uint256; - use util::patricia_tree::PatriciaTree; - use util::BitArray; - - #[test] - fn patricia_single_insert_lookup_delete_test() { - let mut key = Uint256::from_u64(0xDEADBEEFDEADBEEF).unwrap(); - key = key + (key << 64); - - let mut tree = PatriciaTree::new(); - tree.insert(&key, 100, 100u32); - tree.insert(&key, 120, 100u32); - - assert_eq!(tree.lookup(&key, 100), Some(&100u32)); - assert_eq!(tree.lookup(&key, 101), None); - assert_eq!(tree.lookup(&key, 99), None); - assert_eq!(tree.delete(&key, 100), Some(100u32)); - } - - #[test] - fn patricia_insert_lookup_delete_test() { - let mut tree = PatriciaTree::new(); - let mut hashes = vec![]; - for i in 0u32..5000 { - let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_le().low_128(); - tree.insert(&hash, 250, i); - hashes.push(hash); - } - - // Check that all inserts are correct - for (n, hash) in hashes.iter().enumerate() { - let ii = n as u32; - let ret = tree.lookup(hash, 250); - assert_eq!(ret, Some(&ii)); - } - - // Delete all the odd-numbered nodes - for (n, hash) in hashes.iter().enumerate() { - if n % 2 == 1 { - let ii = n as u32; - let ret = tree.delete(hash, 250); - assert_eq!(ret, Some(ii)); - } - } - - // Confirm all is correct - for (n, hash) in hashes.iter().enumerate() { - let ii = n as u32; - let ret = tree.lookup(hash, 250); - if n % 2 == 0 { - assert_eq!(ret, Some(&ii)); - } else { - assert_eq!(ret, None); - } - } - } - - #[test] - fn patricia_insert_substring_keys() { - // This test uses a bunch of keys that are substrings of each other - // to make sure insertion and deletion does not lose data - let mut tree = PatriciaTree::new(); - let mut hashes = vec![]; - // Start by inserting a bunch of chunder - for i in 1u32..500 { - let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_le().low_128(); - tree.insert(&hash, 128, i * 1000); - hashes.push(hash); - } - // Do the actual test -- note that we also test insertion and deletion - // at the root here. - for i in 0u32..10 { - tree.insert(&BitArray::zero(), i as usize, i); - } - for i in 0u32..10 { - let m = tree.lookup(&BitArray::zero(), i as usize); - assert_eq!(m, Some(&i)); - } - for i in 0u32..10 { - let m = tree.delete(&BitArray::zero(), i as usize); - assert_eq!(m, Some(i)); - } - // Check that the chunder was unharmed - for (n, hash) in hashes.iter().enumerate() { - let ii = ((n + 1) * 1000) as u32; - let ret = tree.lookup(hash, 128); - assert_eq!(ret, Some(&ii)); - } - } - - #[test] - fn patricia_iter_test() { - let n_elems = 5000; - let mut tree = PatriciaTree::new(); - let mut data = vec![None; n_elems]; - // Start by inserting a bunch of stuff - for i in 0..n_elems { - let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_le().low_128(); - tree.insert(&hash, 128, i); - data[i] = Some(()); - } - - // Iterate over and try to get everything - for n in tree.iter() { - assert!(data[*n].is_some()); - data[*n] = None; - } - - // Check that we got everything - assert!(data.iter().all(|opt| opt.is_none())); - } - - #[test] - fn patricia_mut_iter_test() { - let n_elems = 5000; - let mut tree = PatriciaTree::new(); - let mut data = vec![None; n_elems]; - // Start by inserting a bunch of stuff - for i in 0..n_elems { - let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_le().low_128(); - tree.insert(&hash, 128, i); - data[i] = Some(()); - } - - // Iterate over and flip all the values - for n in tree.mut_iter() { - *n = n_elems - *n - 1; - } - - // Iterate over and try to get everything - for n in tree.mut_iter() { - assert!(data[*n].is_some()); - data[*n] = None; - } - - // Check that we got everything - assert!(data.iter().all(|opt| opt.is_none())); - } - - #[test] - fn patricia_serialize_test() { - // Build a tree - let mut tree = PatriciaTree::new(); - let mut hashes = vec![]; - for i in 0u32..5000 { - let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).into_le().low_128(); - tree.insert(&hash, 250, i); - hashes.push(hash); - } - - // Serialize it - let serialized = serialize(&tree).unwrap(); - // Deserialize it - let deserialized: Result, _> = deserialize(&serialized); - assert!(deserialized.is_ok()); - let new_tree = deserialized.unwrap(); - - // Check that all inserts are still there - for (n, hash) in hashes.iter().enumerate() { - let ii = n as u32; - let ret = new_tree.lookup(hash, 250); - assert_eq!(ret, Some(&ii)); - } - } -} -