Rename Serializable::hash() to Serializable::bitcoin_hash()

We were conflicting with the Rust stdlib trait Hash, which is used
by various datastructures which need a general hash. Also implement
Hash for Sha256dHash so that we can use bitcoin hashes as keys for
such data structures.
This commit is contained in:
Andrew Poelstra 2014-07-19 16:03:45 -07:00
parent a3846965e3
commit 54e4ea4586
7 changed files with 71 additions and 50 deletions

View File

@ -101,7 +101,7 @@ impl BlockHeader {
if target != required_target { if target != required_target {
return Err(SpvBadTarget); 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) } if hash <= target { Ok(()) } else { Err(SpvBadProofOfWork) }
} }

View File

@ -106,8 +106,8 @@ impl Serializable for BlockchainNode {
// Override Serialize::hash to return the blockheader hash, since the // Override Serialize::hash to return the blockheader hash, since the
// hash of the node itself is pretty much meaningless. // hash of the node itself is pretty much meaningless.
fn hash(&self) -> Sha256dHash { fn bitcoin_hash(&self) -> Sha256dHash {
self.block.header.hash() self.block.header.bitcoin_hash()
} }
} }
@ -186,7 +186,7 @@ impl Serializable for Blockchain {
} }
// Check that "genesis" is the genesis // Check that "genesis" is the genesis
if (*scan).block.header.hash() != genesis_hash { if (*scan).bitcoin_hash() != genesis_hash {
return Err(IoError { return Err(IoError {
kind: OtherIoError, kind: OtherIoError,
desc: "best tip did not link back to genesis", desc: "best tip did not link back to genesis",
@ -225,7 +225,7 @@ impl Iterator<Sha256dHash> for LocatorHashIter {
if self.index.is_null() { if self.index.is_null() {
return None; 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) // Rewind once (if we are at the genesis, this will set self.index to None)
self.index = unsafe { (*self.index).prev }; self.index = unsafe { (*self.index).prev };
@ -357,7 +357,7 @@ impl Blockchain {
/// Constructs a new blockchain /// Constructs a new blockchain
pub fn new(network: Network) -> Blockchain { pub fn new(network: Network) -> Blockchain {
let genesis = genesis_block(network); let genesis = genesis_block(network);
let genhash = genesis.header.hash(); let genhash = genesis.header.bitcoin_hash();
let new_node = box BlockchainNode { let new_node = box BlockchainNode {
total_work: Zero::zero(), total_work: Zero::zero(),
required_difficulty: genesis.header.target(), required_difficulty: genesis.header.target(),
@ -422,7 +422,7 @@ impl Blockchain {
/// Locates a block in the chain and overwrites its txdata /// Locates a block in the chain and overwrites its txdata
pub fn add_txdata(&mut self, block: Block) -> BitcoinResult<()> { 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 /// 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 // Check for multiple inserts (bitcoind from c9a09183 to 3c85d2ec doesn't
// handle locator hashes properly and may return blocks multiple times, // handle locator hashes properly and may return blocks multiple times,
// and this may also happen in case of a reorg. // 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); return Err(DuplicateHash);
} }
// Construct node, if possible // Construct node, if possible
@ -532,7 +532,7 @@ impl Blockchain {
// Insert the new block // Insert the new block
let raw_ptr = &*new_block as NodePtr; 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 // Replace the best tip if necessary
if unsafe { (*raw_ptr).total_work > (*self.best_tip).total_work } { if unsafe { (*raw_ptr).total_work > (*self.best_tip).total_work } {
self.set_best_tip(raw_ptr); self.set_best_tip(raw_ptr);
@ -556,7 +556,7 @@ impl Blockchain {
} }
} }
// Set best // Set best
self.best_hash = unsafe { (*tip).hash() }; self.best_hash = unsafe { (*tip).bitcoin_hash() };
self.best_tip = tip; self.best_tip = tip;
} }
@ -632,8 +632,8 @@ mod tests {
#[test] #[test]
fn blockchain_serialize_test() { fn blockchain_serialize_test() {
let empty_chain = Blockchain::new(Bitcoin); let empty_chain = Blockchain::new(Bitcoin);
assert_eq!(empty_chain.best_tip().header.hash().serialize(), assert_eq!(empty_chain.best_tip().header.bitcoin_hash().serialize(),
genesis_block(Bitcoin).header.hash().serialize()); genesis_block(Bitcoin).header.bitcoin_hash().serialize());
let serial = empty_chain.serialize(); let serial = empty_chain.serialize();
assert_eq!(serial, empty_chain.serialize_iter().collect()); assert_eq!(serial, empty_chain.serialize_iter().collect());
@ -641,8 +641,8 @@ mod tests {
let deserial: IoResult<Blockchain> = Serializable::deserialize(serial.iter().map(|n| *n)); let deserial: IoResult<Blockchain> = Serializable::deserialize(serial.iter().map(|n| *n));
assert!(deserial.is_ok()); assert!(deserial.is_ok());
let read_chain = deserial.unwrap(); let read_chain = deserial.unwrap();
assert_eq!(read_chain.best_tip().header.hash().serialize(), assert_eq!(read_chain.best_tip().header.bitcoin_hash().serialize(),
genesis_block(Bitcoin).header.hash().serialize()); genesis_block(Bitcoin).header.bitcoin_hash().serialize());
} }
} }

View File

@ -28,7 +28,7 @@ use blockdata::script::Script;
use blockdata::transaction::{Transaction, TxOut, TxIn}; use blockdata::transaction::{Transaction, TxOut, TxIn};
use blockdata::block::{Block, BlockHeader}; use blockdata::block::{Block, BlockHeader};
use util::misc::hex_bytes; use util::misc::hex_bytes;
use util::hash::{merkle_root, zero_hash}; use util::hash::{MerkleRoot, zero_hash};
use util::uint::Uint256; use util::uint::Uint256;
pub static MAX_SEQUENCE: u32 = 0xFFFFFFFF; pub static MAX_SEQUENCE: u32 = 0xFFFFFFFF;
@ -86,7 +86,7 @@ pub fn genesis_block(network: Network) -> Block {
header: BlockHeader { header: BlockHeader {
version: 1, version: 1,
prev_blockhash: zero_hash(), prev_blockhash: zero_hash(),
merkle_root: merkle_root(txdata.as_slice()), merkle_root: txdata.merkle_root(),
time: 1231006505, time: 1231006505,
bits: 0x1d00ffff, bits: 0x1d00ffff,
nonce: 2083236893 nonce: 2083236893
@ -100,7 +100,7 @@ pub fn genesis_block(network: Network) -> Block {
header: BlockHeader { header: BlockHeader {
version: 1, version: 1,
prev_blockhash: zero_hash(), prev_blockhash: zero_hash(),
merkle_root: merkle_root(txdata.as_slice()), merkle_root: txdata.merkle_root(),
time: 1296688602, time: 1296688602,
bits: 0x1d00ffff, bits: 0x1d00ffff,
nonce: 414098458 nonce: 414098458
@ -138,8 +138,8 @@ mod test {
assert_eq!(gen.output[0].value, 50 * COIN_VALUE); assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
assert_eq!(gen.lock_time, 0); assert_eq!(gen.lock_time, 0);
assert_eq!(gen.hash().serialize().iter().rev().map(|n| *n).collect::<Vec<u8>>(), assert_eq!(gen.bitcoin_hash().le_hex_string(),
hex_bytes("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap()); "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
} }
#[test] #[test]
@ -153,7 +153,7 @@ mod test {
assert_eq!(gen.header.time, 1231006505); assert_eq!(gen.header.time, 1231006505);
assert_eq!(gen.header.bits, 0x1d00ffff); assert_eq!(gen.header.bits, 0x1d00ffff);
assert_eq!(gen.header.nonce, 2083236893); 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()); "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
} }
@ -167,7 +167,7 @@ mod test {
assert_eq!(gen.header.time, 1296688602); assert_eq!(gen.header.time, 1296688602);
assert_eq!(gen.header.bits, 0x1d00ffff); assert_eq!(gen.header.bits, 0x1d00ffff);
assert_eq!(gen.header.nonce, 414098458); 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()); "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
} }
} }

View File

@ -91,16 +91,15 @@ fn test_transaction() {
assert_eq!(realtx.version, 1); assert_eq!(realtx.version, 1);
assert_eq!(realtx.input.len(), 1); assert_eq!(realtx.input.len(), 1);
// In particular this one is easy to get backward -- in bitcoin hashes are encoded // 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 // as little-endian 256-bit numbers rather than as data strings.
// have this crazy .iter().rev() thing going on in many hash-related tests. assert_eq!(realtx.input[0].prev_hash.le_hex_string(),
assert_eq!(realtx.input[0].prev_hash.as_slice().iter().rev().map(|n| *n).collect::<Vec<u8>>(), "ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string());
hex_bytes("ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1").unwrap());
assert_eq!(realtx.input[0].prev_index, 1); assert_eq!(realtx.input[0].prev_index, 1);
assert_eq!(realtx.output.len(), 1); assert_eq!(realtx.output.len(), 1);
assert_eq!(realtx.lock_time, 0); assert_eq!(realtx.lock_time, 0);
assert_eq!(realtx.hash().serialize().iter().rev().map(|n| *n).collect::<Vec<u8>>(), assert_eq!(realtx.bitcoin_hash().le_hex_string(),
hex_bytes("a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7").unwrap()); "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
} }

View File

@ -60,7 +60,7 @@ impl UtxoSet {
// while the reference client won't, causing us to fork off the network. // while the reference client won't, causing us to fork off the network.
UtxoSet { UtxoSet {
tree: PatriciaTree::new(), 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_txos: Vec::from_elem(rewind_limit, vec![]),
spent_idx: 0, spent_idx: 0,
n_utxos: 0 n_utxos: 0
@ -69,7 +69,7 @@ impl UtxoSet {
/// Add all the UTXOs of a transaction to the set /// Add all the UTXOs of a transaction to the set
fn add_utxos(&mut self, tx: &Transaction) -> bool { fn add_utxos(&mut self, tx: &Transaction) -> bool {
let txid = tx.hash(); let txid = tx.bitcoin_hash();
// Locate node if it's already there // Locate node if it's already there
let mut new_node = ThinVec::with_capacity(tx.output.len() as u32); let mut new_node = ThinVec::with_capacity(tx.output.len() as u32);
for (vout, txo) in tx.output.iter().enumerate() { 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, // Set the next hash immediately so that if anything goes wrong,
// we can rewind from the point that we're at. // 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; let spent_idx = self.spent_idx as uint;
self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64; self.spent_idx = (self.spent_idx + 1) % self.spent_txos.len() as u64;
self.spent_txos.get_mut(spent_idx).clear(); self.spent_txos.get_mut(spent_idx).clear();
@ -165,11 +165,11 @@ impl UtxoSet {
// dupes were noticed. See bitcoind commit `ab91bf39` and BIP30. // dupes were noticed. See bitcoind commit `ab91bf39` and BIP30.
// TODO: add a unit test for these blocks. // TODO: add a unit test for these blocks.
if !self.add_utxos(tx) { 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() || if blockhash == "00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec".to_string() ||
blockhash == "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721".to_string() { blockhash == "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721".to_string() {
// For these specific blocks, overwrite the old UTXOs. // 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); self.add_utxos(tx);
} else { } else {
// Otherwise fail the block // Otherwise fail the block
@ -185,7 +185,7 @@ impl UtxoSet {
/// Unapply the transactions contained in a block /// Unapply the transactions contained in a block
pub fn rewind(&mut self, block: &Block) -> bool { pub fn rewind(&mut self, block: &Block) -> bool {
// Make sure we are rewinding the latest block // 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; return false;
} }
@ -200,7 +200,7 @@ impl UtxoSet {
// Delete added txouts // Delete added txouts
let mut skipped_genesis = false; let mut skipped_genesis = false;
for tx in block.txdata.iter() { for tx in block.txdata.iter() {
let txhash = tx.hash(); let txhash = tx.bitcoin_hash();
for n in range(0, tx.output.len()) { for n in range(0, tx.output.len()) {
// Just bomb out the whole transaction // Just bomb out the whole transaction
self.take_utxo(txhash, n as u32); self.take_utxo(txhash, n as u32);
@ -290,12 +290,12 @@ mod tests {
for tx in new_block.txdata.iter() { for tx in new_block.txdata.iter() {
empty_set.add_utxos(tx); 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 // Check that all the UTXOs were added
assert_eq!(empty_set.n_utxos(), 2); assert_eq!(empty_set.n_utxos(), 2);
for tx in new_block.txdata.iter() { for tx in new_block.txdata.iter() {
let hash = tx.hash(); let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() { for (n, out) in tx.output.iter().enumerate() {
let n = n as u32; let n = n as u32;
assert_eq!(empty_set.get_utxo(hash, n), Some(&box out.clone())); 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!(!empty_set.update(&new_block));
assert_eq!(empty_set.n_utxos(), 2); assert_eq!(empty_set.n_utxos(), 2);
for tx in new_block.txdata.iter() { for tx in new_block.txdata.iter() {
let hash = tx.hash(); let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() { for (n, out) in tx.output.iter().enumerate() {
let n = n as u32; let n = n as u32;
assert_eq!(empty_set.get_utxo(hash, n), Some(&box out.clone())); assert_eq!(empty_set.get_utxo(hash, n), Some(&box out.clone()));
@ -324,7 +324,7 @@ mod tests {
// Check that all outputs are there // Check that all outputs are there
let mut read_set = deserial.unwrap(); let mut read_set = deserial.unwrap();
for tx in new_block.txdata.iter() { for tx in new_block.txdata.iter() {
let hash = tx.hash(); let hash = tx.bitcoin_hash();
for (n, out) in tx.output.iter().enumerate() { for (n, out) in tx.output.iter().enumerate() {
let n = n as u32; let n = n as u32;
@ -343,7 +343,7 @@ mod tests {
assert!(read_again.rewind(&new_block)); assert!(read_again.rewind(&new_block));
assert_eq!(read_again.n_utxos(), 0); assert_eq!(read_again.n_utxos(), 0);
for tx in new_block.txdata.iter() { for tx in new_block.txdata.iter() {
let hash = tx.hash(); let hash = tx.bitcoin_hash();
for n in range(0, tx.output.len()) { for n in range(0, tx.output.len()) {
let n = n as u32; let n = n as u32;

View File

@ -98,7 +98,7 @@ pub trait Serializable {
/// Read an object off the wire /// Read an object off the wire
fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Self>; fn deserialize<I: Iterator<u8>>(iter: I) -> IoResult<Self>;
/// Obtain a hash of the object /// Obtain a hash of the object
fn hash(&self) -> Sha256dHash { fn bitcoin_hash(&self) -> Sha256dHash {
Sha256dHash::from_data(self.serialize().as_slice()) Sha256dHash::from_data(self.serialize().as_slice())
} }
/// Dump the object to a file /// Dump the object to a file

View File

@ -21,6 +21,8 @@ use core::cmp::min;
use std::fmt; use std::fmt;
use std::io::{IoResult, IoError, InvalidInput}; use std::io::{IoResult, IoError, InvalidInput};
use std::mem::transmute; use std::mem::transmute;
use std::hash::sip::SipState;
use std::hash::Hash;
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha2; use crypto::sha2;
@ -33,6 +35,16 @@ use util::uint::Uint256;
/// A Bitcoin hash, 32-bytes, computed from x as SHA256(SHA256(x)) /// A Bitcoin hash, 32-bytes, computed from x as SHA256(SHA256(x))
pub struct Sha256dHash([u8, ..32]); 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" /// Returns the all-zeroes "hash"
pub fn zero_hash() -> Sha256dHash { Sha256dHash([0u8, ..32]) } 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. /// Any collection of objects for which a merkle root makes sense to calculate
//See https://github.com/rust-lang/rust/issues/15060 for why this isn't so. pub trait MerkleRoot {
//impl<T: Serializable> Vec<T> { /// Construct a merkle tree from a collection, with elements ordered as
/// Construct a merkle tree from a vector, with elements ordered as /// they were in the original collection, and return the merkle root.
/// they were in the original vector, and return the merkle root. fn merkle_root(&self) -> Sha256dHash;
pub fn merkle_root<T: Serializable>(data: &[T]) -> Sha256dHash { }
impl<'a, T: Serializable> MerkleRoot for &'a [T] {
fn merkle_root(&self) -> Sha256dHash {
fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash { fn merkle_root(data: Vec<Sha256dHash>) -> Sha256dHash {
// Base case // Base case
if data.len() < 1 { if data.len() < 1 {
@ -167,14 +182,21 @@ impl fmt::Show for Sha256dHash {
for idx in range(0, (data.len() + 1) / 2) { for idx in range(0, (data.len() + 1) / 2) {
let idx1 = 2 * idx; let idx1 = 2 * idx;
let idx2 = min(idx1 + 1, data.len() - 1); let idx2 = min(idx1 + 1, data.len() - 1);
let to_hash = data[idx1].hash().serialize().append(data[idx2].hash().serialize().as_slice()); let to_hash = data[idx1].bitcoin_hash().serialize()
next.push(to_hash.hash()); .append(data[idx2].bitcoin_hash().serialize().as_slice());
next.push(to_hash.bitcoin_hash());
} }
merkle_root(next) merkle_root(next)
} }
merkle_root(data.iter().map(|obj| obj.hash()).collect()) merkle_root(self.iter().map(|obj| obj.bitcoin_hash()).collect())
} }
//} }
impl <T: Serializable> MerkleRoot for Vec<T> {
fn merkle_root(&self) -> Sha256dHash {
self.as_slice().merkle_root()
}
}
#[cfg(test)] #[cfg(test)]