Merge pull request #56 from tamasblummer/memblocks
Moved blockchain and patricia_tree to rust-memblocks
This commit is contained in:
commit
0ef9dc3365
|
@ -1,621 +0,0 @@
|
||||||
// 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/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
//! # 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<Uint256, Box<BlockchainNode>>;
|
|
||||||
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<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));
|
|
||||||
// Don't serialize the prev or next pointers
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The blockchain
|
|
||||||
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));
|
|
||||||
|
|
||||||
// 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<Sha256dHash> {
|
|
||||||
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<Transaction>, 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<NodePtr> {
|
|
||||||
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<Sha256dHash> {
|
|
||||||
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<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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ pub mod opcodes;
|
||||||
#[cfg(not(feature="broken_consensus_code"))] pub mod script;
|
#[cfg(not(feature="broken_consensus_code"))] pub mod script;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod blockchain;
|
|
||||||
|
|
||||||
#[cfg(feature="broken_consensus_code")]
|
#[cfg(feature="broken_consensus_code")]
|
||||||
/// # Script -- including consensus code
|
/// # Script -- including consensus code
|
||||||
|
|
|
@ -25,7 +25,6 @@ pub mod decimal;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
pub mod misc;
|
pub mod misc;
|
||||||
pub mod patricia_tree;
|
|
||||||
pub mod uint;
|
pub mod uint;
|
||||||
|
|
||||||
use std::{error, fmt, io};
|
use std::{error, fmt, io};
|
||||||
|
|
|
@ -1,715 +0,0 @@
|
||||||
// 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/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
//! # 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<K: Copy, V> {
|
|
||||||
data: Option<V>,
|
|
||||||
child_l: Option<Box<PatriciaTree<K, V>>>,
|
|
||||||
child_r: Option<Box<PatriciaTree<K, V>>>,
|
|
||||||
skip_prefix: K,
|
|
||||||
skip_len: u8
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> PatriciaTree<K, V>
|
|
||||||
where K: Copy + BitArray + cmp::Eq +
|
|
||||||
ops::BitXor<K, Output=K> +
|
|
||||||
ops::Add<K, Output=K> +
|
|
||||||
ops::Shr<usize, Output=K> +
|
|
||||||
ops::Shl<usize, Output=K>
|
|
||||||
{
|
|
||||||
/// Constructs a new Patricia tree
|
|
||||||
pub fn new() -> PatriciaTree<K, V> {
|
|
||||||
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<U> 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<U> 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<V> {
|
|
||||||
/// 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<K, V>(tree: &mut PatriciaTree<K, V>, key: &K, key_len: usize) -> (bool, Option<V>)
|
|
||||||
where K: Copy + BitArray + cmp::Eq +
|
|
||||||
ops::Add<K, Output=K> +
|
|
||||||
ops::Shr<usize, Output=K> +
|
|
||||||
ops::Shl<usize, Output=K>
|
|
||||||
{
|
|
||||||
// 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<K: Copy, V>(node: &Option<Box<PatriciaTree<K, V>>>) -> 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<K, V> {
|
|
||||||
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<K, V> {
|
|
||||||
MutItems {
|
|
||||||
node: self as *mut _,
|
|
||||||
parents: vec![],
|
|
||||||
started: false,
|
|
||||||
marker: marker::PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: Copy + BitArray, V: Debug> Debug for PatriciaTree<K, V> {
|
|
||||||
/// Print the entire tree
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
||||||
fn recurse<K, V>(tree: &PatriciaTree<K, V>, 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<S, K, V> ConsensusEncodable<S> for PatriciaTree<K, V>
|
|
||||||
where S: SimpleEncoder,
|
|
||||||
K: Copy + ConsensusEncodable<S>,
|
|
||||||
V: ConsensusEncodable<S>
|
|
||||||
{
|
|
||||||
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<D, K, V> ConsensusDecodable<D> for PatriciaTree<K, V>
|
|
||||||
where D: SimpleDecoder,
|
|
||||||
K: Copy + ConsensusDecodable<D>,
|
|
||||||
V: ConsensusDecodable<D>
|
|
||||||
{
|
|
||||||
fn consensus_decode(d: &mut D) -> Result<PatriciaTree<K, V>, 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<K, V>>,
|
|
||||||
parents: Vec<&'tree PatriciaTree<K, V>>
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutable iterator
|
|
||||||
pub struct MutItems<'tree, K: Copy + 'tree, V: 'tree> {
|
|
||||||
started: bool,
|
|
||||||
node: *mut PatriciaTree<K, V>,
|
|
||||||
parents: Vec<*mut PatriciaTree<K, V>>,
|
|
||||||
marker: marker::PhantomData<&'tree PatriciaTree<K, V>>
|
|
||||||
}
|
|
||||||
|
|
||||||
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<K: Copy, V>(opt_ptr: &Option<Box<PatriciaTree<K, V>>>) -> Option<&PatriciaTree<K, V>> {
|
|
||||||
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<K: Copy, V>(opt_ptr: &Option<Box<PatriciaTree<K, V>>>) -> *mut PatriciaTree<K, V> {
|
|
||||||
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<PatriciaTree<Uint128, u32>, _> = 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue