Initial commit, move into Cargo
This commit is contained in:
commit
c9ad7c0b58
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
[package]
|
||||||
|
|
||||||
|
name = "bitcoin-rs"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
|
||||||
|
|
||||||
|
|
||||||
|
[[lib]]
|
||||||
|
|
||||||
|
name = "bitcoin"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
|
||||||
|
[dependencies.rust-crypto]
|
||||||
|
|
||||||
|
git = "https://github.com/DaGenix/rust-crypto.git"
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
// 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 Block
|
||||||
|
//!
|
||||||
|
//! A block is a bundle of transactions with a proof-of-work attached,
|
||||||
|
//! which attaches to an earlier block to form the blockchain. This
|
||||||
|
//! module describes structures and functions needed to describe
|
||||||
|
//! these blocks and the blockchain.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::io::IoResult;
|
||||||
|
use std::num::{Zero, from_u64};
|
||||||
|
|
||||||
|
use util::hash::Sha256dHash;
|
||||||
|
use util::uint::Uint256;
|
||||||
|
use network::serialize::{Serializable, SerializeIter, VarInt};
|
||||||
|
use blockdata::transaction::Transaction;
|
||||||
|
|
||||||
|
/// A block header, which contains all the block's information except
|
||||||
|
/// the actual transactions
|
||||||
|
#[deriving(PartialEq, Show)]
|
||||||
|
pub struct BlockHeader {
|
||||||
|
/// The protocol version. Should always be 1.
|
||||||
|
pub version: u32,
|
||||||
|
/// Reference to the previous block in the chain
|
||||||
|
pub prev_blockhash: Sha256dHash,
|
||||||
|
/// The root hash of the merkle tree of transactions in the block
|
||||||
|
pub merkle_root: Sha256dHash,
|
||||||
|
/// The timestamp of the block, as claimed by the mainer
|
||||||
|
pub time: u32,
|
||||||
|
/// The target value below which the blockhash must lie, encoded as a
|
||||||
|
/// a float (with well-defined rounding, of course)
|
||||||
|
pub bits: u32,
|
||||||
|
/// The nonce, selected to obtain a low enough blockhash
|
||||||
|
pub nonce: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Bitcoin block, which is a collection of transactions with an attached
|
||||||
|
/// proof of work.
|
||||||
|
#[deriving(PartialEq, Show)]
|
||||||
|
pub struct Block {
|
||||||
|
/// The block header
|
||||||
|
pub header: BlockHeader,
|
||||||
|
/// List of transactions contained in the block
|
||||||
|
pub txdata: Vec<Transaction>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A block header with txcount attached, which is given in the `headers`
|
||||||
|
/// network message.
|
||||||
|
#[deriving(PartialEq, Show)]
|
||||||
|
pub struct LoneBlockHeader {
|
||||||
|
/// The actual block header
|
||||||
|
pub header: BlockHeader,
|
||||||
|
/// The number of transactions in the block. This will always be zero
|
||||||
|
/// when the LoneBlockHeader is returned as part ef a `headers` message.
|
||||||
|
pub tx_count: VarInt
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockHeader {
|
||||||
|
/// Computes the target [0, T] that a blockhash must land in to be valid
|
||||||
|
pub fn target(&self) -> Uint256 {
|
||||||
|
// This is a floating-point "compact" encoding originally used by
|
||||||
|
// OpenSSL, which satoshi put into consensus code, so we're stuck
|
||||||
|
// with it. The exponent needs to have 3 subtracted from it, hence
|
||||||
|
// this goofy decoding code:
|
||||||
|
let (mant, expt) = {
|
||||||
|
let unshifted_expt = self.bits >> 24;
|
||||||
|
if unshifted_expt <= 3 {
|
||||||
|
((self.bits & 0xFFFFFF) >> 8 * (3 - unshifted_expt as uint), 0)
|
||||||
|
} else {
|
||||||
|
(self.bits & 0xFFFFFF, 8 * ((self.bits >> 24) - 3))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The mantissa is signed but may not be negative
|
||||||
|
if mant > 0x7FFFFF {
|
||||||
|
Zero::zero()
|
||||||
|
} else {
|
||||||
|
from_u64::<Uint256>(mant as u64).unwrap() << (expt as uint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an SPV validation of a block, which confirms that the proof-of-work
|
||||||
|
/// is correct, but does not verify that the transactions are valid or encoded
|
||||||
|
/// correctly.
|
||||||
|
pub fn spv_validate(&self, required_target: &Uint256) -> bool {
|
||||||
|
let ref target = self.target();
|
||||||
|
if target != required_target {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let ref hash = self.hash().as_uint256();
|
||||||
|
hash <= target
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the total work of the block
|
||||||
|
pub fn work(&self) -> Uint256 {
|
||||||
|
// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind)
|
||||||
|
let mut ret = !self.target();
|
||||||
|
let mut ret1 = self.target();
|
||||||
|
ret1.increment();
|
||||||
|
ret = ret.div(&ret1);
|
||||||
|
ret.increment();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_serializable!(BlockHeader, version, prev_blockhash, merkle_root, time, bits, nonce)
|
||||||
|
impl_serializable!(Block, header, txdata)
|
||||||
|
impl_serializable!(LoneBlockHeader, header, tx_count)
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::io::IoResult;
|
||||||
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
|
use blockdata::block::Block;
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_test() {
|
||||||
|
let some_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap();
|
||||||
|
let cutoff_block = "010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac".from_hex().unwrap();
|
||||||
|
|
||||||
|
let prevhash = "4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000".from_hex().unwrap();
|
||||||
|
let merkle = "bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c".from_hex().unwrap();
|
||||||
|
|
||||||
|
let decode: IoResult<Block> = Serializable::deserialize(some_block.iter().map(|n| *n));
|
||||||
|
let bad_decode: IoResult<Block> = Serializable::deserialize(cutoff_block.iter().map(|n| *n));
|
||||||
|
|
||||||
|
assert!(decode.is_ok());
|
||||||
|
assert!(bad_decode.is_err());
|
||||||
|
let real_decode = decode.unwrap();
|
||||||
|
assert_eq!(real_decode.header.version, 1);
|
||||||
|
assert_eq!(real_decode.header.prev_blockhash.as_slice(), prevhash.as_slice());
|
||||||
|
// [test] TODO: actually compute the merkle root
|
||||||
|
assert_eq!(real_decode.header.merkle_root.as_slice(), merkle.as_slice());
|
||||||
|
assert_eq!(real_decode.header.time, 1231965655);
|
||||||
|
assert_eq!(real_decode.header.bits, 486604799);
|
||||||
|
assert_eq!(real_decode.header.nonce, 2067413810);
|
||||||
|
// [test] TODO: check the transaction data
|
||||||
|
|
||||||
|
let reserialize = real_decode.serialize();
|
||||||
|
assert_eq!(reserialize.as_slice(), some_block.as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,624 @@
|
||||||
|
// 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.
|
||||||
|
//!
|
||||||
|
//! Note to developers: do not expose any ref-counted pointers in the public
|
||||||
|
//! API of this module. Internally we do unsafe mutations of them and we need
|
||||||
|
//! to make sure we are holding the only references.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use alloc::rc::Rc;
|
||||||
|
use std::cell::{Ref, RefCell};
|
||||||
|
use std::io::{IoResult, IoError, OtherIoError};
|
||||||
|
use std::num::Zero;
|
||||||
|
use std::kinds::marker;
|
||||||
|
|
||||||
|
use blockdata::block::{Block, BlockHeader};
|
||||||
|
use blockdata::transaction::Transaction;
|
||||||
|
use blockdata::constants::{DIFFCHANGE_INTERVAL, DIFFCHANGE_TIMESPAN, max_target};
|
||||||
|
use network::serialize::{Serializable, SerializeIter};
|
||||||
|
use util::BitArray;
|
||||||
|
use util::uint::Uint256;
|
||||||
|
use util::hash::Sha256dHash;
|
||||||
|
use util::misc::prepend_err;
|
||||||
|
use util::patricia_tree::PatriciaTree;
|
||||||
|
|
||||||
|
type BlockTree = PatriciaTree<Rc<BlockchainNode>, Uint256>;
|
||||||
|
type NodePtr = Option<Rc<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: RefCell<NodePtr>,
|
||||||
|
/// Pointer to block's child
|
||||||
|
next: RefCell<NodePtr>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockchainNode {
|
||||||
|
/// Look up the previous link, caching the result
|
||||||
|
fn prev(&self, tree: &BlockTree) -> NodePtr {
|
||||||
|
let mut cache = self.prev.borrow_mut();
|
||||||
|
if cache.is_some() {
|
||||||
|
return Some(cache.get_ref().clone())
|
||||||
|
}
|
||||||
|
match tree.lookup(&self.block.header.prev_blockhash.as_uint256(), 256) {
|
||||||
|
Some(prev) => { *cache = Some(prev.clone()); return Some(prev.clone()); }
|
||||||
|
None => { return None; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Look up the next link
|
||||||
|
fn next<'a>(&'a self) -> Ref<'a, NodePtr> {
|
||||||
|
self.next.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the next link
|
||||||
|
fn set_next(&self, next: Rc<BlockchainNode>) {
|
||||||
|
let mut cache = self.next.borrow_mut();
|
||||||
|
*cache = Some(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the node on the main chain?
|
||||||
|
fn is_on_main_chain(&self, chain: &Blockchain) -> bool {
|
||||||
|
if self.block.header == chain.best_tip.block.header {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let mut scan = self.next().clone();
|
||||||
|
while scan.is_some() {
|
||||||
|
if scan.get_ref().block.header == chain.best_tip.block.header {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
scan = scan.get_ref().next().clone();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Rc<BlockchainNode> {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut ret = vec![];
|
||||||
|
ret.extend(self.block.serialize().move_iter());
|
||||||
|
ret.extend(self.total_work.serialize().move_iter());
|
||||||
|
ret.extend(self.required_difficulty.serialize().move_iter());
|
||||||
|
ret.extend(self.height.serialize().move_iter());
|
||||||
|
ret.extend(self.has_txdata.serialize().move_iter());
|
||||||
|
// Don't serialize the prev pointer
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Rc<BlockchainNode>> {
|
||||||
|
Ok(Rc::new(BlockchainNode {
|
||||||
|
block: try!(prepend_err("block", Serializable::deserialize(iter.by_ref()))),
|
||||||
|
total_work: try!(prepend_err("total_work", Serializable::deserialize(iter.by_ref()))),
|
||||||
|
required_difficulty: try!(prepend_err("req_difficulty", Serializable::deserialize(iter.by_ref()))),
|
||||||
|
height: try!(prepend_err("height", Serializable::deserialize(iter.by_ref()))),
|
||||||
|
has_txdata: try!(prepend_err("has_txdata", Serializable::deserialize(iter.by_ref()))),
|
||||||
|
prev: RefCell::new(None),
|
||||||
|
next: RefCell::new(None)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override Serialize::hash to return the blockheader hash, since the
|
||||||
|
// hash of the node itself is pretty much meaningless.
|
||||||
|
fn hash(&self) -> Sha256dHash {
|
||||||
|
self.block.header.hash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The blockchain
|
||||||
|
pub struct Blockchain {
|
||||||
|
tree: BlockTree,
|
||||||
|
best_tip: Rc<BlockchainNode>,
|
||||||
|
best_hash: Sha256dHash,
|
||||||
|
genesis_hash: Sha256dHash
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Blockchain {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut ret = vec![];
|
||||||
|
ret.extend(self.tree.serialize().move_iter());
|
||||||
|
ret.extend(self.best_hash.serialize().move_iter());
|
||||||
|
ret.extend(self.genesis_hash.serialize().move_iter());
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||||
|
SerializeIter {
|
||||||
|
data_iter: None,
|
||||||
|
sub_iter_iter: box vec![ &self.tree as &Serializable,
|
||||||
|
&self.best_hash as &Serializable,
|
||||||
|
&self.genesis_hash as &Serializable ].move_iter(),
|
||||||
|
sub_iter: None,
|
||||||
|
sub_started: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Blockchain> {
|
||||||
|
let tree: BlockTree = try!(prepend_err("tree", Serializable::deserialize(iter.by_ref())));
|
||||||
|
let best_hash: Sha256dHash = try!(prepend_err("best_hash", Serializable::deserialize(iter.by_ref())));
|
||||||
|
let genesis_hash: Sha256dHash = try!(prepend_err("genesis_hash", Serializable::deserialize(iter.by_ref())));
|
||||||
|
// Lookup best tip
|
||||||
|
let best = match tree.lookup(&best_hash.as_uint256(), 256) {
|
||||||
|
Some(rc) => rc.clone(),
|
||||||
|
None => { return Err(IoError {
|
||||||
|
kind: OtherIoError,
|
||||||
|
desc: "best tip reference not found in tree",
|
||||||
|
detail: Some(format!("best tip {:x} not found", best_hash))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Lookup genesis
|
||||||
|
if tree.lookup(&genesis_hash.as_uint256(), 256).is_none() {
|
||||||
|
return Err(IoError {
|
||||||
|
kind: OtherIoError,
|
||||||
|
desc: "genesis block not found in tree",
|
||||||
|
detail: Some(format!("genesis {:x} not found", genesis_hash))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Reconnect next and prev pointers back to "genesis", the first node
|
||||||
|
// with no prev pointer.
|
||||||
|
let mut scan = best.clone();
|
||||||
|
let mut prev = best.prev(&tree);
|
||||||
|
while prev.is_some() {
|
||||||
|
prev.get_mut_ref().set_next(scan);
|
||||||
|
scan = prev.get_ref().clone();
|
||||||
|
prev = prev.get_ref().prev(&tree);
|
||||||
|
}
|
||||||
|
// Check that "genesis" is the genesis
|
||||||
|
if scan.block.header.hash() != genesis_hash {
|
||||||
|
Err(IoError {
|
||||||
|
kind: OtherIoError,
|
||||||
|
desc: "best tip did not link back to genesis",
|
||||||
|
detail: Some(format!("no path from tip {:x} to genesis {:x}", best_hash, genesis_hash))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Return the chain
|
||||||
|
Ok(Blockchain {
|
||||||
|
tree: tree,
|
||||||
|
best_tip: best.clone(),
|
||||||
|
best_hash: best_hash,
|
||||||
|
genesis_hash: genesis_hash
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LocatorHashIter<'tree> {
|
||||||
|
index: NodePtr,
|
||||||
|
tree: &'tree BlockTree,
|
||||||
|
count: uint,
|
||||||
|
skip: uint
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tree> LocatorHashIter<'tree> {
|
||||||
|
fn new<'tree>(init: Rc<BlockchainNode>, tree: &'tree BlockTree) -> LocatorHashIter<'tree> {
|
||||||
|
LocatorHashIter { index: Some(init), tree: tree, count: 0, skip: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tree> Iterator<Sha256dHash> for LocatorHashIter<'tree> {
|
||||||
|
fn next(&mut self) -> Option<Sha256dHash> {
|
||||||
|
let ret = match self.index {
|
||||||
|
Some(ref node) => Some(node.hash()),
|
||||||
|
None => { return None; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rewind once (if we are at the genesis, this will set self.index to None)
|
||||||
|
self.index = self.index.get_ref().prev(self.tree);
|
||||||
|
// If we are not at the genesis, rewind `self.skip` times, or until we are.
|
||||||
|
if self.index.is_some() {
|
||||||
|
for _ in range(1, self.skip) {
|
||||||
|
self.index = match self.index.get_ref().prev(self.tree) {
|
||||||
|
Some(rc) => Some(rc),
|
||||||
|
None => { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::ContravariantLifetime<'tree>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
tree: &'tree BlockTree
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<&'tree BlockchainNode> for BlockIter<'tree> {
|
||||||
|
fn next(&mut self) -> Option<&'tree BlockchainNode> {
|
||||||
|
match self.index.clone() {
|
||||||
|
Some(rc) => {
|
||||||
|
use core::mem::transmute;
|
||||||
|
self.index = rc.next().clone();
|
||||||
|
// This transmute is just to extend the lifetime of rc.block
|
||||||
|
// There is unsafety here because we need to be assured that
|
||||||
|
// another copy of the rc (presumably the one in the tree)
|
||||||
|
// exists and will live as long as 'tree.
|
||||||
|
Some(unsafe { transmute(&*rc) } )
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tree> Iterator<&'tree BlockchainNode> for RevBlockIter<'tree> {
|
||||||
|
fn next(&mut self) -> Option<&'tree BlockchainNode> {
|
||||||
|
match self.index.clone() {
|
||||||
|
Some(rc) => {
|
||||||
|
use core::mem::transmute;
|
||||||
|
self.index = rc.prev(self.tree).clone();
|
||||||
|
// This transmute is just to extend the lifetime of rc.block
|
||||||
|
// There is unsafety here because we need to be assured that
|
||||||
|
// another copy of the rc (presumably the one in the tree)
|
||||||
|
// exists and will live as long as 'tree.
|
||||||
|
Some(unsafe { transmute(&*rc) } )
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tree> Iterator<&'tree Block> for RevStaleBlockIter<'tree> {
|
||||||
|
fn next(&mut self) -> Option<&'tree Block> {
|
||||||
|
match self.index.clone() {
|
||||||
|
Some(rc) => {
|
||||||
|
use core::mem::transmute;
|
||||||
|
let next_index = rc.prev(&self.chain.tree);
|
||||||
|
|
||||||
|
// Check if the next block is going to be on the main chain
|
||||||
|
if next_index.is_some() &&
|
||||||
|
next_index.get_ref().next().get_ref().block.header != rc.block.header &&
|
||||||
|
next_index.get_ref().is_on_main_chain(self.chain) {
|
||||||
|
self.index = None;
|
||||||
|
} else {
|
||||||
|
self.index = next_index.clone();
|
||||||
|
}
|
||||||
|
// This transmute is just to extend the lifetime of rc.block
|
||||||
|
// There is unsafety here because we need to be assured that
|
||||||
|
// another copy of the rc (presumably the one in the tree)
|
||||||
|
// exists and will live as long as 'tree.
|
||||||
|
Some(unsafe { transmute(&rc.block) } )
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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. The. Fuck.
|
||||||
|
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(genesis: Block) -> Blockchain {
|
||||||
|
let genhash = genesis.header.hash();
|
||||||
|
let rc_gen = Rc::new(BlockchainNode {
|
||||||
|
total_work: Zero::zero(),
|
||||||
|
required_difficulty: genesis.header.target(),
|
||||||
|
block: genesis,
|
||||||
|
height: 0,
|
||||||
|
has_txdata: true,
|
||||||
|
prev: RefCell::new(None),
|
||||||
|
next: RefCell::new(None)
|
||||||
|
});
|
||||||
|
Blockchain {
|
||||||
|
tree: {
|
||||||
|
let mut pat = PatriciaTree::new();
|
||||||
|
pat.insert(&genhash.as_uint256(), 256, rc_gen.clone());
|
||||||
|
pat
|
||||||
|
},
|
||||||
|
best_hash: genhash,
|
||||||
|
genesis_hash: genhash,
|
||||||
|
best_tip: rc_gen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_txdata(&mut self, hash: &Uint256, txdata: Vec<Transaction>, has_txdata: bool) -> bool {
|
||||||
|
match self.tree.lookup_mut(hash, 256) {
|
||||||
|
Some(existing_block) => {
|
||||||
|
unsafe {
|
||||||
|
// existing_block is an Rc. Rust will not let us mutate it under
|
||||||
|
// any circumstances, since if it were to be reallocated, then
|
||||||
|
// all other references to it would be destroyed. However, we
|
||||||
|
// just need a mutable pointer to the txdata vector; by calling
|
||||||
|
// Vec::clone_from() rather than assigning, we can be assured that
|
||||||
|
// no reallocation can occur, since clone_from() takes an &mut self,
|
||||||
|
// which it does not own and therefore cannot move.
|
||||||
|
//
|
||||||
|
// To be clear: there will undoubtedly be some reallocation within
|
||||||
|
// the Vec itself. We don't care about this. What we care about is
|
||||||
|
// that the Vec (and more pointedly, its containing struct) does not
|
||||||
|
// move, since this would invalidate the Rc that we are snookering.
|
||||||
|
use std::mem::{forget, transmute};
|
||||||
|
let mutable_vec: &mut Vec<Transaction> = transmute(&existing_block.block.txdata);
|
||||||
|
mutable_vec.clone_from(&txdata);
|
||||||
|
// If mutable_vec went out of scope unhindered, it would deallocate
|
||||||
|
// the Vec it points to, since Rust assumes that a mutable vector
|
||||||
|
// is a unique reference (and this one is definitely not).
|
||||||
|
forget(mutable_vec);
|
||||||
|
// Do the same thing with the txdata flac
|
||||||
|
let mutable_bool: &mut bool = transmute(&existing_block.has_txdata);
|
||||||
|
*mutable_bool = has_txdata;
|
||||||
|
forget(mutable_bool);
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
None => return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Locates a block in the chain and overwrites its txdata
|
||||||
|
pub fn add_txdata(&mut self, block: Block) -> bool {
|
||||||
|
self.replace_txdata(&block.header.hash().as_uint256(), block.txdata, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Locates a block in the chain and removes its txdata
|
||||||
|
pub fn remove_txdata(&mut self, hash: Sha256dHash) -> bool {
|
||||||
|
self.replace_txdata(&hash.as_uint256(), vec![], false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a block header to the chain
|
||||||
|
pub fn add_header(&mut self, header: BlockHeader) -> bool {
|
||||||
|
self.real_add_block(Block { header: header, txdata: vec![] }, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a block to the chain
|
||||||
|
pub fn add_block(&mut self, block: Block) -> bool {
|
||||||
|
self.real_add_block(block, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn real_add_block(&mut self, block: Block, has_txdata: bool) -> bool {
|
||||||
|
// get_prev optimizes the common case where we are extending the best tip
|
||||||
|
fn get_prev<'a>(chain: &'a Blockchain, hash: Sha256dHash) -> Option<&'a Rc<BlockchainNode>> {
|
||||||
|
if hash == chain.best_hash { return Some(&chain.best_tip); }
|
||||||
|
chain.tree.lookup(&hash.as_uint256(), 256)
|
||||||
|
}
|
||||||
|
// 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.hash().as_uint256(), 256).is_some() {
|
||||||
|
println!("Warning: tried to add block {} twice!", block.header.hash());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Construct node, if possible
|
||||||
|
let rc_block = match get_prev(self, block.header.prev_blockhash) {
|
||||||
|
Some(prev) => {
|
||||||
|
let difficulty =
|
||||||
|
// Compute required difficulty if this is a diffchange block
|
||||||
|
if (prev.height + 1) % DIFFCHANGE_INTERVAL == 0 {
|
||||||
|
// Scan back DIFFCHANGE_INTERVAL blocks
|
||||||
|
let mut scan = prev.clone();
|
||||||
|
for _ in range(0, DIFFCHANGE_INTERVAL - 1) {
|
||||||
|
scan = scan.prev(&self.tree).unwrap();
|
||||||
|
}
|
||||||
|
// Get clamped timespan between first and last blocks
|
||||||
|
let timespan = 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 = prev.block.header.target();
|
||||||
|
target = target.mul_u32(timespan);
|
||||||
|
target = target / FromPrimitive::from_u64(DIFFCHANGE_TIMESPAN as u64).unwrap();
|
||||||
|
// Clamp below MAX_TARGET (difficulty 1)
|
||||||
|
let max = max_target();
|
||||||
|
if target > max { target = max };
|
||||||
|
// Compactify (make expressible in the 8+24 nBits float format
|
||||||
|
satoshi_the_precision(&target)
|
||||||
|
} else {
|
||||||
|
// Otherwise just use the last block's difficulty
|
||||||
|
prev.required_difficulty
|
||||||
|
};
|
||||||
|
// Create node
|
||||||
|
let ret = Rc::new(BlockchainNode {
|
||||||
|
total_work: block.header.work().add(&prev.total_work),
|
||||||
|
block: block,
|
||||||
|
required_difficulty: difficulty,
|
||||||
|
height: prev.height + 1,
|
||||||
|
has_txdata: has_txdata,
|
||||||
|
prev: RefCell::new(Some(prev.clone())),
|
||||||
|
next: RefCell::new(None)
|
||||||
|
});
|
||||||
|
prev.set_next(ret.clone());
|
||||||
|
ret
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
println!("TODO: couldn't add block");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// spv validate the block
|
||||||
|
if !rc_block.block.header.spv_validate(&rc_block.required_difficulty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the new block
|
||||||
|
self.tree.insert(&rc_block.block.header.hash().as_uint256(), 256, rc_block.clone());
|
||||||
|
// Replace the best tip if necessary
|
||||||
|
if rc_block.total_work > self.best_tip.total_work {
|
||||||
|
self.set_best_tip(rc_block);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the best tip (not public)
|
||||||
|
fn set_best_tip(&mut self, tip: Rc<BlockchainNode>) {
|
||||||
|
let old_best = self.best_tip.clone();
|
||||||
|
// Set best
|
||||||
|
self.best_hash = tip.hash();
|
||||||
|
self.best_tip = tip;
|
||||||
|
// Fix next links
|
||||||
|
let mut scan = self.best_tip.clone();
|
||||||
|
let mut prev = self.best_tip.prev(&self.tree);
|
||||||
|
// Scan backward
|
||||||
|
loop {
|
||||||
|
// If we hit the old best, there is no need to reorg
|
||||||
|
if scan.block.header == old_best.block.header {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If we hit the genesis, stop
|
||||||
|
if prev.is_none() {
|
||||||
|
println!("Warning: reorg past the genesis. This is a bug.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If we hit something pointing along the wrong chain, this is
|
||||||
|
// a branch point at which we are reorg'ing
|
||||||
|
if prev.get_ref().next().is_none() ||
|
||||||
|
prev.get_ref().next().get_ref().block.header != scan.block.header {
|
||||||
|
prev.get_mut_ref().set_next(scan);
|
||||||
|
}
|
||||||
|
scan = prev.clone().unwrap();
|
||||||
|
prev = prev.unwrap().prev(&self.tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the best tip
|
||||||
|
pub fn best_tip<'a>(&'a self) -> &'a Block {
|
||||||
|
&self.best_tip.block
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.clone(), &self.tree).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator over all blocks in the chain starting from `start_hash`
|
||||||
|
pub fn iter<'a>(&'a self, start_hash: Sha256dHash) -> BlockIter<'a> {
|
||||||
|
BlockIter {
|
||||||
|
index: self.tree.lookup(&start_hash.as_uint256(), 256).map(|rc| rc.clone()),
|
||||||
|
marker: marker::ContravariantLifetime::<'a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator over all blocks in reverse order to the genesis, starting with `start_hash`
|
||||||
|
pub fn rev_iter<'a>(&'a self, start_hash: Sha256dHash) -> RevBlockIter<'a> {
|
||||||
|
RevBlockIter {
|
||||||
|
index: self.tree.lookup(&start_hash.as_uint256(), 256).map(|rc| rc.clone()),
|
||||||
|
tree: &self.tree
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator over all blocks -not- in the best chain, in reverse order, starting from `start_hash`
|
||||||
|
pub fn rev_stale_iter<'a>(&'a self, start_hash: Sha256dHash) -> RevStaleBlockIter<'a> {
|
||||||
|
let mut start = self.tree.lookup(&start_hash.as_uint256(), 256).map(|rc| rc.clone());
|
||||||
|
// If we are already on the main chain, we have a dead iterator
|
||||||
|
if start.is_some() && start.get_ref().is_on_main_chain(self) {
|
||||||
|
start = None;
|
||||||
|
}
|
||||||
|
// Return iterator
|
||||||
|
RevStaleBlockIter {
|
||||||
|
index: start,
|
||||||
|
chain: self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::prelude::*;
|
||||||
|
use std::io::IoResult;
|
||||||
|
|
||||||
|
use blockdata::blockchain::Blockchain;
|
||||||
|
use blockdata::constants::genesis_block;
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn blockchain_serialize_test() {
|
||||||
|
let empty_chain = Blockchain::new(genesis_block());
|
||||||
|
assert_eq!(empty_chain.best_tip.hash().serialize(), genesis_block().header.hash().serialize());
|
||||||
|
|
||||||
|
let serial = empty_chain.serialize();
|
||||||
|
assert_eq!(serial, empty_chain.serialize_iter().collect());
|
||||||
|
|
||||||
|
let deserial: IoResult<Blockchain> = Serializable::deserialize(serial.iter().map(|n| *n));
|
||||||
|
assert!(deserial.is_ok());
|
||||||
|
let read_chain = deserial.unwrap();
|
||||||
|
assert_eq!(read_chain.best_tip.hash().serialize(), genesis_block().header.hash().serialize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Blockdata constants
|
||||||
|
//!
|
||||||
|
//! This module provides various constants relating to the blockchain and
|
||||||
|
//! consensus code. In particular, it defines the genesis block and its
|
||||||
|
//! single transaction
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::num::from_u64;
|
||||||
|
|
||||||
|
use blockdata::opcodes;
|
||||||
|
use blockdata::script::Script;
|
||||||
|
use blockdata::transaction::{Transaction, TxOut, TxIn};
|
||||||
|
use blockdata::block::{Block, BlockHeader};
|
||||||
|
use util::misc::hex_bytes;
|
||||||
|
use util::hash::{merkle_root, zero_hash};
|
||||||
|
use util::uint::Uint256;
|
||||||
|
|
||||||
|
pub static MAX_SEQUENCE: u32 = 0xFFFFFFFF;
|
||||||
|
pub static COIN_VALUE: u64 = 100000000;
|
||||||
|
pub static DIFFCHANGE_INTERVAL: u32 = 2016;
|
||||||
|
pub static DIFFCHANGE_TIMESPAN: u32 = 14 * 24 * 3600;
|
||||||
|
|
||||||
|
/// In Bitcoind this is insanely described as ~((u256)0 >> 32)
|
||||||
|
pub fn max_target() -> Uint256 {
|
||||||
|
from_u64::<Uint256>(0xFFFF).unwrap() << 208u
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs and returns the coinbase (and only) transaction of the genesis block
|
||||||
|
pub fn genesis_tx() -> Transaction {
|
||||||
|
// Base
|
||||||
|
let mut ret = Transaction {
|
||||||
|
version: 1,
|
||||||
|
lock_time: 0,
|
||||||
|
input: vec![],
|
||||||
|
output: vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inputs
|
||||||
|
let mut in_script = Script::new();
|
||||||
|
in_script.push_scriptint(486604799);
|
||||||
|
in_script.push_scriptint(4);
|
||||||
|
in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
|
||||||
|
ret.input.push(TxIn {
|
||||||
|
prev_hash: zero_hash(),
|
||||||
|
prev_index: 0xFFFFFFFF,
|
||||||
|
script_sig: in_script,
|
||||||
|
sequence: MAX_SEQUENCE
|
||||||
|
});
|
||||||
|
|
||||||
|
// Outputs
|
||||||
|
let mut out_script = Script::new();
|
||||||
|
out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
|
||||||
|
out_script.push_opcode(opcodes::CHECKSIG);
|
||||||
|
ret.output.push(TxOut {
|
||||||
|
value: 50 * COIN_VALUE,
|
||||||
|
script_pubkey: out_script
|
||||||
|
});
|
||||||
|
|
||||||
|
// end
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs and returns the genesis block
|
||||||
|
pub fn genesis_block() -> Block {
|
||||||
|
let txdata = vec![genesis_tx()];
|
||||||
|
let header = BlockHeader {
|
||||||
|
version: 1,
|
||||||
|
prev_blockhash: zero_hash(),
|
||||||
|
merkle_root: merkle_root(txdata.as_slice()),
|
||||||
|
time: 1231006505,
|
||||||
|
bits: 0x1d00ffff,
|
||||||
|
nonce: 2083236893
|
||||||
|
};
|
||||||
|
|
||||||
|
Block {
|
||||||
|
header: header,
|
||||||
|
txdata: txdata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
use blockdata::constants::{genesis_block, genesis_tx};
|
||||||
|
use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};
|
||||||
|
use util::misc::hex_bytes;
|
||||||
|
use util::hash::zero_hash;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn genesis_first_transaction() {
|
||||||
|
let gen = genesis_tx();
|
||||||
|
|
||||||
|
assert_eq!(gen.version, 1);
|
||||||
|
assert_eq!(gen.input.len(), 1);
|
||||||
|
assert_eq!(gen.input[0].prev_hash.as_slice(), zero_hash().as_slice());
|
||||||
|
assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
|
||||||
|
assert_eq!(gen.input[0].script_sig.serialize().as_slice(),
|
||||||
|
hex_bytes("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73").unwrap().as_slice());
|
||||||
|
|
||||||
|
assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
|
||||||
|
assert_eq!(gen.output.len(), 1);
|
||||||
|
assert_eq!(gen.output[0].script_pubkey.serialize().as_slice(),
|
||||||
|
hex_bytes("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap().as_slice());
|
||||||
|
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
||||||
|
assert_eq!(gen.lock_time, 0);
|
||||||
|
|
||||||
|
assert_eq!(gen.hash().serialize().iter().rev().map(|n| *n).collect::<Vec<u8>>(),
|
||||||
|
hex_bytes("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn genesis_full_block() {
|
||||||
|
let gen = genesis_block();
|
||||||
|
|
||||||
|
assert_eq!(gen.header.version, 1);
|
||||||
|
assert_eq!(gen.header.prev_blockhash.as_slice(), zero_hash().as_slice());
|
||||||
|
assert_eq!(gen.header.merkle_root.serialize().iter().rev().map(|n| *n).collect::<Vec<u8>>(),
|
||||||
|
hex_bytes("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap());
|
||||||
|
assert_eq!(gen.header.time, 1231006505);
|
||||||
|
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||||
|
assert_eq!(gen.header.nonce, 2083236893);
|
||||||
|
assert_eq!(gen.header.hash().serialize().iter().rev().map(|n| *n).collect::<Vec<u8>>(),
|
||||||
|
hex_bytes("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Blockdata
|
||||||
|
//!
|
||||||
|
//! This module defines structions and functions for storing the blocks and
|
||||||
|
//! transactions which make up the Bitcoin system.
|
||||||
|
//!
|
||||||
|
|
||||||
|
pub mod constants;
|
||||||
|
pub mod opcodes;
|
||||||
|
pub mod script;
|
||||||
|
pub mod transaction;
|
||||||
|
pub mod block;
|
||||||
|
pub mod blockchain;
|
||||||
|
pub mod utxoset;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Opcodes
|
||||||
|
//!
|
||||||
|
//! Bitcoin's script uses a stack-based assembly language. This module defines
|
||||||
|
//! the mapping from assembler instructions to bytes.
|
||||||
|
//!
|
||||||
|
|
||||||
|
pub static FALSE: u8 = 0x00;
|
||||||
|
pub static TRUE: u8 = 0x51;
|
||||||
|
pub static PUSHDATA1: u8 = 0x4C;
|
||||||
|
pub static PUSHDATA2: u8 = 0x4D;
|
||||||
|
pub static PUSHDATA4: u8 = 0x4E;
|
||||||
|
pub static CHECKSIG: u8 = 0xAC;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Script
|
||||||
|
//!
|
||||||
|
//! Scripts define Bitcoin's digital signature scheme: a signature is formed
|
||||||
|
//! from a script (the second half of which is defined by a coin to be spent,
|
||||||
|
//! and the first half provided by the spending transaction), and is valid
|
||||||
|
//! iff the script leaves `TRUE` on the stack after being evaluated.
|
||||||
|
//! Bitcoin's script is a stack-based assembly language similar in spirit to
|
||||||
|
//! Forth.
|
||||||
|
//!
|
||||||
|
//! This module provides the structures and functions needed to support scripts.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::io::IoResult;
|
||||||
|
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
use blockdata::opcodes;
|
||||||
|
use util::thinvec::ThinVec;
|
||||||
|
|
||||||
|
#[deriving(PartialEq, Show, Clone)]
|
||||||
|
/// A Bitcoin script
|
||||||
|
pub struct Script(ThinVec<u8>);
|
||||||
|
|
||||||
|
impl Script {
|
||||||
|
/// Creates a new empty script
|
||||||
|
pub fn new() -> Script { Script(ThinVec::new()) }
|
||||||
|
|
||||||
|
/// Adds instructions to push an integer onto the stack. Integers are
|
||||||
|
/// encoded as little-endian signed-magnitude numbers, but there are
|
||||||
|
/// dedicated opcodes to push some small integers.
|
||||||
|
pub fn push_int(&mut self, data: int) {
|
||||||
|
// We can special-case -1, 1-16
|
||||||
|
if data == -1 || (data >= 1 && data <=16) {
|
||||||
|
let &Script(ref mut raw) = self;
|
||||||
|
raw.push(data as u8 + opcodes::TRUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We can also special-case zero
|
||||||
|
if data == 0 {
|
||||||
|
let &Script(ref mut raw) = self;
|
||||||
|
raw.push(opcodes::FALSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Otherwise encode it as data
|
||||||
|
self.push_scriptint(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds instructions to push an integer onto the stack, using the explicit
|
||||||
|
/// encoding regardless of the availability of dedicated opcodes.
|
||||||
|
pub fn push_scriptint(&mut self, data: int) {
|
||||||
|
let neg = data < 0;
|
||||||
|
|
||||||
|
let mut abs = if neg { -data } else { data } as uint;
|
||||||
|
let mut v = vec![];
|
||||||
|
while abs > 0xFF {
|
||||||
|
v.push((abs & 0xFF) as u8);
|
||||||
|
abs >>= 8;
|
||||||
|
}
|
||||||
|
// If the number's value causes the sign bit to be set, we need an extra
|
||||||
|
// byte to get the correct value and correct sign bit
|
||||||
|
if abs & 0x80 != 0 {
|
||||||
|
v.push(abs as u8);
|
||||||
|
v.push(if neg { 0x80u8 } else { 0u8 });
|
||||||
|
}
|
||||||
|
// Otherwise we just set the sign bit ourselves
|
||||||
|
else {
|
||||||
|
abs |= if neg { 0x80 } else { 0 };
|
||||||
|
v.push(abs as u8);
|
||||||
|
}
|
||||||
|
// Finally we put the encoded int onto the stack
|
||||||
|
self.push_slice(v.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds instructions to push some arbitrary data onto the stack
|
||||||
|
pub fn push_slice(&mut self, data: &[u8]) {
|
||||||
|
let &Script(ref mut raw) = self;
|
||||||
|
// Start with a PUSH opcode
|
||||||
|
match data.len() {
|
||||||
|
n if n < opcodes::PUSHDATA1 as uint => { raw.push(n as u8); },
|
||||||
|
n if n < 0x100 => {
|
||||||
|
raw.push(opcodes::PUSHDATA1);
|
||||||
|
raw.push(n as u8);
|
||||||
|
},
|
||||||
|
n if n < 0x10000 => {
|
||||||
|
raw.push(opcodes::PUSHDATA2);
|
||||||
|
raw.push((n % 0x100) as u8);
|
||||||
|
raw.push((n / 0x100) as u8);
|
||||||
|
},
|
||||||
|
n if n < 0x100000000 => {
|
||||||
|
raw.push(opcodes::PUSHDATA4);
|
||||||
|
raw.push((n % 0x100) as u8);
|
||||||
|
raw.push(((n / 0x100) % 0x100) as u8);
|
||||||
|
raw.push(((n / 0x10000) % 0x100) as u8);
|
||||||
|
raw.push((n / 0x1000000) as u8);
|
||||||
|
}
|
||||||
|
_ => fail!("tried to put a 4bn+ sized object into a script!")
|
||||||
|
}
|
||||||
|
// Then push the acraw
|
||||||
|
raw.extend(data.iter().map(|n| *n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an individual opcode to the script
|
||||||
|
pub fn push_opcode(&mut self, data: u8) {
|
||||||
|
let &Script(ref mut raw) = self;
|
||||||
|
raw.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Script {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let &Script(ref data) = self;
|
||||||
|
data.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Script> {
|
||||||
|
let raw = Serializable::deserialize(iter);
|
||||||
|
raw.map(|ok| Script(ok))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::io::IoResult;
|
||||||
|
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
use blockdata::script::Script;
|
||||||
|
use blockdata::opcodes;
|
||||||
|
use util::misc::hex_bytes;
|
||||||
|
use util::thinvec::ThinVec;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn script() {
|
||||||
|
let mut comp = ThinVec::new();
|
||||||
|
let mut script = Script::new();
|
||||||
|
assert_eq!(script, Script(ThinVec::new()));
|
||||||
|
|
||||||
|
// small ints
|
||||||
|
script.push_int(1); comp.push(82u8); assert_eq!(script, Script(comp.clone()));
|
||||||
|
script.push_int(0); comp.push(0u8); assert_eq!(script, Script(comp.clone()));
|
||||||
|
script.push_int(4); comp.push(85u8); assert_eq!(script, Script(comp.clone()));
|
||||||
|
script.push_int(-1); comp.push(80u8); assert_eq!(script, Script(comp.clone()));
|
||||||
|
// forced scriptint
|
||||||
|
script.push_scriptint(4); comp.push_all([1u8, 4]); assert_eq!(script, Script(comp.clone()));
|
||||||
|
// big ints
|
||||||
|
script.push_int(17); comp.push_all([1u8, 17]); assert_eq!(script, Script(comp.clone()));
|
||||||
|
script.push_int(10000); comp.push_all([2u8, 16, 39]); assert_eq!(script, Script(comp.clone()));
|
||||||
|
// notice the sign bit set here, hence the extra zero/128 at the end
|
||||||
|
script.push_int(10000000); comp.push_all([4u8, 128, 150, 152, 0]); assert_eq!(script, Script(comp.clone()));
|
||||||
|
script.push_int(-10000000); comp.push_all([4u8, 128, 150, 152, 128]); assert_eq!(script, Script(comp.clone()));
|
||||||
|
|
||||||
|
// data
|
||||||
|
script.push_slice("NRA4VR".as_bytes()); comp.push_all([6u8, 78, 82, 65, 52, 86, 82]); assert_eq!(script, Script(comp.clone()));
|
||||||
|
|
||||||
|
// opcodes
|
||||||
|
script.push_opcode(opcodes::CHECKSIG); comp.push(0xACu8); assert_eq!(script, Script(comp.clone()));
|
||||||
|
script.push_opcode(opcodes::CHECKSIG); comp.push(0xACu8); assert_eq!(script, Script(comp.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn script_serialize() {
|
||||||
|
let hex_script = hex_bytes("6c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52").unwrap();
|
||||||
|
let script: IoResult<Script> = Serializable::deserialize(hex_script.iter().map(|n| *n));
|
||||||
|
assert!(script.is_ok());
|
||||||
|
assert_eq!(script.unwrap().serialize().as_slice(), hex_script.as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
// 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 Transaction
|
||||||
|
//!
|
||||||
|
//! A transaction describes a transfer of money. It consumes previously-unspent
|
||||||
|
//! transaction outputs and produces new ones, satisfying the condition to spend
|
||||||
|
//! the old outputs (typically a digital signature with a specific key must be
|
||||||
|
//! provided) and defining the condition to spend the new ones. The use of digital
|
||||||
|
//! signatures ensures that coins cannot be spent by unauthorized parties.
|
||||||
|
//!
|
||||||
|
//! This module provides the structures and functions needed to support transactions.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::io::IoResult;
|
||||||
|
use util::hash::Sha256dHash;
|
||||||
|
use network::serialize::{Serializable, SerializeIter};
|
||||||
|
use blockdata::script::Script;
|
||||||
|
#[cfg(test)]
|
||||||
|
use util::misc::hex_bytes;
|
||||||
|
|
||||||
|
/// A transaction input, which defines old coins to be consumed
|
||||||
|
#[deriving(Clone, PartialEq, Show)]
|
||||||
|
pub struct TxIn {
|
||||||
|
/// The hash of the transaction whose output is being used an an input
|
||||||
|
pub prev_hash: Sha256dHash,
|
||||||
|
/// The index of the output in the previous transaction, which may have several
|
||||||
|
pub prev_index: u32,
|
||||||
|
/// The script which pushes values on the stack which will cause
|
||||||
|
/// the referenced output's script to accept
|
||||||
|
pub script_sig: Script,
|
||||||
|
/// The sequence number, which suggests to miners which of two
|
||||||
|
/// conflicting transactions should be preferred, or 0xFFFFFFFF
|
||||||
|
/// to ignore this feature. This is generally never used since
|
||||||
|
/// the miner behaviour cannot be enforced.
|
||||||
|
pub sequence: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A transaction output, which defines new coins to be created from old ones.
|
||||||
|
#[deriving(Clone, PartialEq, Show)]
|
||||||
|
pub struct TxOut {
|
||||||
|
/// The value of the output, in satoshis
|
||||||
|
pub value: u64,
|
||||||
|
/// The script which must satisfy for the output to be spent
|
||||||
|
pub script_pubkey: Script
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Bitcoin transaction, which describes an authenticated movement of coins
|
||||||
|
#[deriving(Clone, PartialEq, Show)]
|
||||||
|
pub struct Transaction {
|
||||||
|
/// The protocol version, should always be 1.
|
||||||
|
pub version: u32,
|
||||||
|
/// Block number before which this transaction is valid, or 0 for
|
||||||
|
/// valid immediately.
|
||||||
|
pub lock_time: u32,
|
||||||
|
/// List of inputs
|
||||||
|
pub input: Vec<TxIn>,
|
||||||
|
/// List of outputs
|
||||||
|
pub output: Vec<TxOut>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_serializable!(TxIn, prev_hash, prev_index, script_sig, sequence)
|
||||||
|
impl_serializable!(TxOut, value, script_pubkey)
|
||||||
|
impl_serializable!(Transaction, version, input, output, lock_time)
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_txin() {
|
||||||
|
let txin: IoResult<TxIn> = Serializable::deserialize(hex_bytes("a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff").unwrap().iter().map(|n| *n));
|
||||||
|
assert!(txin.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transaction() {
|
||||||
|
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||||
|
let tx: IoResult<Transaction> = Serializable::deserialize(hex_tx.iter().map(|n| *n));
|
||||||
|
assert!(tx.is_ok());
|
||||||
|
let realtx = tx.unwrap();
|
||||||
|
// All these tests aren't really needed because if they fail, the hash check at the end
|
||||||
|
// will also fail. But these will show you where the failure is so I'll leave them in.
|
||||||
|
assert_eq!(realtx.version, 1);
|
||||||
|
assert_eq!(realtx.input.len(), 1);
|
||||||
|
// In particular this one is easy to get backward -- in bitcoin hashes are encoded
|
||||||
|
// as little-endian 256-bit numbers rather than as data strings. (This is why we
|
||||||
|
// have this crazy .iter().rev() thing going on in many hash-related tests.
|
||||||
|
assert_eq!(realtx.input[0].prev_hash.as_slice().iter().rev().map(|n| *n).collect::<Vec<u8>>(),
|
||||||
|
hex_bytes("ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1").unwrap());
|
||||||
|
assert_eq!(realtx.input[0].prev_index, 1);
|
||||||
|
assert_eq!(realtx.output.len(), 1);
|
||||||
|
assert_eq!(realtx.lock_time, 0);
|
||||||
|
|
||||||
|
assert_eq!(realtx.hash().serialize().iter().rev().map(|n| *n).collect::<Vec<u8>>(),
|
||||||
|
hex_bytes("a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,356 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # UTXO Set
|
||||||
|
//!
|
||||||
|
//! This module provides the structures and functions to maintain an
|
||||||
|
//! index of UTXOs.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::io::IoResult;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use blockdata::transaction::{Transaction, TxOut};
|
||||||
|
use blockdata::block::Block;
|
||||||
|
use network::serialize::{Serializable, SerializeIter};
|
||||||
|
use util::hash::Sha256dHash;
|
||||||
|
use util::uint::Uint128;
|
||||||
|
use util::patricia_tree::PatriciaTree;
|
||||||
|
use util::thinvec::ThinVec;
|
||||||
|
|
||||||
|
/// How much of the hash to use as a key
|
||||||
|
static KEY_LEN: uint = 128;
|
||||||
|
|
||||||
|
/// Vector of outputs; None indicates a nonexistent or already spent output
|
||||||
|
type UtxoNode = ThinVec<Option<Box<TxOut>>>;
|
||||||
|
|
||||||
|
/// The UTXO set
|
||||||
|
pub struct UtxoSet {
|
||||||
|
// We use a 128-bit indexed tree to save memory
|
||||||
|
tree: PatriciaTree<UtxoNode, Uint128>,
|
||||||
|
last_hash: Sha256dHash,
|
||||||
|
// A circular buffer of deleted utxos, grouped by block
|
||||||
|
spent_txos: Vec<Vec<Box<TxOut>>>,
|
||||||
|
// The last index into the above buffer that was assigned to
|
||||||
|
spent_idx: u64,
|
||||||
|
n_utxos: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_serializable!(UtxoSet, last_hash, n_utxos, spent_txos, spent_idx, tree)
|
||||||
|
|
||||||
|
impl UtxoSet {
|
||||||
|
/// Constructs a new UTXO set
|
||||||
|
pub fn new(genesis: Block, rewind_limit: uint) -> UtxoSet {
|
||||||
|
// There is in fact a transaction in the genesis block, but the Bitcoin
|
||||||
|
// reference client does not add its sole output to the UTXO set. We
|
||||||
|
// must follow suit, otherwise we will accept a transaction spending it
|
||||||
|
// while the reference client won't, causing us to fork off the network.
|
||||||
|
UtxoSet {
|
||||||
|
tree: PatriciaTree::new(),
|
||||||
|
last_hash: genesis.header.hash(),
|
||||||
|
spent_txos: Vec::from_elem(rewind_limit, vec![]),
|
||||||
|
spent_idx: 0,
|
||||||
|
n_utxos: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add all the UTXOs of a transaction to the set
|
||||||
|
fn add_utxos(&mut self, tx: &Transaction) -> bool {
|
||||||
|
let txid = tx.hash();
|
||||||
|
// Locate node if it's already there
|
||||||
|
let mut new_node = ThinVec::with_capacity(tx.output.len() as u32);
|
||||||
|
for (vout, txo) in tx.output.iter().enumerate() {
|
||||||
|
// Unsafe since we are not uninitializing the old data in the vector
|
||||||
|
unsafe { new_node.init(vout as uint, Some(box txo.clone())); }
|
||||||
|
}
|
||||||
|
// TODO: insert/lookup should return a Result which we pass along
|
||||||
|
if self.tree.insert(&txid.as_uint128(), KEY_LEN, new_node) {
|
||||||
|
self.n_utxos += tx.output.len() as u64;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a UTXO from the set and return it
|
||||||
|
fn take_utxo(&mut self, txid: Sha256dHash, vout: u32) -> Option<Box<TxOut>> {
|
||||||
|
// This whole function has awkward scoping thx to lexical borrow scoping :(
|
||||||
|
let (ret, should_delete) = {
|
||||||
|
// Locate the UTXO, failing if not found
|
||||||
|
let node = match self.tree.lookup_mut(&txid.as_uint128(), KEY_LEN) {
|
||||||
|
Some(node) => node,
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = {
|
||||||
|
// Check that this specific output is there
|
||||||
|
if vout as uint >= node.len() { return None; }
|
||||||
|
let replace = unsafe { node.get_mut(vout as uint) };
|
||||||
|
replace.take()
|
||||||
|
};
|
||||||
|
|
||||||
|
let should_delete = node.iter().filter(|slot| slot.is_some()).count() == 0;
|
||||||
|
(ret, should_delete)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete the whole node if it is no longer being used
|
||||||
|
if should_delete {
|
||||||
|
self.tree.delete(&txid.as_uint128(), KEY_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.n_utxos -= if ret.is_some() { 1 } else { 0 };
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to a UTXO in the set
|
||||||
|
pub fn get_utxo<'a>(&'a mut self, txid: Sha256dHash, vout: u32) -> Option<&'a Box<TxOut>> {
|
||||||
|
// Locate the UTXO, failing if not found
|
||||||
|
let node = match self.tree.lookup_mut(&txid.as_uint128(), KEY_LEN) {
|
||||||
|
Some(node) => node,
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
|
// Check that this specific output is there
|
||||||
|
if vout as uint >= node.len() { return None; }
|
||||||
|
let replace = unsafe { node.get(vout as uint) };
|
||||||
|
replace.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply the transactions contained in a block
|
||||||
|
pub fn update(&mut self, block: &Block) -> bool {
|
||||||
|
// Make sure we are extending the UTXO set in order
|
||||||
|
if self.last_hash != block.header.prev_blockhash {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the next hash immediately so that if anything goes wrong,
|
||||||
|
// we can rewind from the point that we're at.
|
||||||
|
self.last_hash = block.header.hash();
|
||||||
|
let spent_idx = self.spent_idx as uint;
|
||||||
|
self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64;
|
||||||
|
self.spent_txos.get_mut(spent_idx).clear();
|
||||||
|
|
||||||
|
let mut skipped_genesis = false;
|
||||||
|
for tx in block.txdata.iter() {
|
||||||
|
// Put the removed utxos into the stxo cache. Note that the order that
|
||||||
|
// they are pushed onto the stxo cache -must- match the order of the
|
||||||
|
// txos in the block so that rewind() will rewind them properly.
|
||||||
|
if skipped_genesis {
|
||||||
|
self.spent_txos.get_mut(spent_idx).reserve_additional(tx.input.len());
|
||||||
|
for input in tx.input.iter() {
|
||||||
|
let taken = self.take_utxo(input.prev_hash, input.prev_index);
|
||||||
|
match taken {
|
||||||
|
Some(txo) => { self.spent_txos.get_mut(spent_idx).push(txo); }
|
||||||
|
None => { self.rewind(block); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skipped_genesis = true;
|
||||||
|
|
||||||
|
// Add outputs
|
||||||
|
// This will fail in the case of a duplicate transaction. This can only
|
||||||
|
// happen with coinbases, and in this case the block is invalid, -except-
|
||||||
|
// for two historic blocks which appeared in the blockchain before the
|
||||||
|
// dupes were noticed. See bitcoind commit `ab91bf39` and BIP30.
|
||||||
|
// TODO: add a unit test for these blocks.
|
||||||
|
if !self.add_utxos(tx) {
|
||||||
|
let blockhash = block.header.hash().le_hex_string();
|
||||||
|
if blockhash == "00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec".to_string() ||
|
||||||
|
blockhash == "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721".to_string() {
|
||||||
|
// For these specific blocks, overwrite the old UTXOs.
|
||||||
|
self.tree.delete(&tx.hash().as_uint128(), KEY_LEN);
|
||||||
|
self.add_utxos(tx);
|
||||||
|
} else {
|
||||||
|
// Otherwise fail the block
|
||||||
|
self.rewind(block);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we made it here, success!
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unapply the transactions contained in a block
|
||||||
|
pub fn rewind(&mut self, block: &Block) -> bool {
|
||||||
|
// Make sure we are rewinding the latest block
|
||||||
|
if self.last_hash != block.header.hash() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We deliberately do no error checking here, since we may be rewinding
|
||||||
|
// from halfway through the new block addition, in which case many of
|
||||||
|
// the utxos we try to remove may be missing; the ones we try to add,
|
||||||
|
// we stored ourselves when we removed them, so they won't be unaddable
|
||||||
|
// for any reason.
|
||||||
|
// Plus we don't care too much about efficiency, not many blocks should
|
||||||
|
// get rewound.
|
||||||
|
|
||||||
|
// Delete added txouts
|
||||||
|
let mut skipped_genesis = false;
|
||||||
|
for tx in block.txdata.iter() {
|
||||||
|
let txhash = tx.hash();
|
||||||
|
for n in range(0, tx.output.len()) {
|
||||||
|
// Just bomb out the whole transaction
|
||||||
|
self.take_utxo(txhash, n as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read deleted txouts -- note that we are trusting that these are
|
||||||
|
// in the same order in our cache as they were in the original block.
|
||||||
|
if skipped_genesis {
|
||||||
|
|
||||||
|
let mut extract_vec = vec![];
|
||||||
|
mem::swap(&mut extract_vec, self.spent_txos.get_mut(self.spent_idx as uint));
|
||||||
|
for (txo, inp) in extract_vec.move_iter().zip(tx.input.iter()) {
|
||||||
|
// Remove the tx's utxo list and patch the txo into place
|
||||||
|
let new_node =
|
||||||
|
match self.tree.delete(&inp.prev_hash.as_uint128(), KEY_LEN) {
|
||||||
|
Some(mut thinvec) => {
|
||||||
|
let old_len = thinvec.len() as u32;
|
||||||
|
if old_len < inp.prev_index + 1 {
|
||||||
|
thinvec.reserve(inp.prev_index + 1);
|
||||||
|
for i in range(old_len, inp.prev_index + 1) {
|
||||||
|
unsafe { thinvec.init(i as uint, None); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { *thinvec.get_mut(inp.prev_index as uint) = Some(txo); }
|
||||||
|
thinvec
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut thinvec = ThinVec::with_capacity(inp.prev_index + 1);
|
||||||
|
for i in range(0, inp.prev_index + 1) {
|
||||||
|
unsafe { thinvec.init(i as uint, None); }
|
||||||
|
}
|
||||||
|
unsafe { *thinvec.get_mut(inp.prev_index as uint) = Some(txo); }
|
||||||
|
thinvec
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Ram it back into the tree
|
||||||
|
self.tree.insert(&inp.prev_hash.as_uint128(), KEY_LEN, new_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skipped_genesis = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement mod the spent txo cache size
|
||||||
|
self.spent_idx = (self.spent_idx + self.spent_txos.len() as u64 - 1) %
|
||||||
|
self.spent_txos.len() as u64;
|
||||||
|
self.last_hash = block.header.prev_blockhash;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the hash of the last block added to the utxo set
|
||||||
|
pub fn last_hash(&self) -> Sha256dHash {
|
||||||
|
self.last_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the number of UTXOs in the set
|
||||||
|
pub fn n_utxos(&self) -> uint {
|
||||||
|
self.n_utxos as uint
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the number of UTXOs in the set
|
||||||
|
pub fn tree_size(&self) -> uint {
|
||||||
|
self.tree.node_count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::prelude::*;
|
||||||
|
use std::io::IoResult;
|
||||||
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
|
use blockdata::constants::genesis_block;
|
||||||
|
use blockdata::block::Block;
|
||||||
|
use blockdata::utxoset::UtxoSet;
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn utxoset_serialize_test() {
|
||||||
|
let mut empty_set = UtxoSet::new(genesis_block(), 100);
|
||||||
|
|
||||||
|
let new_block: Block = Serializable::deserialize("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000".from_hex().unwrap().iter().map(|n| *n)).unwrap();
|
||||||
|
|
||||||
|
// Make sure we can't add the block directly, since we are missing the inputs
|
||||||
|
assert!(!empty_set.update(&new_block));
|
||||||
|
assert_eq!(empty_set.n_utxos(), 0);
|
||||||
|
// Add the block manually so that we'll have some UTXOs for the rest of the test
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
empty_set.add_utxos(tx);
|
||||||
|
}
|
||||||
|
empty_set.last_hash = new_block.header.hash();
|
||||||
|
|
||||||
|
// Check that all the UTXOs were added
|
||||||
|
assert_eq!(empty_set.n_utxos(), 2);
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
let hash = tx.hash();
|
||||||
|
for (n, out) in tx.output.iter().enumerate() {
|
||||||
|
let n = n as u32;
|
||||||
|
assert_eq!(empty_set.get_utxo(hash, n), Some(&box out.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check again that we can't add the block, and that this doesn't mess up the
|
||||||
|
// existing UTXOs
|
||||||
|
assert!(!empty_set.update(&new_block));
|
||||||
|
assert_eq!(empty_set.n_utxos(), 2);
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
let hash = tx.hash();
|
||||||
|
for (n, out) in tx.output.iter().enumerate() {
|
||||||
|
let n = n as u32;
|
||||||
|
assert_eq!(empty_set.get_utxo(hash, n), Some(&box out.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize/deserialize the resulting UTXO set
|
||||||
|
let serial = empty_set.serialize();
|
||||||
|
assert_eq!(serial, empty_set.serialize_iter().collect());
|
||||||
|
|
||||||
|
let deserial: IoResult<UtxoSet> = Serializable::deserialize(serial.iter().map(|n| *n));
|
||||||
|
assert!(deserial.is_ok());
|
||||||
|
|
||||||
|
// Check that all outputs are there
|
||||||
|
let mut read_set = deserial.unwrap();
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
let hash = tx.hash();
|
||||||
|
|
||||||
|
for (n, out) in tx.output.iter().enumerate() {
|
||||||
|
let n = n as u32;
|
||||||
|
// Try taking non-existent UTXO
|
||||||
|
assert_eq!(read_set.take_utxo(hash, 100 + n), None);
|
||||||
|
// Check take of real UTXO
|
||||||
|
let ret = read_set.take_utxo(hash, n);
|
||||||
|
assert_eq!(ret, Some(box out.clone()));
|
||||||
|
// Try double-take
|
||||||
|
assert_eq!(read_set.take_utxo(hash, n), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let deserial_again: IoResult<UtxoSet> = Serializable::deserialize(serial.iter().map(|n| *n));
|
||||||
|
let mut read_again = deserial_again.unwrap();
|
||||||
|
assert!(read_again.rewind(&new_block));
|
||||||
|
assert_eq!(read_again.n_utxos(), 0);
|
||||||
|
for tx in new_block.txdata.iter() {
|
||||||
|
let hash = tx.hash();
|
||||||
|
|
||||||
|
for n in range(0, tx.output.len()) {
|
||||||
|
let n = n as u32;
|
||||||
|
let ret = read_again.take_utxo(hash, n);
|
||||||
|
assert_eq!(ret, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
#![macro_escape]
|
||||||
|
|
||||||
|
macro_rules! impl_serializable(
|
||||||
|
($thing:ident, $($field:ident),+) => (
|
||||||
|
impl Serializable for $thing {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut ret = vec![];
|
||||||
|
$( ret.extend(self.$field.serialize().move_iter()); )+
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||||
|
SerializeIter {
|
||||||
|
data_iter: None,
|
||||||
|
sub_iter_iter: box vec![ $( &self.$field as &Serializable, )+ ].move_iter(),
|
||||||
|
sub_iter: None,
|
||||||
|
sub_started: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<$thing> {
|
||||||
|
use util::misc::prepend_err;
|
||||||
|
Ok($thing {
|
||||||
|
$( $field: try!(prepend_err(stringify!($field), Serializable::deserialize(iter.by_ref()))), )+
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Rust Bitcoin Library
|
||||||
|
//!
|
||||||
|
//! This is a library for which supports the Bitcoin network protocol and associated
|
||||||
|
//! primitives. It is designed for Rust programs built to work with the Bitcoin
|
||||||
|
//! network.
|
||||||
|
//!
|
||||||
|
//! It is also written entirely in Rust to illustrate the benefits of strong type
|
||||||
|
//! safety, including ownership and lifetime, for financial and/or cryptographic
|
||||||
|
//! software.
|
||||||
|
//!
|
||||||
|
|
||||||
|
#![crate_name = "bitcoin"]
|
||||||
|
#![crate_type = "dylib"]
|
||||||
|
#![crate_type = "rlib"]
|
||||||
|
|
||||||
|
// Experimental features we need
|
||||||
|
#![feature(globs)]
|
||||||
|
#![feature(macro_rules)]
|
||||||
|
#![feature(overloaded_calls)]
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
|
#![comment = "Rust Bitcoin Library"]
|
||||||
|
#![license = "CC0"]
|
||||||
|
|
||||||
|
// Coding conventions
|
||||||
|
#![deny(non_uppercase_pattern_statics)]
|
||||||
|
#![deny(uppercase_variables)]
|
||||||
|
#![deny(non_camel_case_types)]
|
||||||
|
#![deny(non_snake_case_functions)]
|
||||||
|
#![deny(unused_mut)]
|
||||||
|
#![warn(missing_doc)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
extern crate collections;
|
||||||
|
extern crate core;
|
||||||
|
extern crate num;
|
||||||
|
extern crate rand;
|
||||||
|
extern crate rustrt;
|
||||||
|
extern crate serialize;
|
||||||
|
extern crate sync;
|
||||||
|
extern crate time;
|
||||||
|
|
||||||
|
extern crate crypto = "rust-crypto";
|
||||||
|
|
||||||
|
mod internal_macros;
|
||||||
|
pub mod macros;
|
||||||
|
pub mod network;
|
||||||
|
pub mod blockdata;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Macros
|
||||||
|
//!
|
||||||
|
//! Macros available to users of the Bitcoin library
|
||||||
|
|
||||||
|
#![macro_escape]
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! nu_select(
|
||||||
|
($($name:pat from $rx:expr => $code:expr),+) => ({
|
||||||
|
nu_select!{ $($name from $rx using recv => $code),+ }
|
||||||
|
});
|
||||||
|
($($name:pat from $rx:expr using $meth:ident => $code:expr),+) => ({
|
||||||
|
use rustrt::local::Local;
|
||||||
|
use rustrt::task::Task;
|
||||||
|
use sync::comm::Packet;
|
||||||
|
|
||||||
|
let task: Box<Task> = Local::take();
|
||||||
|
|
||||||
|
// Is anything already ready to receive? Grab it without waiting.
|
||||||
|
$(
|
||||||
|
if (&$rx as &Packet).can_recv() {
|
||||||
|
let $name = $rx.$meth();
|
||||||
|
$code
|
||||||
|
}
|
||||||
|
)else+
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Start selecting on as many as we need to before getting a bite.
|
||||||
|
// Keep count of how many, since we need to abort every selection
|
||||||
|
// that we started.
|
||||||
|
let mut started_count = 0;
|
||||||
|
let packets = [ $( &$rx as &Packet, )+ ];
|
||||||
|
task.deschedule(packets.len(), |task| {
|
||||||
|
match packets[started_count].start_selection(task) {
|
||||||
|
Ok(()) => {
|
||||||
|
started_count += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(task) => Err(task)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut i = -1;
|
||||||
|
$(
|
||||||
|
// Abort every one, but only react to the first
|
||||||
|
if { i += 1; i < started_count } &&
|
||||||
|
// If start_selection() failed, abort_selection() will fail too,
|
||||||
|
// but it still counts as "data available". FIXME: should we swap
|
||||||
|
// the following two conditions so that packets[started_count - 1].
|
||||||
|
// abort_selection() is never called?
|
||||||
|
(packets[i].abort_selection() || i == started_count - 1) {
|
||||||
|
// Abort the remainder, ignoring their return values
|
||||||
|
i += 1;
|
||||||
|
while i < started_count {
|
||||||
|
packets[i].abort_selection();
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
// React to the first
|
||||||
|
let $name = $rx.$meth();
|
||||||
|
$code
|
||||||
|
}
|
||||||
|
)else+
|
||||||
|
else {
|
||||||
|
println!("i = {} , started_count {}", i, started_count);
|
||||||
|
fail!("we didn't find the ready receiver, but we should have had one"); }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
// 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 network addresses
|
||||||
|
//!
|
||||||
|
//! This module defines the structures and functions needed to encode
|
||||||
|
//! network addresses in Bitcoin messages.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::io::{IoResult, standard_error, InvalidInput};
|
||||||
|
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
|
||||||
|
/// A message which can be sent on the Bitcoin network
|
||||||
|
pub struct Address {
|
||||||
|
/// Services provided by the peer whose address this is
|
||||||
|
pub services: u64,
|
||||||
|
/// Network byte-order ipv6 address, or ipv4-mapped ipv6 address
|
||||||
|
pub address: [u8, ..16],
|
||||||
|
/// Network port
|
||||||
|
pub port: u16
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Address {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut rv = vec!();
|
||||||
|
rv.extend(self.services.serialize().move_iter());
|
||||||
|
rv.extend(self.address.iter().map(|n| *n));
|
||||||
|
// Explicitly code the port since it needs to be big-endian
|
||||||
|
rv.extend([(self.port / 256) as u8, (self.port % 256) as u8].iter().map(|n| *n));
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Address> {
|
||||||
|
let ret = Address {
|
||||||
|
services: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
address: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
// Explicitly code the port since it needs to be big-endian
|
||||||
|
port: {
|
||||||
|
let b1 = iter.next();
|
||||||
|
let b2 = iter.next();
|
||||||
|
if b1.is_none() || b2.is_none() {
|
||||||
|
return Err(standard_error(InvalidInput));
|
||||||
|
}
|
||||||
|
(b1.unwrap() as u16) * 0x100 + (b2.unwrap() as u16)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Show for Address {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
// TODO: render services and hex-ize address
|
||||||
|
write!(f, "Address {{services: {}, address: {}, port: {}}}",
|
||||||
|
self.services, self.address.as_slice(), self.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_address_test() {
|
||||||
|
assert!(Address {
|
||||||
|
services: 1,
|
||||||
|
address: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1],
|
||||||
|
port: 8333
|
||||||
|
}.serialize() == Vec::from_slice([1u8, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1,
|
||||||
|
0x20, 0x8d]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_address_test() {
|
||||||
|
let mut addr: IoResult<Address> = Serializable::deserialize([1u8, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1,
|
||||||
|
0x20, 0x8d].iter().map(|n| *n));
|
||||||
|
assert!(addr.is_ok())
|
||||||
|
let full = addr.unwrap();
|
||||||
|
assert!(full.services == 1);
|
||||||
|
assert!(full.address == [0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1]);
|
||||||
|
assert!(full.port == 8333);
|
||||||
|
|
||||||
|
addr = Serializable::deserialize([1u8, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x0a, 0, 0, 1].iter().map(|n| *n));
|
||||||
|
assert!(addr.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Network constants
|
||||||
|
//!
|
||||||
|
//! This module provides various constants relating to the Bitcoin network
|
||||||
|
//! protocol, such as protocol versioning and magic header bytes.
|
||||||
|
//!
|
||||||
|
|
||||||
|
pub static MAGIC_BITCOIN: u32 = 0xD9B4BEF9;
|
||||||
|
|
||||||
|
pub static PROTOCOL_VERSION: u32 = 70001;
|
||||||
|
pub static SERVICES: u64 = 0;
|
||||||
|
pub static USER_AGENT: &'static str = "bitcoin-rust v0.1";
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Abstract Bitcoin listener
|
||||||
|
//!
|
||||||
|
//! This module defines a listener on the Bitcoin network which is able
|
||||||
|
//! to connect to a peer, send network messages, and receive Bitcoin data.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::io::{IoResult, standard_error, ConnectionFailed};
|
||||||
|
use std::io::timer;
|
||||||
|
|
||||||
|
use network::message::{NetworkMessage, Verack};
|
||||||
|
use network::socket::Socket;
|
||||||
|
use network::constants;
|
||||||
|
|
||||||
|
/// A message which can be sent on the Bitcoin network
|
||||||
|
pub trait Listener {
|
||||||
|
/// Return a string encoding of the peer's network address
|
||||||
|
fn peer<'a>(&'a self) -> &'a str;
|
||||||
|
/// Return the port we have connected to the peer on
|
||||||
|
fn port(&self) -> u16;
|
||||||
|
/// Main listen loop
|
||||||
|
fn start(&self) -> IoResult<(Receiver<NetworkMessage>, Socket)> {
|
||||||
|
// Open socket
|
||||||
|
let mut ret_sock = Socket::new(constants::MAGIC_BITCOIN);
|
||||||
|
match ret_sock.connect(self.peer(), self.port()) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(_) => return Err(standard_error(ConnectionFailed))
|
||||||
|
}
|
||||||
|
let mut sock = ret_sock.clone();
|
||||||
|
|
||||||
|
let (recv_tx, recv_rx) = channel();
|
||||||
|
|
||||||
|
// Send version message to peer
|
||||||
|
let version_message = try!(sock.version_message(0));
|
||||||
|
try!(sock.send_message(version_message));
|
||||||
|
|
||||||
|
// Message loop
|
||||||
|
spawn(proc() {
|
||||||
|
let mut handshake_complete = false;
|
||||||
|
let mut sock = sock;
|
||||||
|
loop {
|
||||||
|
// Receive new message
|
||||||
|
match sock.receive_message() {
|
||||||
|
Ok(payload) => {
|
||||||
|
// React to any network messages that affect our state.
|
||||||
|
match payload {
|
||||||
|
// Make an exception for verack since there is no response required
|
||||||
|
Verack => {
|
||||||
|
// TODO: when the timeout stuff in std::io::net::tcp is sorted out we should
|
||||||
|
// actually time out if the verack doesn't come in in time
|
||||||
|
if handshake_complete {
|
||||||
|
println!("Received second verack (peer is misbehaving)");
|
||||||
|
} else {
|
||||||
|
handshake_complete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
// We have to pass the message to the main thread for processing,
|
||||||
|
// unfortunately, because sipa says we have to handle everything
|
||||||
|
// in order.
|
||||||
|
recv_tx.send(payload);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Received error {:} when decoding message. Pausing for 1sec.", e);
|
||||||
|
timer::sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok((recv_rx, ret_sock))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Network message
|
||||||
|
//!
|
||||||
|
//! This module defines the `Message` traits which are used
|
||||||
|
//! for (de)serializing Bitcoin objects for transmission on the network. It
|
||||||
|
//! also defines (de)serialization routines for many primitives.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use collections::Vec;
|
||||||
|
use std::io::{IoError, IoResult, InvalidInput, OtherIoError, standard_error};
|
||||||
|
|
||||||
|
use blockdata::block;
|
||||||
|
use network::address::Address;
|
||||||
|
use network::message_network;
|
||||||
|
use network::message_blockdata;
|
||||||
|
use network::serialize::{Serializable, CheckedData};
|
||||||
|
use util::iter::FixedTakeable;
|
||||||
|
use util::misc::prepend_err;
|
||||||
|
|
||||||
|
/// Serializer for command string
|
||||||
|
#[deriving(PartialEq, Clone, Show)]
|
||||||
|
pub struct CommandString(pub String);
|
||||||
|
|
||||||
|
impl Serializable for CommandString {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let &CommandString(ref inner_str) = self;
|
||||||
|
let mut rawbytes = [0u8, ..12];
|
||||||
|
rawbytes.copy_from(inner_str.as_bytes().as_slice());
|
||||||
|
Vec::from_slice(rawbytes.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<CommandString> {
|
||||||
|
let mut fixiter = iter.fixed_take(12);
|
||||||
|
let rv: String = FromIterator::from_iter(fixiter.by_ref().filter_map(|u| if u > 0 { Some(u as char) } else { None }));
|
||||||
|
// Once we've read the string, run out the iterator
|
||||||
|
for _ in fixiter {}
|
||||||
|
match fixiter.is_err() {
|
||||||
|
false => Ok(CommandString(rv)),
|
||||||
|
true => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Network message
|
||||||
|
pub struct RawNetworkMessage {
|
||||||
|
/// Magic bytes to identify the network these messages are meant for
|
||||||
|
pub magic: u32,
|
||||||
|
/// The actual message data
|
||||||
|
pub payload: NetworkMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Show)]
|
||||||
|
/// A Network message payload. Proper documentation is available on the Bitcoin
|
||||||
|
/// wiki https://en.bitcoin.it/wiki/Protocol_specification
|
||||||
|
pub enum NetworkMessage{
|
||||||
|
/// `version`
|
||||||
|
Version(message_network::VersionMessage),
|
||||||
|
/// `verack`
|
||||||
|
Verack,
|
||||||
|
/// `addr`
|
||||||
|
Addr(Vec<(u32, Address)>),
|
||||||
|
/// `inv`
|
||||||
|
Inv(Vec<message_blockdata::Inventory>),
|
||||||
|
/// `getdata`
|
||||||
|
GetData(Vec<message_blockdata::Inventory>),
|
||||||
|
/// `notfound`
|
||||||
|
NotFound(Vec<message_blockdata::Inventory>),
|
||||||
|
/// `getblocks`
|
||||||
|
GetBlocks(message_blockdata::GetBlocksMessage),
|
||||||
|
/// `getheaders`
|
||||||
|
GetHeaders(message_blockdata::GetHeadersMessage),
|
||||||
|
// TODO: tx,
|
||||||
|
/// `block`
|
||||||
|
Block(block::Block),
|
||||||
|
/// `headers`
|
||||||
|
Headers(Vec<block::LoneBlockHeader>),
|
||||||
|
// TODO: getaddr,
|
||||||
|
// TODO: mempool,
|
||||||
|
// TODO: checkorder,
|
||||||
|
// TODO: submitorder,
|
||||||
|
// TODO: reply,
|
||||||
|
/// `ping`
|
||||||
|
Ping(u64),
|
||||||
|
/// `pong`
|
||||||
|
Pong(u64),
|
||||||
|
// TODO: reject,
|
||||||
|
// TODO: bloom filtering
|
||||||
|
// TODO: alert
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawNetworkMessage {
|
||||||
|
fn command(&self) -> String {
|
||||||
|
match self.payload {
|
||||||
|
Version(_) => "version",
|
||||||
|
Verack => "verack",
|
||||||
|
Addr(_) => "addr",
|
||||||
|
Inv(_) => "inv",
|
||||||
|
GetData(_) => "getdata",
|
||||||
|
NotFound(_) => "notfound",
|
||||||
|
GetBlocks(_) => "getblocks",
|
||||||
|
GetHeaders(_) => "getheaders",
|
||||||
|
Block(_) => "block",
|
||||||
|
Headers(_) => "headers",
|
||||||
|
Ping(_) => "ping",
|
||||||
|
Pong(_) => "pong"
|
||||||
|
}.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for RawNetworkMessage {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut ret = vec![];
|
||||||
|
ret.extend(self.magic.serialize().move_iter());
|
||||||
|
ret.extend(CommandString(self.command()).serialize().move_iter());
|
||||||
|
let payload_data = match self.payload {
|
||||||
|
Version(ref dat) => dat.serialize(),
|
||||||
|
Verack => vec![],
|
||||||
|
Addr(ref dat) => dat.serialize(),
|
||||||
|
Inv(ref dat) => dat.serialize(),
|
||||||
|
GetData(ref dat) => dat.serialize(),
|
||||||
|
NotFound(ref dat) => dat.serialize(),
|
||||||
|
GetBlocks(ref dat) => dat.serialize(),
|
||||||
|
GetHeaders(ref dat) => dat.serialize(),
|
||||||
|
Block(ref dat) => dat.serialize(),
|
||||||
|
Headers(ref dat) => dat.serialize(),
|
||||||
|
Ping(ref dat) => dat.serialize(),
|
||||||
|
Pong(ref dat) => dat.serialize()
|
||||||
|
};
|
||||||
|
ret.extend(CheckedData(payload_data).serialize().move_iter());
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<RawNetworkMessage> {
|
||||||
|
let magic = try!(prepend_err("magic", Serializable::deserialize(iter.by_ref())));
|
||||||
|
let CommandString(cmd): CommandString = try!(prepend_err("command", Serializable::deserialize(iter.by_ref())));
|
||||||
|
let CheckedData(raw_payload): CheckedData = try!(prepend_err("payload", Serializable::deserialize(iter.by_ref())));
|
||||||
|
let payload = match cmd.as_slice() {
|
||||||
|
"version" => Version(try!(prepend_err("version", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"verack" => Verack,
|
||||||
|
"addr" => Addr(try!(prepend_err("addr", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"inv" => Inv(try!(prepend_err("inv", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"getdata" => GetData(try!(prepend_err("getdata", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"notfound" => NotFound(try!(prepend_err("notfound", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"getblocks" => GetBlocks(try!(prepend_err("getblocks", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"getheaders" => GetHeaders(try!(prepend_err("getheaders", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"block" => Block(try!(prepend_err("block", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"headers" => Headers(try!(prepend_err("headers", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"ping" => Ping(try!(prepend_err("ping", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
"pong" => Ping(try!(prepend_err("pong", Serializable::deserialize(raw_payload.iter().map(|n| *n))))),
|
||||||
|
cmd => {
|
||||||
|
return Err(IoError {
|
||||||
|
kind: OtherIoError,
|
||||||
|
desc: "unknown message type",
|
||||||
|
detail: Some(format!("`{}` not recognized", cmd))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(RawNetworkMessage {
|
||||||
|
magic: magic,
|
||||||
|
payload: payload
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::io::IoResult;
|
||||||
|
|
||||||
|
use network::message::CommandString;
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_commandstring_test() {
|
||||||
|
let cs = CommandString(String::from_str("Andrew"));
|
||||||
|
assert_eq!(cs.serialize(), vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_commandstring_test() {
|
||||||
|
let cs: IoResult<CommandString> = Serializable::deserialize([0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0].iter().map(|n| *n));
|
||||||
|
assert!(cs.is_ok());
|
||||||
|
assert_eq!(cs.unwrap(), CommandString(String::from_str("Andrew")));
|
||||||
|
|
||||||
|
let short_cs: IoResult<CommandString> = Serializable::deserialize([0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0].iter().map(|n| *n));
|
||||||
|
assert!(short_cs.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: write tests for full network messages
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Blockdata network messages
|
||||||
|
//!
|
||||||
|
//! This module describes network messages which are used for passing
|
||||||
|
//! Bitcoin data (blocks and transactions) around.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::io::{IoResult, IoError, InvalidInput};
|
||||||
|
#[cfg(test)]
|
||||||
|
use serialize::hex::FromHex;
|
||||||
|
#[cfg(test)]
|
||||||
|
use util::hash::zero_hash;
|
||||||
|
|
||||||
|
use network::constants;
|
||||||
|
use network::serialize::{Serializable, SerializeIter};
|
||||||
|
use util::hash::Sha256dHash;
|
||||||
|
|
||||||
|
#[deriving(Clone, PartialEq, Show)]
|
||||||
|
/// The type of an inventory object
|
||||||
|
pub enum InvType {
|
||||||
|
/// Error --- these inventories can be ignored
|
||||||
|
InvError,
|
||||||
|
/// Transaction
|
||||||
|
InvTransaction,
|
||||||
|
/// Block
|
||||||
|
InvBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some simple messages
|
||||||
|
|
||||||
|
/// The `getblocks` message
|
||||||
|
#[deriving(Show)]
|
||||||
|
pub struct GetBlocksMessage {
|
||||||
|
/// The protocol version
|
||||||
|
pub version: u32,
|
||||||
|
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||||
|
/// reply with its longest known chain, starting from a locator hash
|
||||||
|
/// if possible and block 1 otherwise.
|
||||||
|
pub locator_hashes: Vec<Sha256dHash>,
|
||||||
|
/// References the block to stop at, or zero to just fetch the maximum 500 blocks
|
||||||
|
pub stop_hash: Sha256dHash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `getheaders` message
|
||||||
|
#[deriving(Show)]
|
||||||
|
pub struct GetHeadersMessage {
|
||||||
|
/// The protocol version
|
||||||
|
pub version: u32,
|
||||||
|
/// Locator hashes --- ordered newest to oldest. The remote peer will
|
||||||
|
/// reply with its longest known chain, starting from a locator hash
|
||||||
|
/// if possible and block 1 otherwise.
|
||||||
|
pub locator_hashes: Vec<Sha256dHash>,
|
||||||
|
/// References the header to stop at, or zero to just fetch the maximum 2000 headers
|
||||||
|
pub stop_hash: Sha256dHash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An inventory object --- a reference to a Bitcoin object
|
||||||
|
#[deriving(Clone, Show)]
|
||||||
|
pub struct Inventory {
|
||||||
|
/// The type of object that is referenced
|
||||||
|
pub inv_type: InvType,
|
||||||
|
/// The object's hash
|
||||||
|
pub hash: Sha256dHash
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetBlocksMessage {
|
||||||
|
/// Construct a new `getblocks` message
|
||||||
|
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetBlocksMessage {
|
||||||
|
GetBlocksMessage {
|
||||||
|
version: constants::PROTOCOL_VERSION,
|
||||||
|
locator_hashes: locator_hashes.clone(),
|
||||||
|
stop_hash: stop_hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_serializable!(GetBlocksMessage, version, locator_hashes, stop_hash)
|
||||||
|
|
||||||
|
impl GetHeadersMessage {
|
||||||
|
/// Construct a new `getheaders` message
|
||||||
|
pub fn new(locator_hashes: Vec<Sha256dHash>, stop_hash: Sha256dHash) -> GetHeadersMessage {
|
||||||
|
GetHeadersMessage {
|
||||||
|
version: constants::PROTOCOL_VERSION,
|
||||||
|
locator_hashes: locator_hashes,
|
||||||
|
stop_hash: stop_hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_serializable!(GetHeadersMessage, version, locator_hashes, stop_hash)
|
||||||
|
|
||||||
|
impl Serializable for Inventory {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let int_type: u32 = match self.inv_type {
|
||||||
|
InvError => 0,
|
||||||
|
InvTransaction => 1,
|
||||||
|
InvBlock => 2
|
||||||
|
};
|
||||||
|
let mut rv = vec!();
|
||||||
|
rv.extend(int_type.serialize().move_iter());
|
||||||
|
rv.extend(self.hash.serialize().move_iter());
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Inventory> {
|
||||||
|
let int_type: u32 = try!(Serializable::deserialize(iter.by_ref()));
|
||||||
|
Ok(Inventory {
|
||||||
|
inv_type: match int_type {
|
||||||
|
0 => InvError,
|
||||||
|
1 => InvTransaction,
|
||||||
|
2 => InvBlock,
|
||||||
|
_ => { return Err(IoError {
|
||||||
|
kind: InvalidInput,
|
||||||
|
desc: "bad inventory type field",
|
||||||
|
detail: None
|
||||||
|
})}
|
||||||
|
},
|
||||||
|
hash: try!(Serializable::deserialize(iter.by_ref()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn getblocks_message_test() {
|
||||||
|
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||||
|
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||||
|
|
||||||
|
let decode: IoResult<GetBlocksMessage> = Serializable::deserialize(from_sat.iter().map(|n| *n));
|
||||||
|
assert!(decode.is_ok());
|
||||||
|
let real_decode = decode.unwrap();
|
||||||
|
assert_eq!(real_decode.version, 70002);
|
||||||
|
assert_eq!(real_decode.locator_hashes.len(), 1);
|
||||||
|
assert_eq!(real_decode.locator_hashes[0].as_slice(), genhash.as_slice());
|
||||||
|
assert_eq!(real_decode.stop_hash.as_slice(), zero_hash().as_slice());
|
||||||
|
|
||||||
|
let reserialize = real_decode.serialize();
|
||||||
|
assert_eq!(reserialize.as_slice(), from_sat.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn getheaders_message_test() {
|
||||||
|
let from_sat = "72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap();
|
||||||
|
let genhash = "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".from_hex().unwrap();
|
||||||
|
|
||||||
|
let decode: IoResult<GetHeadersMessage> = Serializable::deserialize(from_sat.iter().map(|n| *n));
|
||||||
|
assert!(decode.is_ok());
|
||||||
|
let real_decode = decode.unwrap();
|
||||||
|
assert_eq!(real_decode.version, 70002);
|
||||||
|
assert_eq!(real_decode.locator_hashes.len(), 1);
|
||||||
|
assert_eq!(real_decode.locator_hashes[0].as_slice(), genhash.as_slice());
|
||||||
|
assert_eq!(real_decode.stop_hash.as_slice(), zero_hash().as_slice());
|
||||||
|
|
||||||
|
let reserialize = real_decode.serialize();
|
||||||
|
assert_eq!(reserialize.as_slice(), from_sat.as_slice());
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Network-related network messages
|
||||||
|
//!
|
||||||
|
//! This module defines network messages which describe peers and their
|
||||||
|
//! capabilities
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::io::IoResult;
|
||||||
|
#[cfg(test)]
|
||||||
|
use serialize::hex::FromHex;
|
||||||
|
|
||||||
|
use network::constants;
|
||||||
|
use network::address::Address;
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
use network::socket::Socket;
|
||||||
|
|
||||||
|
/// Some simple messages
|
||||||
|
|
||||||
|
/// The `version` message
|
||||||
|
#[deriving(Show)]
|
||||||
|
pub struct VersionMessage {
|
||||||
|
/// The P2P network protocol version
|
||||||
|
pub version: u32,
|
||||||
|
/// A bitmask describing the services supported by this node
|
||||||
|
pub services: u64,
|
||||||
|
/// The time at which the `version` message was sent
|
||||||
|
pub timestamp: i64,
|
||||||
|
/// The network address of the peer receiving the message
|
||||||
|
pub receiver: Address,
|
||||||
|
/// The network address of the peer sending the message
|
||||||
|
pub sender: Address,
|
||||||
|
/// A random nonce used to detect loops in the network
|
||||||
|
pub nonce: u64,
|
||||||
|
/// A string describing the peer's software
|
||||||
|
pub user_agent: String,
|
||||||
|
/// The height of the maxmimum-work blockchain that the peer is aware of
|
||||||
|
pub start_height: i32,
|
||||||
|
/// Whether the receiving peer should relay messages to the sender; used
|
||||||
|
/// if the sender is bandwidth-limited and would like to support bloom
|
||||||
|
/// filtering. Defaults to true.
|
||||||
|
pub relay: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VersionMessage {
|
||||||
|
// TODO: we have fixed services and relay to 0
|
||||||
|
/// Constructs a new `version` message
|
||||||
|
pub fn new(timestamp: i64, mut socket: Socket, nonce: u64, start_height: i32) -> IoResult<VersionMessage> {
|
||||||
|
let recv_addr = socket.receiver_address();
|
||||||
|
let send_addr = socket.sender_address();
|
||||||
|
// If we are not connected, we might not be able to get these address.s
|
||||||
|
match recv_addr {
|
||||||
|
Err(e) => { return Err(e); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match send_addr {
|
||||||
|
Err(e) => { return Err(e); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(VersionMessage {
|
||||||
|
version: constants::PROTOCOL_VERSION,
|
||||||
|
services: socket.services,
|
||||||
|
timestamp: timestamp,
|
||||||
|
receiver: recv_addr.unwrap(),
|
||||||
|
sender: send_addr.unwrap(),
|
||||||
|
nonce: nonce,
|
||||||
|
user_agent: socket.user_agent,
|
||||||
|
start_height: start_height,
|
||||||
|
relay: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for VersionMessage {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut rv = vec!();
|
||||||
|
rv.extend(self.version.serialize().move_iter());
|
||||||
|
rv.extend(self.services.serialize().move_iter());
|
||||||
|
rv.extend(self.timestamp.serialize().move_iter());
|
||||||
|
rv.extend(self.receiver.serialize().move_iter());
|
||||||
|
rv.extend(self.sender.serialize().move_iter());
|
||||||
|
rv.extend(self.nonce.serialize().move_iter());
|
||||||
|
rv.extend(self.user_agent.serialize().move_iter());
|
||||||
|
rv.extend(self.start_height.serialize().move_iter());
|
||||||
|
if self.version >= 70001 {
|
||||||
|
rv.extend(self.relay.serialize().move_iter());
|
||||||
|
}
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<VersionMessage> {
|
||||||
|
Ok(VersionMessage {
|
||||||
|
version: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
services: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
timestamp: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
receiver: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
sender: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
nonce: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
user_agent: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
start_height: try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
relay: try!(Serializable::deserialize(iter.by_ref()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn version_message_test() {
|
||||||
|
// This message is from my satoshi node, morning of May 27 2014
|
||||||
|
let from_sat = "721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001".from_hex().unwrap();
|
||||||
|
|
||||||
|
let decode: IoResult<VersionMessage> = Serializable::deserialize(from_sat.iter().map(|n| *n));
|
||||||
|
assert!(decode.is_ok());
|
||||||
|
let real_decode = decode.unwrap();
|
||||||
|
assert_eq!(real_decode.version, 70002);
|
||||||
|
assert_eq!(real_decode.services, 1);
|
||||||
|
assert_eq!(real_decode.timestamp, 1401217254);
|
||||||
|
// address decodes should be covered by Address tests
|
||||||
|
assert_eq!(real_decode.nonce, 16735069437859780935);
|
||||||
|
assert_eq!(real_decode.user_agent, String::from_str("/Satoshi:0.9.99/"));
|
||||||
|
assert_eq!(real_decode.start_height, 302892);
|
||||||
|
assert_eq!(real_decode.relay, true);
|
||||||
|
|
||||||
|
let reserialize = real_decode.serialize();
|
||||||
|
assert_eq!(reserialize.as_slice(), from_sat.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Network Support
|
||||||
|
//!
|
||||||
|
//! This module defines support for (de)serialization and network transport
|
||||||
|
//! of Bitcoin data and network messages.
|
||||||
|
//!
|
||||||
|
|
||||||
|
pub mod constants;
|
||||||
|
pub mod socket;
|
||||||
|
pub mod serialize;
|
||||||
|
|
||||||
|
pub mod address;
|
||||||
|
pub mod listener;
|
||||||
|
pub mod message;
|
||||||
|
pub mod message_blockdata;
|
||||||
|
pub mod message_network;
|
||||||
|
|
|
@ -0,0 +1,722 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Network Serialization
|
||||||
|
//!
|
||||||
|
//! This module defines the `Serializable` trait which is used for
|
||||||
|
//! (de)serializing Bitcoin objects for transmission on the network.
|
||||||
|
//! It also defines (de)serialization routines for many primitives.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use collections::Vec;
|
||||||
|
use collections::bitv::{Bitv, from_bytes};
|
||||||
|
use std::io::{IoError, IoResult, InvalidInput, OtherIoError, standard_error};
|
||||||
|
use std::io::{BufferedReader, BufferedWriter, File, Truncate, Write};
|
||||||
|
use std::io::fs::rename;
|
||||||
|
use std::mem::transmute;
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
use util::iter::{FixedTake, FixedTakeable, NullIterator};
|
||||||
|
use util::hash::Sha256dHash;
|
||||||
|
use util::thinvec::ThinVec;
|
||||||
|
|
||||||
|
/// An iterator which returns serialized data one byte at a time
|
||||||
|
pub struct SerializeIter<'a> {
|
||||||
|
/// Iterator over actual data
|
||||||
|
pub data_iter: Option<Box<Iterator<u8>>>,
|
||||||
|
/// Objects which are serialized through their own `SerializeIter`s
|
||||||
|
pub sub_iter_iter: Box<Iterator<&'a Serializable>>,
|
||||||
|
/// Current subiterator
|
||||||
|
pub sub_iter: Option<Box<SerializeIter<'a>>>,
|
||||||
|
/// Whether we have started using sub_iter_iter
|
||||||
|
pub sub_started: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator<u8> for SerializeIter<'a> {
|
||||||
|
fn next(&mut self) -> Option<u8> {
|
||||||
|
let mut ret = None;
|
||||||
|
// Try to use the data iterator
|
||||||
|
if self.data_iter.is_some() {
|
||||||
|
// Unwrap the current data iterator to use it
|
||||||
|
let mut it = self.data_iter.take_unwrap();
|
||||||
|
ret = it.next();
|
||||||
|
// Delete the current data iterator if it's exhausted, by putting
|
||||||
|
// it back only when it's -not- exhausted
|
||||||
|
if ret.is_some() { self.data_iter = Some(it); }
|
||||||
|
}
|
||||||
|
// Failing that, start using the subobject iterator
|
||||||
|
if ret.is_none() && !self.sub_started {
|
||||||
|
// Unwrap the current data iterator to use it
|
||||||
|
self.sub_started = true;
|
||||||
|
self.sub_iter = self.sub_iter_iter.next().map(|obj| box obj.serialize_iter());
|
||||||
|
}
|
||||||
|
// If it doesn't work, find one that does
|
||||||
|
while ret.is_none() && self.sub_iter.is_some() {
|
||||||
|
let mut iter = self.sub_iter.take_unwrap();
|
||||||
|
ret = iter.next();
|
||||||
|
self.sub_iter = if ret.is_none() {
|
||||||
|
self.sub_iter_iter.next().map(|obj| box obj.serialize_iter())
|
||||||
|
} else {
|
||||||
|
Some(iter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Eventually we got Some(u8) --- or None and we're exhausted
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A string which must be encoded as 12 bytes, used in network message headers
|
||||||
|
|
||||||
|
|
||||||
|
#[deriving(PartialEq, Clone, Show)]
|
||||||
|
/// Data which must be preceded by a 4-byte checksum
|
||||||
|
pub struct CheckedData(pub Vec<u8>);
|
||||||
|
|
||||||
|
/// An object which can be (de)serialized. If the object can be sent on the
|
||||||
|
/// Bitcoin network, the serialization must be the standard p2p network
|
||||||
|
/// serialization.
|
||||||
|
pub trait Serializable {
|
||||||
|
/// Turn an object into a bytestring that can be put on the wire
|
||||||
|
fn serialize(&self) -> Vec<u8>;
|
||||||
|
/// Serialize an object, returning an iterator rather than complete vector
|
||||||
|
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||||
|
SerializeIter {
|
||||||
|
data_iter: Some(box self.serialize().move_iter() as Box<Iterator<u8>>),
|
||||||
|
sub_iter_iter: box NullIterator::<&Serializable>::new(),
|
||||||
|
sub_iter: None,
|
||||||
|
sub_started: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Read an object off the wire
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Self>;
|
||||||
|
/// Obtain a hash of the object
|
||||||
|
fn hash(&self) -> Sha256dHash {
|
||||||
|
Sha256dHash::from_data(self.serialize().as_slice())
|
||||||
|
}
|
||||||
|
/// Dump the object to a file
|
||||||
|
fn serialize_file(&self, p: &Path) -> IoResult<()> {
|
||||||
|
let tmp_path = p.with_extension("0");
|
||||||
|
{
|
||||||
|
let file = File::open_mode(&tmp_path, Truncate, Write);
|
||||||
|
let mut writer = BufferedWriter::new(file);
|
||||||
|
for ch in self.serialize_iter() {
|
||||||
|
try!(writer.write_u8(ch));
|
||||||
|
}
|
||||||
|
try!(writer.flush());
|
||||||
|
}
|
||||||
|
rename(&tmp_path, p)
|
||||||
|
}
|
||||||
|
/// Read the object from a file
|
||||||
|
fn deserialize_file(p: &Path) -> IoResult<Self> {
|
||||||
|
let file = try!(File::open(p));
|
||||||
|
let mut reader = BufferedReader::new(file);
|
||||||
|
let mut error: IoResult<u8> = Ok(0);
|
||||||
|
// This is kinda a hacky way to catch file read errors
|
||||||
|
let ret = Serializable::deserialize(reader.bytes().filter_map(|res| {
|
||||||
|
if res.is_err() {
|
||||||
|
error = res;
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
res.ok()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
// Return file error if there was one, else parse error
|
||||||
|
match error {
|
||||||
|
Ok(_) => ret,
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A variable-length unsigned integer
|
||||||
|
#[deriving(PartialEq, Show)]
|
||||||
|
pub enum VarInt {
|
||||||
|
/// 8-bit int
|
||||||
|
VarU8(u8),
|
||||||
|
/// 16-bit int
|
||||||
|
VarU16(u16),
|
||||||
|
/// 32-bit int
|
||||||
|
VarU32(u32),
|
||||||
|
/// 64-bit int
|
||||||
|
VarU64(u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
/// Convert a Rust uint to a Bitcoin network Varint
|
||||||
|
pub fn u64_to_varint(n: u64) -> VarInt {
|
||||||
|
match n {
|
||||||
|
n if n < 0xFD => VarU8(n as u8),
|
||||||
|
n if n <= 0xFFFF => VarU16(n as u16),
|
||||||
|
n if n <= 0xFFFFFFFF => VarU32(n as u32),
|
||||||
|
n => VarU64(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a Bitcoin network Varint to a Rust uint
|
||||||
|
pub fn varint_to_u64(n: VarInt) -> u64 {
|
||||||
|
match n {
|
||||||
|
VarU8(m) => m as u64,
|
||||||
|
VarU16(m) => m as u64,
|
||||||
|
VarU32(m) => m as u64,
|
||||||
|
VarU64(m) => m,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_uint_le<I: Iterator<u8>>(mut iter: FixedTake<I>) -> Option<u64> {
|
||||||
|
let (rv, _) = iter.fold((0u64, 1u64), |(old, mult), next| (old + next as u64 * mult, mult * 0x100));
|
||||||
|
match iter.is_err() {
|
||||||
|
false => Some(rv),
|
||||||
|
true => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do a double-SHA256 on some data and return the first 4 bytes
|
||||||
|
fn sha2_checksum(data: &[u8]) -> u32 {
|
||||||
|
let checksum = Sha256dHash::from_data(data);
|
||||||
|
read_uint_le(checksum.as_slice().iter().map(|n| *n).fixed_take(4)).unwrap() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Primitives
|
||||||
|
impl Serializable for bool {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
if *self { Vec::from_slice(&[1u8]) } else { Vec::from_slice(&[0u8]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<bool> {
|
||||||
|
match iter.next() {
|
||||||
|
Some(u) => Ok(u != 0),
|
||||||
|
None => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for u8 {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
Vec::from_slice(&[*self])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<u8> {
|
||||||
|
match iter.next() {
|
||||||
|
Some(u) => Ok(u as u8),
|
||||||
|
None => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for u16 {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
unsafe { Vec::from_slice(transmute::<_, [u8, ..2]>(self.to_le())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<u16> {
|
||||||
|
match read_uint_le(iter.fixed_take(2)) {
|
||||||
|
Some(u) => Ok(u as u16),
|
||||||
|
None => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for u32 {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
unsafe { Vec::from_slice(transmute::<_, [u8, ..4]>(self.to_le())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<u32> {
|
||||||
|
match read_uint_le(iter.fixed_take(4)) {
|
||||||
|
Some(u) => Ok(u as u32),
|
||||||
|
None => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for i32 {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
unsafe { Vec::from_slice(transmute::<_, [u8, ..4]>(self.to_le())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<i32> {
|
||||||
|
match read_uint_le(iter.fixed_take(4)) {
|
||||||
|
Some(u) => Ok(u as i32),
|
||||||
|
None => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for u64 {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
unsafe { Vec::from_slice(transmute::<_, [u8, ..8]>(self.to_le())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<u64> {
|
||||||
|
match read_uint_le(iter.fixed_take(8)) {
|
||||||
|
Some(u) => Ok(u as u64),
|
||||||
|
None => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for i64 {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
unsafe { Vec::from_slice(transmute::<_, [u8, ..8]>(self.to_le())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<i64> {
|
||||||
|
match read_uint_le(iter.fixed_take(8)) {
|
||||||
|
Some(u) => Ok(u as i64),
|
||||||
|
None => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for VarInt {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
match *self {
|
||||||
|
VarU8(n) => Vec::from_slice(&[n]),
|
||||||
|
VarU16(n) => { let mut rv = n.serialize(); rv.unshift(0xFD); rv },
|
||||||
|
VarU32(n) => { let mut rv = n.serialize(); rv.unshift(0xFE); rv },
|
||||||
|
VarU64(n) => { let mut rv = n.serialize(); rv.unshift(0xFF); rv },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<VarInt> {
|
||||||
|
match iter.next() {
|
||||||
|
Some(n) if n < 0xFD => Ok(VarU8(n)),
|
||||||
|
Some(n) if n == 0xFD => Ok(VarU16(try!(Serializable::deserialize(iter)))),
|
||||||
|
Some(n) if n == 0xFE => Ok(VarU32(try!(Serializable::deserialize(iter)))),
|
||||||
|
Some(n) if n == 0xFF => Ok(VarU64(try!(Serializable::deserialize(iter)))),
|
||||||
|
_ => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! serialize_fixvec(
|
||||||
|
($($size:expr),+) => (
|
||||||
|
$(
|
||||||
|
impl Serializable for [u8, ..$size] {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
Vec::from_slice(self.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<[u8, ..$size]> {
|
||||||
|
let mut v = [0u8, ..$size];
|
||||||
|
let mut fixiter = iter.fixed_take($size);
|
||||||
|
let mut n = 0;
|
||||||
|
for ch in fixiter {
|
||||||
|
v[n] = ch;
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
match fixiter.is_err() {
|
||||||
|
false => Ok(v),
|
||||||
|
true => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fixvec() {
|
||||||
|
$(
|
||||||
|
let vec = [5u8, ..$size];
|
||||||
|
let short_vec = [5u8, ..($size - 1)];
|
||||||
|
assert_eq!(vec.as_slice(), vec.serialize().as_slice());
|
||||||
|
|
||||||
|
let decode: IoResult<[u8, ..$size]> = Serializable::deserialize(vec.iter().map(|n| *n));
|
||||||
|
let short_decode: IoResult<[u8, ..$size]> = Serializable::deserialize(short_vec.iter().map(|n| *n));
|
||||||
|
|
||||||
|
assert!(decode.is_ok());
|
||||||
|
assert!(short_decode.is_err());
|
||||||
|
assert_eq!(decode.unwrap().as_slice(), vec.as_slice());
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
);
|
||||||
|
)
|
||||||
|
// we need to do this in one call so that we can do a test for
|
||||||
|
// every value; we can't define a new test fn for each invocation
|
||||||
|
// because there are no gensyms.
|
||||||
|
serialize_fixvec!(4, 8, 12, 16, 32)
|
||||||
|
|
||||||
|
impl Serializable for CheckedData {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let &CheckedData(ref data) = self;
|
||||||
|
let mut ret = (data.len() as u32).serialize();
|
||||||
|
ret.extend(sha2_checksum(data.as_slice()).serialize().move_iter());
|
||||||
|
ret.extend(data.iter().map(|n| *n));
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<CheckedData> {
|
||||||
|
let length: u32 = try!(Serializable::deserialize(iter.by_ref()));
|
||||||
|
let checksum: u32 = try!(Serializable::deserialize(iter.by_ref()));
|
||||||
|
|
||||||
|
let mut fixiter = iter.fixed_take(length as uint);
|
||||||
|
let v: Vec<u8> = FromIterator::from_iter(fixiter.by_ref());
|
||||||
|
if fixiter.is_err() {
|
||||||
|
return Err(IoError {
|
||||||
|
kind: InvalidInput,
|
||||||
|
desc: "overrun",
|
||||||
|
detail: Some(format!("data length given as {:}, but read fewer bytes", length))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_checksum = sha2_checksum(v.as_slice());
|
||||||
|
if checksum == expected_checksum {
|
||||||
|
Ok(CheckedData(v))
|
||||||
|
} else {
|
||||||
|
Err(IoError {
|
||||||
|
kind: OtherIoError,
|
||||||
|
desc: "bad checksum",
|
||||||
|
detail: Some(format!("checksum {:4x} did not match expected {:4x}", checksum, expected_checksum)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for String {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut rv = u64_to_varint(self.len() as u64).serialize();
|
||||||
|
rv.push_all(self.as_bytes());
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<String> {
|
||||||
|
let length: VarInt = try!(Serializable::deserialize(iter.by_ref()));
|
||||||
|
let mut fixiter = iter.fixed_take(varint_to_u64(length) as uint);
|
||||||
|
let rv: String = FromIterator::from_iter(fixiter.by_ref().map(|u| u as char));
|
||||||
|
match fixiter.is_err() {
|
||||||
|
false => Ok(rv),
|
||||||
|
true => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serializable> Serializable for Vec<T> {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let n_elems = u64_to_varint(self.len() as u64);
|
||||||
|
let mut rv = n_elems.serialize();
|
||||||
|
for elem in self.iter() {
|
||||||
|
rv.extend(elem.serialize().move_iter());
|
||||||
|
}
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Vec<T>> {
|
||||||
|
let mut n_elems = varint_to_u64(try!(Serializable::deserialize(iter.by_ref())));
|
||||||
|
let mut v: Vec<T> = vec![];
|
||||||
|
while n_elems > 0 {
|
||||||
|
v.push(try!(Serializable::deserialize(iter.by_ref())));
|
||||||
|
n_elems -= 1;
|
||||||
|
}
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serializable> Serializable for ThinVec<T> {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let n_elems = u64_to_varint(self.len() as u64);
|
||||||
|
let mut rv = n_elems.serialize();
|
||||||
|
for elem in self.iter() {
|
||||||
|
rv.extend(elem.serialize().move_iter());
|
||||||
|
}
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<ThinVec<T>> {
|
||||||
|
let n_elems = varint_to_u64(try!(Serializable::deserialize(iter.by_ref())));
|
||||||
|
assert!(n_elems < u32::MAX as u64);
|
||||||
|
|
||||||
|
let mut v: ThinVec<T> = ThinVec::with_capacity(n_elems as u32);
|
||||||
|
for i in range(0, n_elems) {
|
||||||
|
unsafe {
|
||||||
|
v.init(i as uint, try!(Serializable::deserialize(iter.by_ref())));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Serializable, U:Serializable> Serializable for (T, U) {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let &(ref self1, ref self2) = self;
|
||||||
|
let mut ret = vec![];
|
||||||
|
ret.extend(self1.serialize().move_iter());
|
||||||
|
ret.extend(self2.serialize().move_iter());
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<(T, U)> {
|
||||||
|
Ok((try!(Serializable::deserialize(iter.by_ref())),
|
||||||
|
try!(Serializable::deserialize(iter.by_ref()))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Serializable+'static> Serializable for Option<T> {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
match self {
|
||||||
|
&Some(ref dat) => {
|
||||||
|
let mut ret = vec![1];
|
||||||
|
ret.extend(dat.serialize().move_iter());
|
||||||
|
ret
|
||||||
|
},
|
||||||
|
&None => vec![0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||||
|
match self {
|
||||||
|
&Some(ref dat) => SerializeIter {
|
||||||
|
data_iter: Some(box Some(1u8).move_iter() as Box<Iterator<u8>>),
|
||||||
|
sub_iter_iter: box vec![ dat as &Serializable ].move_iter(),
|
||||||
|
sub_iter: None,
|
||||||
|
sub_started: false
|
||||||
|
},
|
||||||
|
&None => SerializeIter {
|
||||||
|
data_iter: Some(box Some(0u8).move_iter() as Box<Iterator<u8>>),
|
||||||
|
sub_iter_iter: box NullIterator::<&Serializable>::new(),
|
||||||
|
sub_iter: None,
|
||||||
|
sub_started: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Option<T>> {
|
||||||
|
match iter.next() {
|
||||||
|
Some(0) => Ok(None),
|
||||||
|
Some(1) => Ok(Some(try!(Serializable::deserialize(iter)))),
|
||||||
|
_ => Err(standard_error(InvalidInput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T:Serializable> Serializable for Box<T> {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
(**self).serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||||
|
(**self).serialize_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Box<T>> {
|
||||||
|
let ret: T = try!(Serializable::deserialize(iter));
|
||||||
|
Ok(box ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Bitv {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let n_elems = u64_to_varint(self.len() as u64);
|
||||||
|
let mut rv = n_elems.serialize();
|
||||||
|
for elem in self.to_bytes().iter() {
|
||||||
|
rv.extend(elem.serialize().move_iter());
|
||||||
|
}
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<Bitv> {
|
||||||
|
let n_elems = varint_to_u64(try!(Serializable::deserialize(iter.by_ref())));
|
||||||
|
let mut v: Vec<u8> = vec![];
|
||||||
|
for _ in range(0, (n_elems + 7) / 8) {
|
||||||
|
v.push(try!(Serializable::deserialize(iter.by_ref())));
|
||||||
|
}
|
||||||
|
let mut ret = from_bytes(v.as_slice());
|
||||||
|
ret.truncate(n_elems as uint); // from_bytes will round up to 8
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_iter_test() {
|
||||||
|
assert_eq!(true.serialize(), true.serialize_iter().collect());
|
||||||
|
assert_eq!(1u8.serialize(), 1u8.serialize_iter().collect());
|
||||||
|
assert_eq!(300u32.serialize(), 300u32.serialize_iter().collect());
|
||||||
|
assert_eq!(20u64.serialize(), 20u64.serialize_iter().collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_int_test() {
|
||||||
|
// bool
|
||||||
|
assert_eq!(false.serialize(), Vec::from_slice([0u8]));
|
||||||
|
assert_eq!(true.serialize(), Vec::from_slice([1u8]));
|
||||||
|
// u8
|
||||||
|
assert_eq!(1u8.serialize(), Vec::from_slice([1u8]));
|
||||||
|
assert_eq!(0u8.serialize(), Vec::from_slice([0u8]));
|
||||||
|
assert_eq!(255u8.serialize(), Vec::from_slice([255u8]));
|
||||||
|
// u16
|
||||||
|
assert_eq!(1u16.serialize(), Vec::from_slice([1u8, 0]));
|
||||||
|
assert_eq!(256u16.serialize(), Vec::from_slice([0u8, 1]));
|
||||||
|
assert_eq!(5000u16.serialize(), Vec::from_slice([136u8, 19]));
|
||||||
|
// u32
|
||||||
|
assert_eq!(1u32.serialize(), Vec::from_slice([1u8, 0, 0, 0]));
|
||||||
|
assert_eq!(256u32.serialize(), Vec::from_slice([0u8, 1, 0, 0]));
|
||||||
|
assert_eq!(5000u32.serialize(), Vec::from_slice([136u8, 19, 0, 0]));
|
||||||
|
assert_eq!(500000u32.serialize(), Vec::from_slice([32u8, 161, 7, 0]));
|
||||||
|
assert_eq!(168430090u32.serialize(), Vec::from_slice([10u8, 10, 10, 10]));
|
||||||
|
// TODO: test negative numbers
|
||||||
|
assert_eq!(1i32.serialize(), Vec::from_slice([1u8, 0, 0, 0]));
|
||||||
|
assert_eq!(256i32.serialize(), Vec::from_slice([0u8, 1, 0, 0]));
|
||||||
|
assert_eq!(5000i32.serialize(), Vec::from_slice([136u8, 19, 0, 0]));
|
||||||
|
assert_eq!(500000i32.serialize(), Vec::from_slice([32u8, 161, 7, 0]));
|
||||||
|
assert_eq!(168430090i32.serialize(), Vec::from_slice([10u8, 10, 10, 10]));
|
||||||
|
// u64
|
||||||
|
assert_eq!(1u64.serialize(), Vec::from_slice([1u8, 0, 0, 0, 0, 0, 0, 0]));
|
||||||
|
assert_eq!(256u64.serialize(), Vec::from_slice([0u8, 1, 0, 0, 0, 0, 0, 0]));
|
||||||
|
assert_eq!(5000u64.serialize(), Vec::from_slice([136u8, 19, 0, 0, 0, 0, 0, 0]));
|
||||||
|
assert_eq!(500000u64.serialize(), Vec::from_slice([32u8, 161, 7, 0, 0, 0, 0, 0]));
|
||||||
|
assert_eq!(723401728380766730u64.serialize(), Vec::from_slice([10u8, 10, 10, 10, 10, 10, 10, 10]));
|
||||||
|
// TODO: test negative numbers
|
||||||
|
assert_eq!(1i64.serialize(), Vec::from_slice([1u8, 0, 0, 0, 0, 0, 0, 0]));
|
||||||
|
assert_eq!(256i64.serialize(), Vec::from_slice([0u8, 1, 0, 0, 0, 0, 0, 0]));
|
||||||
|
assert_eq!(5000i64.serialize(), Vec::from_slice([136u8, 19, 0, 0, 0, 0, 0, 0]));
|
||||||
|
assert_eq!(500000i64.serialize(), Vec::from_slice([32u8, 161, 7, 0, 0, 0, 0, 0]));
|
||||||
|
assert_eq!(723401728380766730i64.serialize(), Vec::from_slice([10u8, 10, 10, 10, 10, 10, 10, 10]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_varint_test() {
|
||||||
|
assert_eq!(VarU8(10).serialize(), Vec::from_slice([10u8]));
|
||||||
|
assert_eq!(VarU8(0xFC).serialize(), Vec::from_slice([0xFCu8]));
|
||||||
|
assert_eq!(VarU16(0xFD).serialize(), Vec::from_slice([0xFDu8, 0xFD, 0]));
|
||||||
|
assert_eq!(VarU16(0xFFF).serialize(), Vec::from_slice([0xFDu8, 0xFF, 0xF]));
|
||||||
|
assert_eq!(VarU32(0xF0F0F0F).serialize(), Vec::from_slice([0xFEu8, 0xF, 0xF, 0xF, 0xF]));
|
||||||
|
assert_eq!(VarU64(0xF0F0F0F0F0E0).serialize(), Vec::from_slice([0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_vector_test() {
|
||||||
|
assert_eq!(Vec::from_slice([1u8, 2, 3]).serialize(), Vec::from_slice([3u8, 1, 2, 3]));
|
||||||
|
// TODO: test vectors of more interesting objects
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_strbuf_test() {
|
||||||
|
assert_eq!(String::from_str("Andrew").serialize(), Vec::from_slice([6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_checkeddata_test() {
|
||||||
|
let cd = CheckedData(vec![1u8, 2, 3, 4, 5]);
|
||||||
|
assert_eq!(cd.serialize(), vec![5, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_box_test() {
|
||||||
|
assert_eq!((box 1u8).serialize(), vec![1u8]);
|
||||||
|
assert_eq!((box 1u16).serialize(), vec![1u8, 0]);
|
||||||
|
assert_eq!((box 1u64).serialize(), vec![1u8, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_option_test() {
|
||||||
|
let none: Option<u8> = None;
|
||||||
|
let none_ser = none.serialize();
|
||||||
|
let some_ser = Some(0xFFu8).serialize();
|
||||||
|
assert_eq!(none_ser, vec![0]);
|
||||||
|
assert_eq!(some_ser, vec![1, 0xFF]);
|
||||||
|
|
||||||
|
assert_eq!(none.serialize(), none.serialize_iter().collect());
|
||||||
|
assert_eq!(Some(true).serialize(), Some(true).serialize_iter().collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_bitv_test() {
|
||||||
|
let bv = Bitv::with_capacity(10, true);
|
||||||
|
assert_eq!(bv.serialize(), vec![10, 0xFF, 0xC0]);
|
||||||
|
assert_eq!(bv.serialize(), bv.serialize_iter().collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_int_test() {
|
||||||
|
// bool
|
||||||
|
assert_eq!(Serializable::deserialize([58u8, 0].iter().map(|n| *n)), Ok(true));
|
||||||
|
assert_eq!(Serializable::deserialize([58u8].iter().map(|n| *n)), Ok(true));
|
||||||
|
assert_eq!(Serializable::deserialize([1u8].iter().map(|n| *n)), Ok(true));
|
||||||
|
assert_eq!(Serializable::deserialize([0u8].iter().map(|n| *n)), Ok(false));
|
||||||
|
assert_eq!(Serializable::deserialize([0u8, 1].iter().map(|n| *n)), Ok(false));
|
||||||
|
|
||||||
|
// u8
|
||||||
|
assert_eq!(Serializable::deserialize([58u8].iter().map(|n| *n)), Ok(58u8));
|
||||||
|
|
||||||
|
// u16
|
||||||
|
assert_eq!(Serializable::deserialize([0x01u8, 0x02].iter().map(|n| *n)), Ok(0x0201u16));
|
||||||
|
assert_eq!(Serializable::deserialize([0xABu8, 0xCD].iter().map(|n| *n)), Ok(0xCDABu16));
|
||||||
|
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D].iter().map(|n| *n)), Ok(0xDA0u16));
|
||||||
|
let failure16: IoResult<u16> = Serializable::deserialize([1u8].iter().map(|n| *n));
|
||||||
|
assert!(failure16.is_err());
|
||||||
|
|
||||||
|
// u32
|
||||||
|
assert_eq!(Serializable::deserialize([0xABu8, 0xCD, 0, 0].iter().map(|n| *n)), Ok(0xCDABu32));
|
||||||
|
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D, 0xAB, 0xCD].iter().map(|n| *n)), Ok(0xCDAB0DA0u32));
|
||||||
|
let failure32: IoResult<u32> = Serializable::deserialize([1u8, 2, 3].iter().map(|n| *n));
|
||||||
|
assert!(failure32.is_err());
|
||||||
|
// TODO: test negative numbers
|
||||||
|
assert_eq!(Serializable::deserialize([0xABu8, 0xCD, 0, 0].iter().map(|n| *n)), Ok(0xCDABi32));
|
||||||
|
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D, 0xAB, 0x2D].iter().map(|n| *n)), Ok(0x2DAB0DA0i32));
|
||||||
|
let failurei32: IoResult<i32> = Serializable::deserialize([1u8, 2, 3].iter().map(|n| *n));
|
||||||
|
assert!(failurei32.is_err());
|
||||||
|
|
||||||
|
// u64
|
||||||
|
assert_eq!(Serializable::deserialize([0xABu8, 0xCD, 0, 0, 0, 0, 0, 0].iter().map(|n| *n)), Ok(0xCDABu64));
|
||||||
|
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99].iter().map(|n| *n)), Ok(0x99000099CDAB0DA0u64));
|
||||||
|
let failure64: IoResult<u64> = Serializable::deserialize([1u8, 2, 3, 4, 5, 6, 7].iter().map(|n| *n));
|
||||||
|
assert!(failure64.is_err());
|
||||||
|
// TODO: test negative numbers
|
||||||
|
assert_eq!(Serializable::deserialize([0xABu8, 0xCD, 0, 0, 0, 0, 0, 0].iter().map(|n| *n)), Ok(0xCDABi64));
|
||||||
|
assert_eq!(Serializable::deserialize([0xA0u8, 0x0D, 0xAB, 0xCD, 0x99, 0, 0, 0x99].iter().map(|n| *n)), Ok(0x99000099CDAB0DA0i64));
|
||||||
|
let failurei64: IoResult<i64> = Serializable::deserialize([1u8, 2, 3, 4, 5, 6, 7].iter().map(|n| *n));
|
||||||
|
assert!(failurei64.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_vec_test() {
|
||||||
|
assert_eq!(Serializable::deserialize([3u8, 2, 3, 4].iter().map(|n| *n)), Ok(vec![2u8, 3, 4]));
|
||||||
|
assert_eq!(Serializable::deserialize([4u8, 2, 3, 4, 5, 6].iter().map(|n| *n)), Ok(vec![2u8, 3, 4, 5]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_strbuf_test() {
|
||||||
|
assert_eq!(Serializable::deserialize([6u8, 0x41, 0x6e, 0x64, 0x72, 0x65, 0x77].iter().map(|n| *n)), Ok(String::from_str("Andrew")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_checkeddata_test() {
|
||||||
|
let cd: IoResult<CheckedData> = Serializable::deserialize([5u8, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5].iter().map(|n| *n));
|
||||||
|
assert!(cd.is_ok());
|
||||||
|
assert_eq!(cd.unwrap(), CheckedData(Vec::from_slice([1u8, 2, 3, 4, 5])));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_option_test() {
|
||||||
|
let none: IoResult<Option<u8>> = Serializable::deserialize([0u8].iter().map(|n| *n));
|
||||||
|
let good: IoResult<Option<u8>> = Serializable::deserialize([1u8, 0xFF].iter().map(|n| *n));
|
||||||
|
let bad: IoResult<Option<u8>> = Serializable::deserialize([2u8].iter().map(|n| *n));
|
||||||
|
assert!(bad.is_err());
|
||||||
|
assert_eq!(none, Ok(None));
|
||||||
|
assert_eq!(good, Ok(Some(0xFF)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_box_test() {
|
||||||
|
let zero: IoResult<Box<u8>> = Serializable::deserialize([0u8].iter().map(|n| *n));
|
||||||
|
let one: IoResult<Box<u8>> = Serializable::deserialize([1u8].iter().map(|n| *n));
|
||||||
|
assert_eq!(zero, Ok(box 0));
|
||||||
|
assert_eq!(one, Ok(box 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_bitv_test() {
|
||||||
|
let bv: IoResult<Bitv> = Serializable::deserialize([10u8, 0xFF, 0xC0].iter().map(|n| *n));
|
||||||
|
assert!(bv.is_ok());
|
||||||
|
assert_eq!(bv.unwrap(), Bitv::with_capacity(10, true));
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Sockets
|
||||||
|
//!
|
||||||
|
//! This module provides support for low-level network communication.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use time::now;
|
||||||
|
use std::rand::task_rng;
|
||||||
|
use rand::Rng;
|
||||||
|
use std::io::{IoError, IoResult, NotConnected, OtherIoError, standard_error};
|
||||||
|
use std::io::net::{ip, tcp};
|
||||||
|
|
||||||
|
use network::constants;
|
||||||
|
use network::address::Address;
|
||||||
|
use network::message::{RawNetworkMessage, NetworkMessage, Version};
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
use network::message_network::VersionMessage;
|
||||||
|
|
||||||
|
/// Format an IP address in the 16-byte bitcoin protocol serialization
|
||||||
|
fn ipaddr_to_bitcoin_addr(ipaddr: &ip::IpAddr) -> [u8, ..16] {
|
||||||
|
match *ipaddr {
|
||||||
|
ip::Ipv4Addr(a, b, c, d) =>
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0xff, 0xff, a, b, c, d],
|
||||||
|
ip::Ipv6Addr(a, b, c, d, e, f, g, h) =>
|
||||||
|
[(a / 0x100) as u8, (a % 0x100) as u8, (b / 0x100) as u8, (b % 0x100) as u8,
|
||||||
|
(c / 0x100) as u8, (c % 0x100) as u8, (d / 0x100) as u8, (d % 0x100) as u8,
|
||||||
|
(e / 0x100) as u8, (e % 0x100) as u8, (f / 0x100) as u8, (f % 0x100) as u8,
|
||||||
|
(g / 0x100) as u8, (g % 0x100) as u8, (h / 0x100) as u8, (h % 0x100) as u8 ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A network socket along with information about the peer
|
||||||
|
#[deriving(Clone)]
|
||||||
|
pub struct Socket {
|
||||||
|
/// The underlying network data stream
|
||||||
|
stream: Option<tcp::TcpStream>,
|
||||||
|
/// Services supported by us
|
||||||
|
pub services: u64,
|
||||||
|
/// Our user agent
|
||||||
|
pub user_agent: String,
|
||||||
|
/// Nonce to identify our `version` messages
|
||||||
|
pub version_nonce: u64,
|
||||||
|
/// Network magic
|
||||||
|
pub magic: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Socket {
|
||||||
|
// TODO: we fix services to 0
|
||||||
|
/// Construct a new socket
|
||||||
|
pub fn new(magic: u32) -> Socket {
|
||||||
|
let mut rng = task_rng();
|
||||||
|
Socket {
|
||||||
|
stream: None,
|
||||||
|
services: 0,
|
||||||
|
version_nonce: rng.gen(),
|
||||||
|
user_agent: String::from_str(constants::USER_AGENT),
|
||||||
|
magic: magic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect to the peer
|
||||||
|
pub fn connect(&mut self, host: &str, port: u16) -> IoResult<()> {
|
||||||
|
match tcp::TcpStream::connect(host, port) {
|
||||||
|
Ok(s) => {
|
||||||
|
self.stream = Some(s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Peer address
|
||||||
|
pub fn receiver_address(&mut self) -> IoResult<Address> {
|
||||||
|
match self.stream {
|
||||||
|
Some(ref mut s) => match s.peer_name() {
|
||||||
|
Ok(addr) => {
|
||||||
|
Ok(Address {
|
||||||
|
services: self.services,
|
||||||
|
address: ipaddr_to_bitcoin_addr(&addr.ip),
|
||||||
|
port: addr.port
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(e) => Err(e)
|
||||||
|
},
|
||||||
|
None => Err(standard_error(NotConnected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our own address
|
||||||
|
pub fn sender_address(&mut self) -> IoResult<Address> {
|
||||||
|
match self.stream {
|
||||||
|
Some(ref mut s) => match s.socket_name() {
|
||||||
|
Ok(addr) => {
|
||||||
|
Ok(Address {
|
||||||
|
services: self.services,
|
||||||
|
address: ipaddr_to_bitcoin_addr(&addr.ip),
|
||||||
|
port: addr.port
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(e) => Err(e)
|
||||||
|
},
|
||||||
|
None => Err(standard_error(NotConnected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce a version message appropriate for this socket
|
||||||
|
pub fn version_message(&mut self, start_height: i32) -> IoResult<NetworkMessage> {
|
||||||
|
let timestamp = now().to_timespec().sec;
|
||||||
|
let recv_addr = self.receiver_address();
|
||||||
|
let send_addr = self.sender_address();
|
||||||
|
// If we are not connected, we might not be able to get these address.s
|
||||||
|
match recv_addr {
|
||||||
|
Err(e) => { return Err(e); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match send_addr {
|
||||||
|
Err(e) => { return Err(e); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Version(VersionMessage {
|
||||||
|
version: constants::PROTOCOL_VERSION,
|
||||||
|
services: constants::SERVICES,
|
||||||
|
timestamp: timestamp,
|
||||||
|
receiver: recv_addr.unwrap(),
|
||||||
|
sender: send_addr.unwrap(),
|
||||||
|
nonce: self.version_nonce,
|
||||||
|
user_agent: self.user_agent.clone(),
|
||||||
|
start_height: start_height,
|
||||||
|
relay: false
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a general message across the line
|
||||||
|
pub fn send_message(&mut self, payload: NetworkMessage) -> IoResult<()> {
|
||||||
|
if self.stream.is_none() {
|
||||||
|
Err(standard_error(NotConnected))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let stream = self.stream.get_mut_ref();
|
||||||
|
let message = RawNetworkMessage { magic: self.magic, payload: payload };
|
||||||
|
match stream.write(message.serialize().as_slice()) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive the next message from the peer, decoding the network header
|
||||||
|
/// and verifying its correctness. Returns the undecoded payload.
|
||||||
|
pub fn receive_message(&mut self) -> IoResult<NetworkMessage> {
|
||||||
|
match self.stream {
|
||||||
|
None => Err(standard_error(NotConnected)),
|
||||||
|
Some(ref mut s) => {
|
||||||
|
let mut read_err = None;
|
||||||
|
// We need a new scope since the closure in here borrows read_err,
|
||||||
|
// and we try to read it afterward. Letting `iter` go out fixes it.
|
||||||
|
let ret: IoResult<RawNetworkMessage> = {
|
||||||
|
// Set up iterator so we will catch network errors properly
|
||||||
|
let iter = s.bytes().filter_map(|res|
|
||||||
|
match res {
|
||||||
|
Ok(ch) => Some(ch),
|
||||||
|
Err(e) => { read_err = Some(e); None }
|
||||||
|
});
|
||||||
|
// Receive message
|
||||||
|
Serializable::deserialize(iter)
|
||||||
|
};
|
||||||
|
// Return
|
||||||
|
match read_err {
|
||||||
|
// Network errors get priority since they are probably more meaningful
|
||||||
|
Some(e) => Err(e),
|
||||||
|
_ => {
|
||||||
|
match ret {
|
||||||
|
// Next come parse errors
|
||||||
|
Err(e) => Err(e),
|
||||||
|
Ok(ret) => {
|
||||||
|
// Finally magic (this should come before parse error, but we can't
|
||||||
|
// get to it if the deserialization failed). TODO restructure this
|
||||||
|
if ret.magic != self.magic {
|
||||||
|
Err(IoError {
|
||||||
|
kind: OtherIoError,
|
||||||
|
desc: "bad magic",
|
||||||
|
detail: Some(format!("got magic {:x}, expected {:x}", ret.magic, self.magic)),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(ret.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Hash functions
|
||||||
|
//!
|
||||||
|
//! Utility functions related to hashing data, including merkleization
|
||||||
|
|
||||||
|
use collections::bitv::{Bitv, from_bytes};
|
||||||
|
use core::char::from_digit;
|
||||||
|
use core::cmp::min;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io::{IoResult, IoError, InvalidInput};
|
||||||
|
use std::mem::transmute;
|
||||||
|
|
||||||
|
use crypto::digest::Digest;
|
||||||
|
use crypto::sha2;
|
||||||
|
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
use util::iter::FixedTakeable;
|
||||||
|
use util::uint::Uint128;
|
||||||
|
use util::uint::Uint256;
|
||||||
|
|
||||||
|
/// A Bitcoin hash, 32-bytes, computed from x as SHA256(SHA256(x))
|
||||||
|
pub struct Sha256dHash([u8, ..32]);
|
||||||
|
|
||||||
|
/// Returns the all-zeroes "hash"
|
||||||
|
pub fn zero_hash() -> Sha256dHash { Sha256dHash([0u8, ..32]) }
|
||||||
|
|
||||||
|
impl Sha256dHash {
|
||||||
|
/// Create a hash by hashing some data
|
||||||
|
pub fn from_data(data: &[u8]) -> Sha256dHash {
|
||||||
|
let Sha256dHash(mut ret) = zero_hash();
|
||||||
|
let mut sha2 = sha2::Sha256::new();
|
||||||
|
sha2.input(data);
|
||||||
|
sha2.result(ret.as_mut_slice());
|
||||||
|
sha2.reset();
|
||||||
|
sha2.input(ret.as_slice());
|
||||||
|
sha2.result(ret.as_mut_slice());
|
||||||
|
Sha256dHash(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice containing the bytes of the has
|
||||||
|
pub fn as_slice<'a>(&'a self) -> &'a [u8] {
|
||||||
|
let &Sha256dHash(ref data) = self;
|
||||||
|
data.as_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a hash to a bit vector
|
||||||
|
pub fn as_bitv(&self) -> Bitv {
|
||||||
|
from_bytes(self.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a hash to a Uint256, interpreting it as a little endian encoding.
|
||||||
|
pub fn as_uint256(&self) -> Uint256 {
|
||||||
|
let &Sha256dHash(data) = self;
|
||||||
|
unsafe { Uint256(transmute(data)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a hash to a Uint128, interpreting it as a little endian encoding.
|
||||||
|
pub fn as_uint128(&self) -> Uint128 {
|
||||||
|
let &Sha256dHash(data) = self;
|
||||||
|
unsafe { Uint128(transmute([data[16], data[17], data[18], data[19], data[20],
|
||||||
|
data[21], data[22], data[23], data[24], data[25],
|
||||||
|
data[26], data[27], data[28], data[29], data[30],
|
||||||
|
data[31]])) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human-readable hex output
|
||||||
|
pub fn le_hex_string(&self) -> String {
|
||||||
|
let &Sha256dHash(data) = self;
|
||||||
|
let mut ret = String::with_capacity(64);
|
||||||
|
for i in range(0u, 32).rev() {
|
||||||
|
ret.push_char(from_digit((data[i] / 0x10) as uint, 16).unwrap());
|
||||||
|
ret.push_char(from_digit((data[i] & 0x0f) as uint, 16).unwrap());
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Sha256dHash {
|
||||||
|
fn clone(&self) -> Sha256dHash {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Sha256dHash {
|
||||||
|
fn eq(&self, other: &Sha256dHash) -> bool {
|
||||||
|
let &Sha256dHash(ref mydata) = self;
|
||||||
|
let &Sha256dHash(ref yourdata) = other;
|
||||||
|
for i in range(0u, 32) {
|
||||||
|
if mydata[i] != yourdata[i] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for Sha256dHash {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let &Sha256dHash(ref data) = self;
|
||||||
|
data.iter().map(|n| *n).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Sha256dHash> {
|
||||||
|
let Sha256dHash(mut ret) = zero_hash();
|
||||||
|
let mut fixediter = iter.enumerate().fixed_take(32);
|
||||||
|
for (n, data) in fixediter {
|
||||||
|
ret[n] = data;
|
||||||
|
}
|
||||||
|
match fixediter.is_err() {
|
||||||
|
false => Ok(Sha256dHash(ret)),
|
||||||
|
true => Err(IoError {
|
||||||
|
kind: InvalidInput,
|
||||||
|
desc: "unexpected end of input",
|
||||||
|
detail: Some(format!("Need 32 bytes, was {:} short.", fixediter.remaining()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::LowerHex for Sha256dHash {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let &Sha256dHash(ref data) = self;
|
||||||
|
let mut rv = [0, ..64];
|
||||||
|
let mut hex = data.iter().rev().map(|n| *n).enumerate();
|
||||||
|
for (i, ch) in hex {
|
||||||
|
rv[2*i] = from_digit(ch as uint / 16, 16).unwrap() as u8;
|
||||||
|
rv[2*i + 1] = from_digit(ch as uint % 16, 16).unwrap() as u8;
|
||||||
|
}
|
||||||
|
f.write(rv.as_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Show for Sha256dHash {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:x}", *self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: this should be an impl and the function have first parameter self.
|
||||||
|
//See https://github.com/rust-lang/rust/issues/15060 for why this isn't so.
|
||||||
|
//impl<T: Serializable> Vec<T> {
|
||||||
|
/// Construct a merkle tree from a vector, with elements ordered as
|
||||||
|
/// they were in the original vector, and return the merkle root.
|
||||||
|
pub fn merkle_root<T: Serializable>(data: &[T]) -> Sha256dHash {
|
||||||
|
fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash {
|
||||||
|
// Base case
|
||||||
|
if data.len() < 1 {
|
||||||
|
return zero_hash();
|
||||||
|
}
|
||||||
|
if data.len() < 2 {
|
||||||
|
return data[0];
|
||||||
|
}
|
||||||
|
// Recursion
|
||||||
|
let mut next = vec![];
|
||||||
|
for idx in range(0, (data.len() + 1) / 2) {
|
||||||
|
let idx1 = 2 * idx;
|
||||||
|
let idx2 = min(idx1 + 1, data.len() - 1);
|
||||||
|
let to_hash = data[idx1].hash().serialize().append(data[idx2].hash().serialize().as_slice());
|
||||||
|
next.push(to_hash.hash());
|
||||||
|
}
|
||||||
|
merkle_root(next)
|
||||||
|
}
|
||||||
|
merkle_root(data.iter().map(|obj| obj.hash()).collect())
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::prelude::*;
|
||||||
|
use collections::bitv::from_bytes;
|
||||||
|
|
||||||
|
use util::hash::Sha256dHash;
|
||||||
|
use util::misc::hex_bytes;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sha256d() {
|
||||||
|
assert_eq!(Sha256dHash::from_data(&[]).as_slice(),
|
||||||
|
hex_bytes("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap().as_slice());
|
||||||
|
assert_eq!(Sha256dHash::from_data(&[]).le_hex_string(),
|
||||||
|
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d".to_string());
|
||||||
|
assert_eq!(Sha256dHash::from_data(b"TEST").as_slice(),
|
||||||
|
hex_bytes("d7bd34bfe44a18d2aa755a344fe3e6b06ed0473772e6dfce16ac71ba0b0a241c").unwrap().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hash_to_bitvset() {
|
||||||
|
assert_eq!(Sha256dHash::from_data(&[]).as_bitv(),
|
||||||
|
from_bytes(hex_bytes("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap().as_slice()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Iterator adaptors
|
||||||
|
//!
|
||||||
|
//! Iterator adaptors needed by Bitcoin but not provided by the Rust
|
||||||
|
//! standard library.
|
||||||
|
|
||||||
|
/// An iterator that just returns None
|
||||||
|
pub struct NullIterator<T>;
|
||||||
|
impl<T> Iterator<T> for NullIterator<T> {
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<T> { None }
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(&self) -> (uint, Option<uint>) { (0, Some(0)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> NullIterator<T> {
|
||||||
|
/// Creates a new NullIterator
|
||||||
|
pub fn new() -> NullIterator<T> { NullIterator }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An Iterator which will give n elements of the contained iterator
|
||||||
|
/// before returning None. If the contained iterator returns None too
|
||||||
|
/// early,
|
||||||
|
pub struct FixedTake<I> {
|
||||||
|
iter: I,
|
||||||
|
n_elems: uint,
|
||||||
|
is_err: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I: Iterator<T>> Iterator<T> for FixedTake<I> {
|
||||||
|
fn next(&mut self) -> Option<T> {
|
||||||
|
if self.n_elems == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.n_elems -= 1;
|
||||||
|
match self.iter.next() {
|
||||||
|
Some(e) => Some(e),
|
||||||
|
None => {
|
||||||
|
self.is_err = true;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> FixedTake<I> {
|
||||||
|
/// Constructs a FixedTake iterator from an underlying iterator
|
||||||
|
pub fn new(iter: I, n_elems: uint) -> FixedTake<I> {
|
||||||
|
FixedTake { iter: iter, n_elems: n_elems, is_err: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates whether the underlying iterator has ended early
|
||||||
|
pub fn is_err(&self) -> bool {
|
||||||
|
self.is_err
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of remaining elements
|
||||||
|
pub fn remaining(&self) -> uint {
|
||||||
|
self.n_elems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator that returns at most `n_elems` elements, entering an error
|
||||||
|
/// state if the underlying iterator yields fewer than `n_elems` elements.
|
||||||
|
pub trait FixedTakeable<I> {
|
||||||
|
/// Returns an iterator similar to Take but which detects if the original
|
||||||
|
/// iterator runs out early
|
||||||
|
fn fixed_take(self, n_elems: uint) -> FixedTake<I>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I: Iterator<T>> FixedTakeable<I> for I {
|
||||||
|
fn fixed_take(self, n_elems: uint) -> FixedTake<I> {
|
||||||
|
FixedTake::new(self, n_elems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator that returns pairs of elements
|
||||||
|
pub struct Pair<A, I> {
|
||||||
|
iter: I,
|
||||||
|
last_elem: Option<A>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, I: Iterator<A>> Iterator<(A, A)> for Pair<A, I> {
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<(A, A)> {
|
||||||
|
let elem1 = self.iter.next();
|
||||||
|
if elem1.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let elem2 = self.iter.next();
|
||||||
|
if elem2.is_none() {
|
||||||
|
self.last_elem = elem1;
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((elem1.unwrap(), elem2.unwrap()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(&self) -> (uint, Option<uint>) {
|
||||||
|
match self.iter.size_hint() {
|
||||||
|
(n, None) => (n/2, None),
|
||||||
|
(n, Some(m)) => (n/2, Some(m/2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, I: Iterator<A>> Pair<A, I> {
|
||||||
|
/// Returns the last element of the iterator if there were an odd
|
||||||
|
/// number of elements remaining before it was Pair-ified.
|
||||||
|
#[inline]
|
||||||
|
pub fn remainder(self) -> Option<A> {
|
||||||
|
self.last_elem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
||||||
|
pub trait Pairable<A> {
|
||||||
|
/// Returns an iterator that returns elements of the original iterator 2 at a time
|
||||||
|
fn pair(self) -> Pair<A, Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A, I: Iterator<A>> Pairable<A> for I {
|
||||||
|
/// Creates an iterator that yields pairs ef elements from the underlying
|
||||||
|
/// iterator, yielding `None` when there are fewer than two elements to
|
||||||
|
/// return.
|
||||||
|
#[inline]
|
||||||
|
fn pair(self) -> Pair<A, I> {
|
||||||
|
Pair{iter: self, last_elem: None}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Miscellaneous functions
|
||||||
|
//!
|
||||||
|
//! Various utility functions
|
||||||
|
|
||||||
|
use std::io::{IoError, IoResult, InvalidInput};
|
||||||
|
|
||||||
|
use util::iter::Pairable;
|
||||||
|
|
||||||
|
/// Convert a hexadecimal-encoded string to its corresponding bytes
|
||||||
|
pub fn hex_bytes(s: &str) -> IoResult<Vec<u8>> {
|
||||||
|
let mut v = vec![];
|
||||||
|
let mut iter = s.chars().pair();
|
||||||
|
// Do the parsing
|
||||||
|
try!(iter.fold(Ok(()), |e, (f, s)|
|
||||||
|
if e.is_err() { return e; }
|
||||||
|
else {
|
||||||
|
match (f.to_digit(16), s.to_digit(16)) {
|
||||||
|
(None, _) => return Err(IoError {
|
||||||
|
kind: InvalidInput,
|
||||||
|
desc: "invalid hex character",
|
||||||
|
detail: Some(format!("expected hex, got {:}", f))
|
||||||
|
}),
|
||||||
|
(_, None) => return Err(IoError {
|
||||||
|
kind: InvalidInput,
|
||||||
|
desc: "invalid hex character",
|
||||||
|
detail: Some(format!("expected hex, got {:}", s))
|
||||||
|
}),
|
||||||
|
(Some(f), Some(s)) => { v.push((f * 0x10 + s) as u8); Ok(()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
|
// Check that there was no remainder
|
||||||
|
match iter.remainder() {
|
||||||
|
Some(_) => Err(IoError {
|
||||||
|
kind: InvalidInput,
|
||||||
|
desc: "hexstring of odd length",
|
||||||
|
detail: None
|
||||||
|
}),
|
||||||
|
None => Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepend the detail of an IoResult's error with some text to get poor man's backtracing
|
||||||
|
pub fn prepend_err<T>(s: &str, res: IoResult<T>) -> IoResult<T> {
|
||||||
|
res.map_err(|err| {
|
||||||
|
IoError {
|
||||||
|
kind: err.kind,
|
||||||
|
desc: err.desc,
|
||||||
|
detail: Some(format!("{:s}: {:}", s, match err.detail { Some(s) => s, None => String::new() }))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump an error message to the screen
|
||||||
|
pub fn consume_err<T>(s: &str, res: IoResult<T>) {
|
||||||
|
match res {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => { println!("{:s}: {:}", s, e); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::prelude::*;
|
||||||
|
|
||||||
|
use util::misc::hex_bytes;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hex_bytes() {
|
||||||
|
assert_eq!(hex_bytes("abcd").unwrap().as_slice(), [171u8, 205].as_slice());
|
||||||
|
assert!(hex_bytes("abcde").is_err());
|
||||||
|
assert!(hex_bytes("aBcDeF").is_ok());
|
||||||
|
assert!(hex_bytes("aBcD4eFL").is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Rust Bitcoin Library
|
||||||
|
// Written in 2014 by
|
||||||
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
|
//
|
||||||
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
|
// copyright and related and neighboring rights to this software to
|
||||||
|
// the public domain worldwide. This software is distributed without
|
||||||
|
// any warranty.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
// along with this software.
|
||||||
|
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Utility functions
|
||||||
|
//!
|
||||||
|
//! Functions needed by all parts of the Bitcoin library
|
||||||
|
|
||||||
|
pub mod hash;
|
||||||
|
pub mod iter;
|
||||||
|
pub mod misc;
|
||||||
|
pub mod patricia_tree;
|
||||||
|
pub mod thinvec;
|
||||||
|
pub mod uint;
|
||||||
|
|
||||||
|
/// A trait which allows numbers to act as fixed-size bit arrays
|
||||||
|
pub trait BitArray {
|
||||||
|
/// Is bit set?
|
||||||
|
fn bit(&self, idx: uint) -> bool;
|
||||||
|
|
||||||
|
/// Returns an array which is just the bits from start to end
|
||||||
|
fn bit_slice(&self, start: uint, end: uint) -> Self;
|
||||||
|
|
||||||
|
/// Bitwise and with `n` ones
|
||||||
|
fn mask(&self, n: uint) -> Self;
|
||||||
|
|
||||||
|
/// Trailing zeros
|
||||||
|
fn trailing_zeros(&self) -> uint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,546 @@
|
||||||
|
// 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 core::fmt::Show;
|
||||||
|
use core::iter::ByRef;
|
||||||
|
use core::cmp;
|
||||||
|
use std::num::{Zero, One};
|
||||||
|
use std::io::{IoResult, InvalidInput, standard_error};
|
||||||
|
|
||||||
|
use network::serialize::{Serializable, SerializeIter};
|
||||||
|
use util::BitArray;
|
||||||
|
use util::misc::prepend_err;
|
||||||
|
|
||||||
|
/// Patricia troo
|
||||||
|
pub struct PatriciaTree<T, K> {
|
||||||
|
data: Option<T>,
|
||||||
|
child_l: Option<Box<PatriciaTree<T, K>>>,
|
||||||
|
child_r: Option<Box<PatriciaTree<T, K>>>,
|
||||||
|
skip_prefix: K,
|
||||||
|
skip_len: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>> PatriciaTree<T, K> {
|
||||||
|
/// Constructs a new Patricia tree
|
||||||
|
pub fn new() -> PatriciaTree<T, K> {
|
||||||
|
PatriciaTree {
|
||||||
|
data: None,
|
||||||
|
child_l: None,
|
||||||
|
child_r: None,
|
||||||
|
skip_prefix: Zero::zero(),
|
||||||
|
skip_len: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup a value by exactly matching `key` and return a referenc
|
||||||
|
pub fn lookup_mut<'a>(&'a mut self, key: &K, key_len: uint) -> Option<&'a mut T> {
|
||||||
|
// 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<'a>(&'a self, key: &K, key_len: uint) -> Option<&'a T> {
|
||||||
|
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 uint {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key fails to match prefix --- no match
|
||||||
|
if node.skip_prefix != key.bit_slice(key_idx, key_idx + node.skip_len as uint) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key matches prefix: if they are an exact match, return the data
|
||||||
|
if node.skip_len as uint == 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 uint;
|
||||||
|
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: uint, value: T) -> 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: uint, value: T) -> bool {
|
||||||
|
self.real_insert(key, key_len, value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn real_insert(&mut self, key: &K, key_len: uint, value: T, 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 uint, 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 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 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.get_mut_ref();
|
||||||
|
}
|
||||||
|
// 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 uint > 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 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 uint == 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 uint + 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 PatriciaTree {
|
||||||
|
data: None,
|
||||||
|
child_l: None,
|
||||||
|
child_r: None,
|
||||||
|
skip_prefix: key.bit_slice(idx, key_len),
|
||||||
|
skip_len: key_len as u8 - idx as u8
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// subtree.get_mut_ref is a &mut Box<U> here, so &mut ** gets a &mut U
|
||||||
|
node = &mut **subtree.get_mut_ref();
|
||||||
|
} // 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: uint) -> Option<T> {
|
||||||
|
/// 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<T, K:BitArray+Eq+Zero+One+Add<K,K>+Shr<uint,K>+Shl<uint,K>>(tree: &mut PatriciaTree<T, K>, key: &K, key_len: uint) -> (bool, Option<T>) {
|
||||||
|
// 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 uint {
|
||||||
|
return (false, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key fails to match prefix --- no match
|
||||||
|
if tree.skip_prefix != key.mask(tree.skip_len as uint) {
|
||||||
|
return (false, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are here, the key matches the prefix
|
||||||
|
if tree.skip_len as uint == 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(consolidate), None) | (None, Some(consolidate)) => {
|
||||||
|
tree.data = consolidate.data;
|
||||||
|
tree.child_l = consolidate.child_l;
|
||||||
|
tree.child_r = consolidate.child_r;
|
||||||
|
let new_bit = if bit { let ret: K = One::one();
|
||||||
|
ret << (tree.skip_len as uint) }
|
||||||
|
else { Zero::zero() };
|
||||||
|
tree.skip_prefix = tree.skip_prefix +
|
||||||
|
new_bit +
|
||||||
|
(consolidate.skip_prefix << (1 + tree.skip_len as uint));
|
||||||
|
tree.skip_len += 1 + consolidate.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 uint);
|
||||||
|
// 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.get_mut_ref(),
|
||||||
|
&key.shr(&(tree.skip_len as uint + 1)),
|
||||||
|
key_len - tree.skip_len as uint - 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);
|
||||||
|
return (false, ret);
|
||||||
|
}
|
||||||
|
// One child? Consolidate
|
||||||
|
(bit, Some(consolidate), None) | (bit, None, Some(consolidate)) => {
|
||||||
|
tree.data = consolidate.data;
|
||||||
|
tree.child_l = consolidate.child_l;
|
||||||
|
tree.child_r = consolidate.child_r;
|
||||||
|
let new_bit = if bit { let ret: K = One::one();
|
||||||
|
ret << (tree.skip_len as uint) }
|
||||||
|
else { Zero::zero() };
|
||||||
|
tree.skip_prefix = tree.skip_prefix +
|
||||||
|
new_bit +
|
||||||
|
(consolidate.skip_prefix << (1 + tree.skip_len as uint));
|
||||||
|
tree.skip_len += 1 + consolidate.skip_len;
|
||||||
|
return (false, ret);
|
||||||
|
}
|
||||||
|
// No children? Delete
|
||||||
|
(_, None, None) => {
|
||||||
|
return (true, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (_, ret) = recurse(self, key, key_len);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Count all the nodes
|
||||||
|
pub fn node_count(&self) -> uint {
|
||||||
|
fn recurse<T, K>(node: &Option<Box<PatriciaTree<T, K>>>) -> uint {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Show, K:BitArray> PatriciaTree<T, K> {
|
||||||
|
/// Print the entire tree
|
||||||
|
pub fn print<'a>(&'a self) {
|
||||||
|
fn recurse<'a, T:Show, K:BitArray>(tree: &'a PatriciaTree<T, K>, depth: uint) {
|
||||||
|
for i in range(0, tree.skip_len as uint) {
|
||||||
|
print!("{:}", if tree.skip_prefix.bit(i) { 1u } else { 0 });
|
||||||
|
}
|
||||||
|
println!(": {:}", tree.data);
|
||||||
|
// left gets no indentation
|
||||||
|
match tree.child_l {
|
||||||
|
Some(ref t) => {
|
||||||
|
for _ in range(0, depth + tree.skip_len as uint) {
|
||||||
|
print!("-");
|
||||||
|
}
|
||||||
|
print!("0");
|
||||||
|
recurse(&**t, depth + tree.skip_len as uint + 1);
|
||||||
|
}
|
||||||
|
None => { }
|
||||||
|
}
|
||||||
|
// right one gets indentation
|
||||||
|
match tree.child_r {
|
||||||
|
Some(ref t) => {
|
||||||
|
for _ in range(0, depth + tree.skip_len as uint) {
|
||||||
|
print!("_");
|
||||||
|
}
|
||||||
|
print!("1");
|
||||||
|
recurse(&**t, depth + tree.skip_len as uint + 1);
|
||||||
|
}
|
||||||
|
None => { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recurse(self, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Serializable+'static, K:BitArray+Serializable+'static> Serializable for PatriciaTree<T, K> {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
// Depth-first serialization
|
||||||
|
let mut ret = vec![];
|
||||||
|
// Serialize self, then children
|
||||||
|
ret.extend(self.skip_prefix.serialize().move_iter());
|
||||||
|
ret.extend(self.skip_len.serialize().move_iter());
|
||||||
|
ret.extend(self.data.serialize().move_iter());
|
||||||
|
ret.extend(self.child_l.serialize().move_iter());
|
||||||
|
ret.extend(self.child_r.serialize().move_iter());
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_iter<'a>(&'a self) -> SerializeIter<'a> {
|
||||||
|
SerializeIter {
|
||||||
|
data_iter: None,
|
||||||
|
sub_iter_iter: box vec![ &self.skip_prefix as &Serializable,
|
||||||
|
&self.skip_len as &Serializable,
|
||||||
|
&self.data as &Serializable,
|
||||||
|
&self.child_l as &Serializable,
|
||||||
|
&self.child_r as &Serializable ].move_iter(),
|
||||||
|
sub_iter: None,
|
||||||
|
sub_started: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<PatriciaTree<T, K>> {
|
||||||
|
// This goofy deserialization routine is to prevent an infinite
|
||||||
|
// regress of ByRef<ByRef<...<ByRef<I>>...>>, see #15188
|
||||||
|
fn recurse<T:Serializable, K:Serializable, I: Iterator<u8>>(iter: &mut ByRef<I>) -> IoResult<PatriciaTree<T, K>> {
|
||||||
|
Ok(PatriciaTree {
|
||||||
|
skip_prefix: try!(prepend_err("skip_prefix", Serializable::deserialize(iter.by_ref()))),
|
||||||
|
skip_len: try!(prepend_err("skip_len", Serializable::deserialize(iter.by_ref()))),
|
||||||
|
data: try!(prepend_err("data", Serializable::deserialize(iter.by_ref()))),
|
||||||
|
child_l: match iter.next() {
|
||||||
|
Some(1) => Some(box try!(prepend_err("child_l", recurse(iter)))),
|
||||||
|
Some(0) => None,
|
||||||
|
_ => { return Err(standard_error(InvalidInput)) }
|
||||||
|
},
|
||||||
|
child_r: match iter.next() {
|
||||||
|
Some(1) => Some(box try!(prepend_err("child_r", recurse(iter)))),
|
||||||
|
Some(0) => None,
|
||||||
|
_ => { return Err(standard_error(InvalidInput)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
recurse(&mut iter.by_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::prelude::*;
|
||||||
|
use std::io::IoResult;
|
||||||
|
use std::num::Zero;
|
||||||
|
|
||||||
|
use util::hash::Sha256dHash;
|
||||||
|
use util::uint::Uint128;
|
||||||
|
use util::uint::Uint256;
|
||||||
|
use util::patricia_tree::PatriciaTree;
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn patricia_single_insert_lookup_delete_test() {
|
||||||
|
let mut key: Uint256 = FromPrimitive::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 range(0u32, 5000) {
|
||||||
|
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).as_uint128();
|
||||||
|
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 range(1u32, 500) {
|
||||||
|
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).as_uint128();
|
||||||
|
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 range(0u32, 10) {
|
||||||
|
tree.insert(&Zero::zero(), i as uint, i);
|
||||||
|
}
|
||||||
|
for i in range(0u32, 10) {
|
||||||
|
let m = tree.lookup(&Zero::zero(), i as uint);
|
||||||
|
assert_eq!(m, Some(&i));
|
||||||
|
}
|
||||||
|
for i in range(0u32, 10) {
|
||||||
|
let m = tree.delete(&Zero::zero(), i as uint);
|
||||||
|
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_serialize_test() {
|
||||||
|
// Build a tree
|
||||||
|
let mut tree = PatriciaTree::new();
|
||||||
|
let mut hashes = vec![];
|
||||||
|
for i in range(0u32, 5000) {
|
||||||
|
let hash = Sha256dHash::from_data(&[(i / 0x100) as u8, (i % 0x100) as u8]).as_uint128();
|
||||||
|
tree.insert(&hash, 250, i);
|
||||||
|
hashes.push(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize it
|
||||||
|
let serialized = tree.serialize();
|
||||||
|
// Check iterator
|
||||||
|
let serialized_1 = tree.serialize_iter().collect();
|
||||||
|
assert_eq!(serialized, serialized_1);
|
||||||
|
// Deserialize it
|
||||||
|
let deserialized: IoResult<PatriciaTree<u32, Uint128>> = Serializable::deserialize(serialized.iter().map(|n| *n));
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Thin vectors
|
||||||
|
//!
|
||||||
|
//! A vector type designed to take as little memory as possible by limiting
|
||||||
|
//! its size to 4bn elements and not distinguishing between size and capacity.
|
||||||
|
//! It is very easy to read uninitialized memory: make sure you assign all
|
||||||
|
//! values after calling `reserve` or `with_capacity`.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use alloc::heap::{allocate, reallocate, deallocate};
|
||||||
|
use std::raw::Slice;
|
||||||
|
use std::slice::Items;
|
||||||
|
use std::{fmt, mem, ptr};
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
/// A vector type designed to take very little memory
|
||||||
|
pub struct ThinVec<T> {
|
||||||
|
ptr: *mut T,
|
||||||
|
cap: u32 // capacity and length are the same
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ThinVec<T> {
|
||||||
|
/// Constructor
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> ThinVec<T> { ThinVec { ptr: RawPtr::null(), cap: 0 } }
|
||||||
|
|
||||||
|
/// Constructor with predetermined capacity
|
||||||
|
#[inline]
|
||||||
|
pub fn with_capacity(capacity: u32) -> ThinVec<T> {
|
||||||
|
if mem::size_of::<T>() == 0 {
|
||||||
|
ThinVec { ptr: RawPtr::null(), cap: capacity }
|
||||||
|
} else if capacity == 0 {
|
||||||
|
ThinVec::new()
|
||||||
|
} else {
|
||||||
|
let size = (capacity as uint).checked_mul(&mem::size_of::<T>())
|
||||||
|
.expect("ThinVec::reserve: capacity overflow");
|
||||||
|
let ptr = unsafe { allocate(size, mem::min_align_of::<T>()) };
|
||||||
|
ThinVec { ptr: ptr as *mut T, cap: capacity }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator over elements of the vector
|
||||||
|
#[inline]
|
||||||
|
pub fn iter<'a>(&'a self) -> Items<'a, T> {
|
||||||
|
self.as_slice().iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get vector as mutable slice
|
||||||
|
#[inline]
|
||||||
|
pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] {
|
||||||
|
unsafe { mem::transmute(Slice { data: self.ptr as *const T, len: self.cap as uint }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accessor
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get<'a>(&'a self, index: uint) -> &'a T {
|
||||||
|
&self.as_slice()[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutable accessor NOT for first use
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut T {
|
||||||
|
&mut self.as_mut_slice()[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutable accessor for first use
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn init<'a>(&'a mut self, index: uint, value: T) {
|
||||||
|
ptr::write(&mut *self.ptr.offset(index as int), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push: always reallocates, try not to use this
|
||||||
|
#[inline]
|
||||||
|
pub fn push(&mut self, value: T) {
|
||||||
|
self.cap = self.cap.checked_add(&1).expect("ThinVec::push: length overflow");
|
||||||
|
if mem::size_of::<T>() == 0 {
|
||||||
|
unsafe { mem::forget(value); }
|
||||||
|
} else {
|
||||||
|
let old_size = (self.cap - 1) as uint * mem::size_of::<T>();
|
||||||
|
let new_size = self.cap as uint * mem::size_of::<T>();
|
||||||
|
if new_size < old_size { fail!("ThinVec::push: cap overflow") }
|
||||||
|
unsafe {
|
||||||
|
self.ptr =
|
||||||
|
if old_size == 0 {
|
||||||
|
allocate(new_size, mem::min_align_of::<T>()) as *mut T
|
||||||
|
} else {
|
||||||
|
reallocate(self.ptr as *mut u8, new_size,
|
||||||
|
mem::min_align_of::<T>(), self.cap as uint) as *mut T
|
||||||
|
};
|
||||||
|
ptr::write(&mut *self.ptr.offset((self.cap - 1) as int), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the length of the vector to the minimum of the current capacity and new capacity
|
||||||
|
pub fn reserve(&mut self, new_cap: u32) {
|
||||||
|
if new_cap > self.cap {
|
||||||
|
let new_size = (new_cap as uint).checked_mul(&mem::size_of::<T>())
|
||||||
|
.expect("ThinVec::reserve: capacity overflow");
|
||||||
|
unsafe {
|
||||||
|
self.ptr =
|
||||||
|
if self.cap == 0 {
|
||||||
|
allocate(new_size, mem::min_align_of::<T>()) as *mut T
|
||||||
|
} else {
|
||||||
|
reallocate(self.ptr as *mut u8, new_size,
|
||||||
|
mem::min_align_of::<T>(), self.cap as uint) as *mut T
|
||||||
|
};
|
||||||
|
}
|
||||||
|
self.cap = new_cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increase the length of the vector
|
||||||
|
pub fn reserve_additional(&mut self, extra: u32) {
|
||||||
|
let new_cap = self.cap.checked_add(&extra).expect("ThinVec::reserve_additional: length overflow");
|
||||||
|
self.reserve(new_cap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Clone> ThinVec<T> {
|
||||||
|
/// Push an entire slice onto the ThinVec
|
||||||
|
#[inline]
|
||||||
|
pub fn push_all(&mut self, other: &[T]) {
|
||||||
|
let old_cap = self.cap as uint;
|
||||||
|
self.reserve_additional(other.len() as u32);
|
||||||
|
// Copied from vec.rs, which claims this will be optimized to a memcpy
|
||||||
|
// if T is Copy
|
||||||
|
for i in range(0, other.len()) {
|
||||||
|
unsafe {
|
||||||
|
ptr::write(self.as_mut_slice().unsafe_mut_ref(old_cap + i),
|
||||||
|
other.unsafe_ref(i).clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vector<T> for ThinVec<T> {
|
||||||
|
#[inline]
|
||||||
|
fn as_slice<'a>(&'a self) -> &'a [T] {
|
||||||
|
unsafe { mem::transmute(Slice { data: self.ptr as *const T, len: self.cap as uint }) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Clone> Clone for ThinVec<T> {
|
||||||
|
fn clone(&self) -> ThinVec<T> {
|
||||||
|
let mut ret = ThinVec::with_capacity(self.len() as u32);
|
||||||
|
// Copied from vec.rs, which claims this will be optimized to a memcpy
|
||||||
|
// if T is Copy
|
||||||
|
for i in range(0, self.len()) {
|
||||||
|
unsafe {
|
||||||
|
ptr::write(ret.as_mut_slice().unsafe_mut_ref(i),
|
||||||
|
self.as_slice().unsafe_ref(i).clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: clone_from
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromIterator<T> for ThinVec<T> {
|
||||||
|
#[inline]
|
||||||
|
fn from_iter<I: Iterator<T>>(iter: I) -> ThinVec<T> {
|
||||||
|
let (lower, _) = iter.size_hint();
|
||||||
|
assert!(lower < u32::MAX as uint);
|
||||||
|
let mut vector = ThinVec::with_capacity(lower as u32);
|
||||||
|
for (n, elem) in iter.enumerate() {
|
||||||
|
if n < lower {
|
||||||
|
unsafe { vector.init(n, elem) };
|
||||||
|
} else {
|
||||||
|
vector.push(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vector
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Extendable<T> for ThinVec<T> {
|
||||||
|
#[inline]
|
||||||
|
fn extend<I: Iterator<T>>(&mut self, iter: I) {
|
||||||
|
let old_cap = self.cap;
|
||||||
|
let (lower, _) = iter.size_hint();
|
||||||
|
self.reserve_additional(lower as u32);
|
||||||
|
for (n, elem) in iter.enumerate() {
|
||||||
|
if n < lower {
|
||||||
|
unsafe { self.init(old_cap as uint + n, elem) };
|
||||||
|
} else {
|
||||||
|
self.push(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Collection for ThinVec<T> {
|
||||||
|
#[inline]
|
||||||
|
fn len(&self) -> uint { self.cap as uint }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:fmt::Show> fmt::Show for ThinVec<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.as_slice().fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq for ThinVec<T> {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &ThinVec<T>) -> bool {
|
||||||
|
self.as_slice() == other.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq> Eq for ThinVec<T> {}
|
||||||
|
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T> Drop for ThinVec<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.cap != 0 {
|
||||||
|
unsafe {
|
||||||
|
for x in self.as_mut_slice().iter() {
|
||||||
|
ptr::read(x);
|
||||||
|
}
|
||||||
|
if mem::size_of::<T>() != 0 {
|
||||||
|
deallocate(self.ptr as *mut u8,
|
||||||
|
self.cap as uint * mem::size_of::<T>(),
|
||||||
|
mem::min_align_of::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::ThinVec;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_destructor_thinvec_test() {
|
||||||
|
let cap = 2;
|
||||||
|
let mut thinvec = ThinVec::with_capacity(cap);
|
||||||
|
|
||||||
|
for i in range(0, cap) {
|
||||||
|
unsafe { thinvec.init(i as uint, Some(box i)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in range(0, cap) {
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(thinvec.get_mut(i as uint).take(), Some(box i));
|
||||||
|
assert_eq!(thinvec.get_mut(i as uint).take(), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,483 @@
|
||||||
|
// 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/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # Big unsigned integer types
|
||||||
|
//!
|
||||||
|
//! Implementation of a various large-but-fixed sized unsigned integer types.
|
||||||
|
//! The functions here are designed to be fast.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::io::IoResult;
|
||||||
|
use std::num::{Zero, One};
|
||||||
|
use std::mem::transmute;
|
||||||
|
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
use util::BitArray;
|
||||||
|
|
||||||
|
macro_rules! construct_uint(
|
||||||
|
($name:ident, $n_words:expr) => (
|
||||||
|
/// Little-endian large integer type
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct $name(pub [u64, ..$n_words]);
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
/// Conversion to u32
|
||||||
|
#[inline]
|
||||||
|
pub fn low_u32(&self) -> u32 {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
arr[0] as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the least number of bits needed to represent the number
|
||||||
|
#[inline]
|
||||||
|
pub fn bits(&self) -> uint {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
for i in range(1u, $n_words) {
|
||||||
|
if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as uint; }
|
||||||
|
}
|
||||||
|
0x40 - arr[0].leading_zeros() as uint
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiplication by u32
|
||||||
|
pub fn mul_u32(&self, other: u32) -> $name {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
let mut carry = [0u64, ..$n_words];
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
let upper = other as u64 * (arr[i] >> 32);
|
||||||
|
let lower = other as u64 * (arr[i] & 0xFFFFFFFF);
|
||||||
|
if i < 3 {
|
||||||
|
carry[i + 1] += upper >> 32;
|
||||||
|
}
|
||||||
|
ret[i] = lower + (upper << 32);
|
||||||
|
}
|
||||||
|
$name(ret) + $name(carry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromPrimitive for $name {
|
||||||
|
#[inline]
|
||||||
|
fn from_u64(init: u64) -> Option<$name> {
|
||||||
|
let mut ret = [0, ..$n_words];
|
||||||
|
ret[0] = init;
|
||||||
|
Some($name(ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_i64(init: i64) -> Option<$name> {
|
||||||
|
FromPrimitive::from_u64(init as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zero for $name {
|
||||||
|
fn zero() -> $name { $name([0, ..$n_words]) }
|
||||||
|
fn is_zero(&self) -> bool { *self == Zero::zero() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl One for $name {
|
||||||
|
fn one() -> $name {
|
||||||
|
$name({ let mut ret = [0, ..$n_words]; ret[0] = 1; ret })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<$name,$name> for $name {
|
||||||
|
fn add(&self, other: &$name) -> $name {
|
||||||
|
let &$name(ref me) = self;
|
||||||
|
let &$name(ref you) = other;
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
let mut carry = [0u64, ..$n_words];
|
||||||
|
let mut b_carry = false;
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
ret[i] = me[i] + you[i];
|
||||||
|
if i < $n_words - 1 && ret[i] < me[i] {
|
||||||
|
carry[i + 1] = 1;
|
||||||
|
b_carry = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b_carry { $name(ret) + $name(carry) } else { $name(ret) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<$name,$name> for $name {
|
||||||
|
#[inline]
|
||||||
|
fn sub(&self, other: &$name) -> $name {
|
||||||
|
*self + !*other + One::one()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<$name,$name> for $name {
|
||||||
|
fn mul(&self, other: &$name) -> $name {
|
||||||
|
let mut me = *self;
|
||||||
|
// TODO: be more efficient about this
|
||||||
|
for i in range(0u, 2 * $n_words) {
|
||||||
|
me = me + me.mul_u32((other >> (32 * i)).low_u32()) << (32 * i);
|
||||||
|
}
|
||||||
|
me
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<$name,$name> for $name {
|
||||||
|
fn div(&self, other: &$name) -> $name {
|
||||||
|
let mut sub_copy = *self;
|
||||||
|
let mut shift_copy = *other;
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
|
||||||
|
let my_bits = self.bits();
|
||||||
|
let your_bits = other.bits();
|
||||||
|
|
||||||
|
// Check for division by 0
|
||||||
|
assert!(your_bits != 0);
|
||||||
|
|
||||||
|
// Early return in case we are dividing by a larger number than us
|
||||||
|
if my_bits < your_bits {
|
||||||
|
return $name(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitwise long division
|
||||||
|
let mut shift = my_bits - your_bits;
|
||||||
|
shift_copy = shift_copy << shift;
|
||||||
|
loop {
|
||||||
|
if sub_copy >= shift_copy {
|
||||||
|
ret[shift / 64] |= 1 << (shift % 64);
|
||||||
|
sub_copy = sub_copy.sub(&shift_copy);
|
||||||
|
}
|
||||||
|
shift_copy = shift_copy >> 1;
|
||||||
|
if shift == 0 { break; }
|
||||||
|
shift -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitArray for $name {
|
||||||
|
#[inline]
|
||||||
|
fn bit(&self, index: uint) -> bool {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
arr[index / 64] & (1 << (index % 64)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bit_slice(&self, start: uint, end: uint) -> $name {
|
||||||
|
(self >> start).mask(end - start)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mask(&self, n: uint) -> $name {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
let mut ret = [0, ..$n_words];
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
if n >= 0x40 * (i + 1) {
|
||||||
|
ret[i] = arr[i];
|
||||||
|
} else {
|
||||||
|
ret[i] = arr[i] & ((1 << (n - 0x40 * i)) - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn trailing_zeros(&self) -> uint {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
for i in range(0u, $n_words - 1) {
|
||||||
|
if arr[i] > 0 { return (0x40 * i) + arr[i].trailing_zeros() as uint; }
|
||||||
|
}
|
||||||
|
(0x40 * ($n_words - 1)) + arr[3].trailing_zeros() as uint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitAnd<$name,$name> for $name {
|
||||||
|
#[inline]
|
||||||
|
fn bitand(&self, other: &$name) -> $name {
|
||||||
|
let &$name(ref arr1) = self;
|
||||||
|
let &$name(ref arr2) = other;
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
ret[i] = arr1[i] & arr2[i];
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitXor<$name,$name> for $name {
|
||||||
|
#[inline]
|
||||||
|
fn bitxor(&self, other: &$name) -> $name {
|
||||||
|
let &$name(ref arr1) = self;
|
||||||
|
let &$name(ref arr2) = other;
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
ret[i] = arr1[i] ^ arr2[i];
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOr<$name,$name> for $name {
|
||||||
|
#[inline]
|
||||||
|
fn bitor(&self, other: &$name) -> $name {
|
||||||
|
let &$name(ref arr1) = self;
|
||||||
|
let &$name(ref arr2) = other;
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
ret[i] = arr1[i] | arr2[i];
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Not<$name> for $name {
|
||||||
|
#[inline]
|
||||||
|
fn not(&self) -> $name {
|
||||||
|
let &$name(ref arr) = self;
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
ret[i] = !arr[i];
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shl<uint,$name> for $name {
|
||||||
|
fn shl(&self, shift: &uint) -> $name {
|
||||||
|
let &$name(ref original) = self;
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
let word_shift = *shift / 64;
|
||||||
|
let bit_shift = *shift % 64;
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
// Shift
|
||||||
|
if bit_shift < 64 && i + word_shift < $n_words {
|
||||||
|
ret[i + word_shift] += original[i] << bit_shift;
|
||||||
|
}
|
||||||
|
// Carry
|
||||||
|
if bit_shift > 0 && i + word_shift + 1 < $n_words {
|
||||||
|
ret[i + word_shift + 1] += original[i] >> (64 - bit_shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shr<uint,$name> for $name {
|
||||||
|
#[allow(unsigned_negate)]
|
||||||
|
fn shr(&self, shift: &uint) -> $name {
|
||||||
|
let &$name(ref original) = self;
|
||||||
|
let mut ret = [0u64, ..$n_words];
|
||||||
|
let word_shift = *shift / 64;
|
||||||
|
let bit_shift = *shift % 64;
|
||||||
|
for i in range(0u, $n_words) {
|
||||||
|
// Shift
|
||||||
|
if bit_shift < 64 && i - word_shift < $n_words {
|
||||||
|
ret[i - word_shift] += original[i] >> bit_shift;
|
||||||
|
}
|
||||||
|
// Carry
|
||||||
|
if bit_shift > 0 && i - word_shift - 1 < $n_words {
|
||||||
|
ret[i - word_shift - 1] += original[i] << (64 - bit_shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$name(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for $name {
|
||||||
|
fn eq(&self, other: &$name) -> bool {
|
||||||
|
let &$name(ref arr1) = self;
|
||||||
|
let &$name(ref arr2) = other;
|
||||||
|
for i in range(0, $n_words) {
|
||||||
|
if arr1[i] != arr2[i] { return false; }
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for $name {}
|
||||||
|
|
||||||
|
impl Ord for $name {
|
||||||
|
fn cmp(&self, other: &$name) -> Ordering {
|
||||||
|
let &$name(ref me) = self;
|
||||||
|
let &$name(ref you) = other;
|
||||||
|
for i in range(0, $n_words) {
|
||||||
|
if me[3 - i] < you[3 - i] { return Less; }
|
||||||
|
if me[3 - i] > you[3 - i] { return Greater; }
|
||||||
|
}
|
||||||
|
return Equal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for $name {
|
||||||
|
fn partial_cmp(&self, other: &$name) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Show for $name {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.serialize().as_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serializable for $name {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let vec = unsafe { transmute::<$name, [u8, ..($n_words*8)]>(*self) };
|
||||||
|
vec.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<I: Iterator<u8>>(mut iter: I) -> IoResult<$name> {
|
||||||
|
let ret: [u8, ..($n_words*8)] = try!(Serializable::deserialize(iter.by_ref()));
|
||||||
|
Ok(unsafe { transmute(ret) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
)
|
||||||
|
|
||||||
|
construct_uint!(Uint256, 4)
|
||||||
|
construct_uint!(Uint128, 2)
|
||||||
|
|
||||||
|
impl Uint256 {
|
||||||
|
/// Increment by 1
|
||||||
|
#[inline]
|
||||||
|
pub fn increment(&mut self) {
|
||||||
|
let &Uint256(ref mut arr) = self;
|
||||||
|
arr[0] += 1;
|
||||||
|
if arr[0] == 0 {
|
||||||
|
arr[1] += 1;
|
||||||
|
if arr[1] == 0 {
|
||||||
|
arr[2] += 1;
|
||||||
|
if arr[2] == 0 {
|
||||||
|
arr[3] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::io::IoResult;
|
||||||
|
use std::num::from_u64;
|
||||||
|
|
||||||
|
use network::serialize::Serializable;
|
||||||
|
use util::uint::Uint256;
|
||||||
|
use util::BitArray;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn uint256_bits_test() {
|
||||||
|
assert_eq!(from_u64::<Uint256>(255).unwrap().bits(), 8);
|
||||||
|
assert_eq!(from_u64::<Uint256>(256).unwrap().bits(), 9);
|
||||||
|
assert_eq!(from_u64::<Uint256>(300).unwrap().bits(), 9);
|
||||||
|
assert_eq!(from_u64::<Uint256>(60000).unwrap().bits(), 16);
|
||||||
|
assert_eq!(from_u64::<Uint256>(70000).unwrap().bits(), 17);
|
||||||
|
|
||||||
|
// Try to read the following lines out loud quickly
|
||||||
|
let mut shl: Uint256 = from_u64(70000).unwrap();
|
||||||
|
shl = shl << 100u;
|
||||||
|
assert_eq!(shl.bits(), 117);
|
||||||
|
shl = shl << 100u;
|
||||||
|
assert_eq!(shl.bits(), 217);
|
||||||
|
shl = shl << 100u;
|
||||||
|
assert_eq!(shl.bits(), 0);
|
||||||
|
|
||||||
|
// Bit set check
|
||||||
|
assert!(!from_u64::<Uint256>(10).unwrap().bit(0));
|
||||||
|
assert!(from_u64::<Uint256>(10).unwrap().bit(1));
|
||||||
|
assert!(!from_u64::<Uint256>(10).unwrap().bit(2));
|
||||||
|
assert!(from_u64::<Uint256>(10).unwrap().bit(3));
|
||||||
|
assert!(!from_u64::<Uint256>(10).unwrap().bit(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn uint256_comp_test() {
|
||||||
|
let small = Uint256([10u64, 0, 0, 0]);
|
||||||
|
let big = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||||
|
let bigger = Uint256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||||
|
let biggest = Uint256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]);
|
||||||
|
|
||||||
|
assert!(small < big);
|
||||||
|
assert!(big < bigger);
|
||||||
|
assert!(bigger < biggest);
|
||||||
|
assert!(bigger <= biggest);
|
||||||
|
assert!(biggest <= biggest);
|
||||||
|
assert!(bigger >= big);
|
||||||
|
assert!(bigger >= small);
|
||||||
|
assert!(small <= small);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn uint256_arithmetic_test() {
|
||||||
|
let init: Uint256 = from_u64(0xDEADBEEFDEADBEEF).unwrap();
|
||||||
|
let copy = init;
|
||||||
|
|
||||||
|
let add = init.add(©);
|
||||||
|
assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0]));
|
||||||
|
// Bitshifts
|
||||||
|
let shl = add << 88u;
|
||||||
|
assert_eq!(shl, Uint256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0]));
|
||||||
|
let shr = shl >> 40u;
|
||||||
|
assert_eq!(shr, Uint256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||||
|
// Increment
|
||||||
|
let mut incr = shr;
|
||||||
|
incr.increment();
|
||||||
|
assert_eq!(incr, Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]));
|
||||||
|
// Subtraction
|
||||||
|
let sub = incr.sub(&init);
|
||||||
|
assert_eq!(sub, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||||
|
// Multiplication
|
||||||
|
let mult = sub.mul_u32(300);
|
||||||
|
assert_eq!(mult, Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]));
|
||||||
|
// Division
|
||||||
|
assert_eq!(from_u64::<Uint256>(105).unwrap() /
|
||||||
|
from_u64::<Uint256>(5).unwrap(),
|
||||||
|
from_u64::<Uint256>(21).unwrap());
|
||||||
|
let div = mult / from_u64::<Uint256>(300).unwrap();
|
||||||
|
assert_eq!(div, Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]));
|
||||||
|
// TODO: bit inversion
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn uint256_bitslice_test() {
|
||||||
|
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||||
|
let add = init + (init << 64);
|
||||||
|
assert_eq!(add.bit_slice(64, 128), init);
|
||||||
|
assert_eq!(add.mask(64), init);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn uint256_extreme_bitshift_test() {
|
||||||
|
// Shifting a u64 by 64 bits gives an undefined value, so make sure that
|
||||||
|
// we're doing the Right Thing here
|
||||||
|
let init = from_u64::<Uint256>(0xDEADBEEFDEADBEEF).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
|
let add = (init << 64).add(&init);
|
||||||
|
assert_eq!(add, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
|
assert_eq!(add >> 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
|
assert_eq!(add << 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
|
||||||
|
assert_eq!(add >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0]));
|
||||||
|
assert_eq!(add << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn uint256_serialize_test() {
|
||||||
|
let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
|
||||||
|
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
|
||||||
|
let serial1 = start1.serialize();
|
||||||
|
let serial2 = start2.serialize();
|
||||||
|
let end1: IoResult<Uint256> = Serializable::deserialize(serial1.iter().map(|n| *n));
|
||||||
|
let end2: IoResult<Uint256> = Serializable::deserialize(serial2.iter().map(|n| *n));
|
||||||
|
|
||||||
|
assert_eq!(end1, Ok(start1));
|
||||||
|
assert_eq!(end2, Ok(start2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue