From c9ad7c0b58393dc088ec6535c684ad9029888bde Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 18 Jul 2014 06:56:17 -0700 Subject: [PATCH] Initial commit, move into Cargo --- Cargo.toml | 18 + src/blockdata/block.rs | 158 +++++++ src/blockdata/blockchain.rs | 624 ++++++++++++++++++++++++++ src/blockdata/constants.rs | 140 ++++++ src/blockdata/mod.rs | 29 ++ src/blockdata/opcodes.rs | 28 ++ src/blockdata/script.rs | 181 ++++++++ src/blockdata/transaction.rs | 107 +++++ src/blockdata/utxoset.rs | 356 +++++++++++++++ src/internal_macros.rs | 44 ++ src/lib.rs | 65 +++ src/macros.rs | 84 ++++ src/network/address.rs | 100 +++++ src/network/constants.rs | 26 ++ src/network/listener.rs | 87 ++++ src/network/message.rs | 203 +++++++++ src/network/message_blockdata.rs | 169 ++++++++ src/network/message_network.rs | 140 ++++++ src/network/mod.rs | 30 ++ src/network/serialize.rs | 722 +++++++++++++++++++++++++++++++ src/network/socket.rs | 211 +++++++++ src/util/hash.rs | 204 +++++++++ src/util/iter.rs | 147 +++++++ src/util/misc.rs | 90 ++++ src/util/mod.rs | 41 ++ src/util/patricia_tree.rs | 546 +++++++++++++++++++++++ src/util/thinvec.rs | 265 ++++++++++++ src/util/uint.rs | 483 +++++++++++++++++++++ 28 files changed, 5298 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/blockdata/block.rs create mode 100644 src/blockdata/blockchain.rs create mode 100644 src/blockdata/constants.rs create mode 100644 src/blockdata/mod.rs create mode 100644 src/blockdata/opcodes.rs create mode 100644 src/blockdata/script.rs create mode 100644 src/blockdata/transaction.rs create mode 100644 src/blockdata/utxoset.rs create mode 100644 src/internal_macros.rs create mode 100644 src/lib.rs create mode 100644 src/macros.rs create mode 100644 src/network/address.rs create mode 100644 src/network/constants.rs create mode 100644 src/network/listener.rs create mode 100644 src/network/message.rs create mode 100644 src/network/message_blockdata.rs create mode 100644 src/network/message_network.rs create mode 100644 src/network/mod.rs create mode 100644 src/network/serialize.rs create mode 100644 src/network/socket.rs create mode 100644 src/util/hash.rs create mode 100644 src/util/iter.rs create mode 100644 src/util/misc.rs create mode 100644 src/util/mod.rs create mode 100644 src/util/patricia_tree.rs create mode 100644 src/util/thinvec.rs create mode 100644 src/util/uint.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..e25ba9ce --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ + +[package] + +name = "bitcoin-rs" +version = "0.0.1" +authors = ["Andrew Poelstra "] + + +[[lib]] + +name = "bitcoin" +path = "src/lib.rs" + + +[dependencies.rust-crypto] + +git = "https://github.com/DaGenix/rust-crypto.git" + diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs new file mode 100644 index 00000000..7dc8aadd --- /dev/null +++ b/src/blockdata/block.rs @@ -0,0 +1,158 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Bitcoin 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 +} + +/// 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::(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 = Serializable::deserialize(some_block.iter().map(|n| *n)); + let bad_decode: IoResult = 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()); + } +} + diff --git a/src/blockdata/blockchain.rs b/src/blockdata/blockchain.rs new file mode 100644 index 00000000..ed427d18 --- /dev/null +++ b/src/blockdata/blockchain.rs @@ -0,0 +1,624 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Bitcoin Blockchain +//! +//! This module provides the structures and functions to maintain the +//! blockchain. +//! +//! 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, Uint256>; +type NodePtr = Option>; + +/// 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, + /// Pointer to block's child + next: RefCell +} + +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) { + 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 { + fn serialize(&self) -> Vec { + 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>(mut iter: I) -> IoResult> { + 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, + best_hash: Sha256dHash, + genesis_hash: Sha256dHash +} + +impl Serializable for Blockchain { + fn serialize(&self) -> Vec { + 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>(mut iter: I) -> IoResult { + 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, tree: &'tree BlockTree) -> LocatorHashIter<'tree> { + LocatorHashIter { index: Some(init), tree: tree, count: 0, skip: 1 } + } +} + +impl<'tree> Iterator for LocatorHashIter<'tree> { + fn next(&mut self) -> Option { + 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, 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 = 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> { + 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) { + 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 { + 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 = 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()); + } +} + + + diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs new file mode 100644 index 00000000..6c679da2 --- /dev/null +++ b/src/blockdata/constants.rs @@ -0,0 +1,140 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # 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::(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::>(), + 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::>(), + 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::>(), + hex_bytes("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap()); + } +} + diff --git a/src/blockdata/mod.rs b/src/blockdata/mod.rs new file mode 100644 index 00000000..56639eee --- /dev/null +++ b/src/blockdata/mod.rs @@ -0,0 +1,29 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # 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; + + diff --git a/src/blockdata/opcodes.rs b/src/blockdata/opcodes.rs new file mode 100644 index 00000000..a73039e8 --- /dev/null +++ b/src/blockdata/opcodes.rs @@ -0,0 +1,28 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # 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; + + diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs new file mode 100644 index 00000000..6835b804 --- /dev/null +++ b/src/blockdata/script.rs @@ -0,0 +1,181 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # 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); + +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 { + let &Script(ref data) = self; + data.serialize() + } + + fn deserialize>(iter: I) -> IoResult