diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs index 529b3f30..a049459a 100644 --- a/src/blockdata/block.rs +++ b/src/blockdata/block.rs @@ -101,7 +101,7 @@ impl BlockHeader { if target != required_target { return Err(SpvBadTarget); } - let ref hash = self.hash().as_uint256(); + let ref hash = self.bitcoin_hash().as_uint256(); if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) } } diff --git a/src/blockdata/blockchain.rs b/src/blockdata/blockchain.rs index 0027969b..2f01696b 100644 --- a/src/blockdata/blockchain.rs +++ b/src/blockdata/blockchain.rs @@ -106,8 +106,8 @@ impl Serializable for BlockchainNode { // 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() + fn bitcoin_hash(&self) -> Sha256dHash { + self.block.header.bitcoin_hash() } } @@ -186,7 +186,7 @@ impl Serializable for Blockchain { } // Check that "genesis" is the genesis - if (*scan).block.header.hash() != genesis_hash { + if (*scan).bitcoin_hash() != genesis_hash { return Err(IoError { kind: OtherIoError, desc: "best tip did not link back to genesis", @@ -225,7 +225,7 @@ impl Iterator for LocatorHashIter { if self.index.is_null() { return None; } - let ret = Some(unsafe { (*self.index).hash() }); + let ret = Some(unsafe { (*self.index).bitcoin_hash() }); // Rewind once (if we are at the genesis, this will set self.index to None) self.index = unsafe { (*self.index).prev }; @@ -357,7 +357,7 @@ impl Blockchain { /// Constructs a new blockchain pub fn new(network: Network) -> Blockchain { let genesis = genesis_block(network); - let genhash = genesis.header.hash(); + let genhash = genesis.header.bitcoin_hash(); let new_node = box BlockchainNode { total_work: Zero::zero(), required_difficulty: genesis.header.target(), @@ -422,7 +422,7 @@ impl Blockchain { /// Locates a block in the chain and overwrites its txdata pub fn add_txdata(&mut self, block: Block) -> BitcoinResult<()> { - self.replace_txdata(&block.header.hash().as_uint256(), block.txdata, true) + self.replace_txdata(&block.header.bitcoin_hash().as_uint256(), block.txdata, true) } /// Locates a block in the chain and removes its txdata @@ -453,7 +453,7 @@ impl Blockchain { // 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() { + if self.tree.lookup(&block.header.bitcoin_hash().as_uint256(), 256).is_some() { return Err(DuplicateHash); } // Construct node, if possible @@ -532,7 +532,7 @@ impl Blockchain { // Insert the new block let raw_ptr = &*new_block as NodePtr; - self.tree.insert(&new_block.block.header.hash().as_uint256(), 256, new_block); + self.tree.insert(&new_block.block.header.bitcoin_hash().as_uint256(), 256, new_block); // Replace the best tip if necessary if unsafe { (*raw_ptr).total_work > (*self.best_tip).total_work } { self.set_best_tip(raw_ptr); @@ -556,7 +556,7 @@ impl Blockchain { } } // Set best - self.best_hash = unsafe { (*tip).hash() }; + self.best_hash = unsafe { (*tip).bitcoin_hash() }; self.best_tip = tip; } @@ -632,8 +632,8 @@ mod tests { #[test] fn blockchain_serialize_test() { let empty_chain = Blockchain::new(Bitcoin); - assert_eq!(empty_chain.best_tip().header.hash().serialize(), - genesis_block(Bitcoin).header.hash().serialize()); + assert_eq!(empty_chain.best_tip().header.bitcoin_hash().serialize(), + genesis_block(Bitcoin).header.bitcoin_hash().serialize()); let serial = empty_chain.serialize(); assert_eq!(serial, empty_chain.serialize_iter().collect()); @@ -641,8 +641,8 @@ mod tests { 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().header.hash().serialize(), - genesis_block(Bitcoin).header.hash().serialize()); + assert_eq!(read_chain.best_tip().header.bitcoin_hash().serialize(), + genesis_block(Bitcoin).header.bitcoin_hash().serialize()); } } diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs index a47d41cc..59c032ff 100644 --- a/src/blockdata/constants.rs +++ b/src/blockdata/constants.rs @@ -28,7 +28,7 @@ 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::hash::{MerkleRoot, zero_hash}; use util::uint::Uint256; pub static MAX_SEQUENCE: u32 = 0xFFFFFFFF; @@ -86,7 +86,7 @@ pub fn genesis_block(network: Network) -> Block { header: BlockHeader { version: 1, prev_blockhash: zero_hash(), - merkle_root: merkle_root(txdata.as_slice()), + merkle_root: txdata.merkle_root(), time: 1231006505, bits: 0x1d00ffff, nonce: 2083236893 @@ -100,7 +100,7 @@ pub fn genesis_block(network: Network) -> Block { header: BlockHeader { version: 1, prev_blockhash: zero_hash(), - merkle_root: merkle_root(txdata.as_slice()), + merkle_root: txdata.merkle_root(), time: 1296688602, bits: 0x1d00ffff, nonce: 414098458 @@ -138,8 +138,8 @@ mod test { 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()); + assert_eq!(gen.bitcoin_hash().le_hex_string(), + "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string()); } #[test] @@ -153,7 +153,7 @@ mod test { assert_eq!(gen.header.time, 1231006505); assert_eq!(gen.header.bits, 0x1d00ffff); assert_eq!(gen.header.nonce, 2083236893); - assert_eq!(gen.header.hash().le_hex_string(), + assert_eq!(gen.header.bitcoin_hash().le_hex_string(), "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string()); } @@ -167,7 +167,7 @@ mod test { assert_eq!(gen.header.time, 1296688602); assert_eq!(gen.header.bits, 0x1d00ffff); assert_eq!(gen.header.nonce, 414098458); - assert_eq!(gen.header.hash().le_hex_string(), + assert_eq!(gen.header.bitcoin_hash().le_hex_string(), "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string()); } } diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index fc578f06..2be3bd32 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -91,16 +91,15 @@ fn test_transaction() { 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::>(), - hex_bytes("ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1").unwrap()); + // as little-endian 256-bit numbers rather than as data strings. + assert_eq!(realtx.input[0].prev_hash.le_hex_string(), + "ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string()); 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::>(), - hex_bytes("a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7").unwrap()); + assert_eq!(realtx.bitcoin_hash().le_hex_string(), + "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()); } diff --git a/src/blockdata/utxoset.rs b/src/blockdata/utxoset.rs index 866c0e7d..ab6864b0 100644 --- a/src/blockdata/utxoset.rs +++ b/src/blockdata/utxoset.rs @@ -60,7 +60,7 @@ impl UtxoSet { // while the reference client won't, causing us to fork off the network. UtxoSet { tree: PatriciaTree::new(), - last_hash: genesis_block(network).header.hash(), + last_hash: genesis_block(network).header.bitcoin_hash(), spent_txos: Vec::from_elem(rewind_limit, vec![]), spent_idx: 0, n_utxos: 0 @@ -69,7 +69,7 @@ impl UtxoSet { /// Add all the UTXOs of a transaction to the set fn add_utxos(&mut self, tx: &Transaction) -> bool { - let txid = tx.hash(); + let txid = tx.bitcoin_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() { @@ -136,7 +136,7 @@ impl UtxoSet { // 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(); + self.last_hash = block.header.bitcoin_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(); @@ -165,11 +165,11 @@ impl UtxoSet { // 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(); + let blockhash = block.header.bitcoin_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.tree.delete(&tx.bitcoin_hash().as_uint128(), KEY_LEN); self.add_utxos(tx); } else { // Otherwise fail the block @@ -185,7 +185,7 @@ impl UtxoSet { /// 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() { + if self.last_hash != block.header.bitcoin_hash() { return false; } @@ -200,7 +200,7 @@ impl UtxoSet { // Delete added txouts let mut skipped_genesis = false; for tx in block.txdata.iter() { - let txhash = tx.hash(); + let txhash = tx.bitcoin_hash(); for n in range(0, tx.output.len()) { // Just bomb out the whole transaction self.take_utxo(txhash, n as u32); @@ -290,12 +290,12 @@ mod tests { for tx in new_block.txdata.iter() { empty_set.add_utxos(tx); } - empty_set.last_hash = new_block.header.hash(); + empty_set.last_hash = new_block.header.bitcoin_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(); + let hash = tx.bitcoin_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())); @@ -307,7 +307,7 @@ mod tests { assert!(!empty_set.update(&new_block)); assert_eq!(empty_set.n_utxos(), 2); for tx in new_block.txdata.iter() { - let hash = tx.hash(); + let hash = tx.bitcoin_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())); @@ -324,7 +324,7 @@ mod tests { // Check that all outputs are there let mut read_set = deserial.unwrap(); for tx in new_block.txdata.iter() { - let hash = tx.hash(); + let hash = tx.bitcoin_hash(); for (n, out) in tx.output.iter().enumerate() { let n = n as u32; @@ -343,7 +343,7 @@ mod tests { assert!(read_again.rewind(&new_block)); assert_eq!(read_again.n_utxos(), 0); for tx in new_block.txdata.iter() { - let hash = tx.hash(); + let hash = tx.bitcoin_hash(); for n in range(0, tx.output.len()) { let n = n as u32; diff --git a/src/network/serialize.rs b/src/network/serialize.rs index 09b0b2fa..45434eed 100644 --- a/src/network/serialize.rs +++ b/src/network/serialize.rs @@ -98,7 +98,7 @@ pub trait Serializable { /// Read an object off the wire fn deserialize>(iter: I) -> IoResult; /// Obtain a hash of the object - fn hash(&self) -> Sha256dHash { + fn bitcoin_hash(&self) -> Sha256dHash { Sha256dHash::from_data(self.serialize().as_slice()) } /// Dump the object to a file diff --git a/src/util/hash.rs b/src/util/hash.rs index ac474508..e43b9923 100644 --- a/src/util/hash.rs +++ b/src/util/hash.rs @@ -21,6 +21,8 @@ use core::cmp::min; use std::fmt; use std::io::{IoResult, IoError, InvalidInput}; use std::mem::transmute; +use std::hash::sip::SipState; +use std::hash::Hash; use crypto::digest::Digest; use crypto::sha2; @@ -33,6 +35,16 @@ use util::uint::Uint256; /// A Bitcoin hash, 32-bytes, computed from x as SHA256(SHA256(x)) pub struct Sha256dHash([u8, ..32]); +/// Allow this to be used as a key for Rust's HashMap et. al. +impl Hash for Sha256dHash { + fn hash(&self, state: &mut SipState) { + let &Sha256dHash(ref data) = self; + for ch in data.iter() { + ch.hash(state); + } + } +} + /// Returns the all-zeroes "hash" pub fn zero_hash() -> Sha256dHash { Sha256dHash([0u8, ..32]) } @@ -148,12 +160,15 @@ impl fmt::Show for Sha256dHash { } } -//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 Vec { - /// 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(data: &[T]) -> Sha256dHash { +/// Any collection of objects for which a merkle root makes sense to calculate +pub trait MerkleRoot { + /// Construct a merkle tree from a collection, with elements ordered as + /// they were in the original collection, and return the merkle root. + fn merkle_root(&self) -> Sha256dHash; +} + +impl<'a, T: Serializable> MerkleRoot for &'a [T] { + fn merkle_root(&self) -> Sha256dHash { fn merkle_root(data: Vec) -> Sha256dHash { // Base case if data.len() < 1 { @@ -167,14 +182,21 @@ impl fmt::Show for Sha256dHash { 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()); + let to_hash = data[idx1].bitcoin_hash().serialize() + .append(data[idx2].bitcoin_hash().serialize().as_slice()); + next.push(to_hash.bitcoin_hash()); } merkle_root(next) } - merkle_root(data.iter().map(|obj| obj.hash()).collect()) + merkle_root(self.iter().map(|obj| obj.bitcoin_hash()).collect()) } -//} +} + +impl MerkleRoot for Vec { + fn merkle_root(&self) -> Sha256dHash { + self.as_slice().merkle_root() + } +} #[cfg(test)]