Fix for new partial-move rules; swap hash le_hex_string and be_hex_string
I noticed that the little/big endian hex string functions for Sha256dHash did not match my intuition. What we should have is that the raw bytes correspond to a little-endian representation (since we convert to Uint256 by transmuting, and Uint256's have little-endian representation) while the reversed raw bytes are big-endian. This means that the output from `sha256sum` is "little-endian", while the standard "zeros on the left" output from bitcoind is "big-endian". This is correct since we think of blockhashes as being "below the target" when they have lots of zeros on the left, and we also notice that when hashing Bitcoin objects with sha256sum that the output hashes are always reversed. These two functions le_hex_string and be_hex_string should really not be used outside of the library; the Encodable trait should give access to a "big endian" representation while ConsensusEncodable gives access to a "little endian" representation. That way we describe the split in terms of user-facing/consensus code rather than big/little endian code, which is a better way of thinking about it. After all, a hash is a collection of bytes, not a number --- it doesn't have an intrinsic endianness. Oh, and by the way, to compute a sha256d hash from sha256sum, you do echo -n 'data' | sha256sum | xxd -r -p | sha256dsum
This commit is contained in:
parent
474d04d154
commit
2986e1f983
|
@ -139,7 +139,7 @@ mod test {
|
|||
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
|
||||
assert_eq!(gen.lock_time, 0);
|
||||
|
||||
assert_eq!(gen.bitcoin_hash().le_hex_string(),
|
||||
assert_eq!(gen.bitcoin_hash().be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
}
|
||||
|
||||
|
@ -149,12 +149,12 @@ mod test {
|
|||
|
||||
assert_eq!(gen.header.version, 1);
|
||||
assert_eq!(gen.header.prev_blockhash.as_slice(), zero_hash().as_slice());
|
||||
assert_eq!(gen.header.merkle_root.le_hex_string(),
|
||||
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
assert_eq!(gen.header.time, 1231006505);
|
||||
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||
assert_eq!(gen.header.nonce, 2083236893);
|
||||
assert_eq!(gen.header.bitcoin_hash().le_hex_string(),
|
||||
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
||||
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
|
||||
}
|
||||
|
||||
|
@ -163,12 +163,12 @@ mod test {
|
|||
let gen = genesis_block(BitcoinTestnet);
|
||||
assert_eq!(gen.header.version, 1);
|
||||
assert_eq!(gen.header.prev_blockhash.as_slice(), zero_hash().as_slice());
|
||||
assert_eq!(gen.header.merkle_root.le_hex_string(),
|
||||
assert_eq!(gen.header.merkle_root.be_hex_string(),
|
||||
"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
|
||||
assert_eq!(gen.header.time, 1296688602);
|
||||
assert_eq!(gen.header.bits, 0x1d00ffff);
|
||||
assert_eq!(gen.header.nonce, 414098458);
|
||||
assert_eq!(gen.header.bitcoin_hash().le_hex_string(),
|
||||
assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
|
||||
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,13 +111,13 @@ mod tests {
|
|||
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.
|
||||
assert_eq!(realtx.input[0].prev_hash.le_hex_string(),
|
||||
assert_eq!(realtx.input[0].prev_hash.be_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.bitcoin_hash().le_hex_string(),
|
||||
assert_eq!(realtx.bitcoin_hash().be_hex_string(),
|
||||
"a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ impl UtxoSet {
|
|||
// See bitcoind commit `ab91bf39` and BIP30.
|
||||
match self.add_utxos(tx) {
|
||||
Some(mut replace) => {
|
||||
let blockhash = block.header.bitcoin_hash().le_hex_string();
|
||||
let blockhash = block.header.bitcoin_hash().be_hex_string();
|
||||
if blockhash == "00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec".to_string() ||
|
||||
blockhash == "00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721".to_string() {
|
||||
// For these specific blocks, overwrite the old UTXOs.
|
||||
|
|
|
@ -108,13 +108,13 @@ impl Sha256dHash {
|
|||
from_bytes(self.as_slice())
|
||||
}
|
||||
|
||||
/// Converts a hash to a Uint256, interpreting it as a little endian number.
|
||||
/// Converts a hash to a little-endian Uint256
|
||||
pub fn into_uint256(self) -> Uint256 {
|
||||
let Sha256dHash(data) = self;
|
||||
unsafe { Uint256(transmute(data)) }
|
||||
}
|
||||
|
||||
/// Converts a hash to a Uint128, interpreting it as a little endian number.
|
||||
/// Converts a hash to a little-endian Uint128, using only the "low" bytes
|
||||
pub fn into_uint128(self) -> Uint128 {
|
||||
let Sha256dHash(data) = self;
|
||||
// TODO: this function won't work correctly on big-endian machines
|
||||
|
@ -128,7 +128,7 @@ impl Sha256dHash {
|
|||
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() {
|
||||
for i in range(0u, 32) {
|
||||
ret.push_char(from_digit((data[i] / 0x10) as uint, 16).unwrap());
|
||||
ret.push_char(from_digit((data[i] & 0x0f) as uint, 16).unwrap());
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ impl Sha256dHash {
|
|||
pub fn be_hex_string(&self) -> String {
|
||||
let &Sha256dHash(data) = self;
|
||||
let mut ret = String::with_capacity(64);
|
||||
for i in range(0u, 32) {
|
||||
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());
|
||||
}
|
||||
|
@ -293,20 +293,32 @@ mod tests {
|
|||
use collections::bitv::from_bytes;
|
||||
use std::io::MemWriter;
|
||||
use std::str::from_utf8;
|
||||
|
||||
use serialize::Encodable;
|
||||
use serialize::json;
|
||||
|
||||
use network::serialize::{serialize, deserialize};
|
||||
use util::hash::Sha256dHash;
|
||||
use util::misc::hex_bytes;
|
||||
|
||||
#[test]
|
||||
fn test_sha256d() {
|
||||
// nb the 5df6... output is the one you get from sha256sum. this is the
|
||||
// "little-endian" hex string since it matches the in-memory representation
|
||||
// of a Uint256 (which is little-endian) after transmutation
|
||||
assert_eq!(Sha256dHash::from_data(&[]).as_slice(),
|
||||
hex_bytes("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456").unwrap().as_slice());
|
||||
assert_eq!(Sha256dHash::from_data(&[]).le_hex_string(),
|
||||
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456".to_string());
|
||||
assert_eq!(Sha256dHash::from_data(&[]).be_hex_string(),
|
||||
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d".to_string());
|
||||
assert_eq!(Sha256dHash::from_data(b"TEST").as_slice(),
|
||||
hex_bytes("d7bd34bfe44a18d2aa755a344fe3e6b06ed0473772e6dfce16ac71ba0b0a241c").unwrap().as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_consenus_encode_roundtrip() {
|
||||
let hash = Sha256dHash::from_data(&[]);
|
||||
let serial = serialize(&hash).unwrap();
|
||||
let deserial = deserialize(serial).unwrap();
|
||||
assert_eq!(hash, deserial);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -238,17 +238,18 @@ impl<K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>, V> PatriciaTree
|
|||
}
|
||||
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;
|
||||
(Some(box PatriciaTree { data, child_l, child_r, skip_prefix, skip_len }), None) |
|
||||
(None, Some(box PatriciaTree { data, child_l, child_r, skip_prefix, skip_len })) => {
|
||||
tree.data = data;
|
||||
tree.child_l = child_l;
|
||||
tree.child_r = 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;
|
||||
(skip_prefix << (1 + tree.skip_len as uint));
|
||||
tree.skip_len += 1 + skip_len;
|
||||
return (false, ret);
|
||||
}
|
||||
// No children means this node is deletable
|
||||
|
@ -295,17 +296,18 @@ impl<K:BitArray+Eq+Zero+One+BitXor<K,K>+Shl<uint,K>+Shr<uint,K>, V> PatriciaTree
|
|||
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;
|
||||
(bit, Some(box PatriciaTree { data, child_l, child_r, skip_prefix, skip_len }), None) |
|
||||
(bit, None, Some(box PatriciaTree { data, child_l, child_r, skip_prefix, skip_len })) => {
|
||||
tree.data = data;
|
||||
tree.child_l = child_l;
|
||||
tree.child_r = 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;
|
||||
(skip_prefix << (1 + tree.skip_len as uint));
|
||||
tree.skip_len += 1 + skip_len;
|
||||
return (false, ret);
|
||||
}
|
||||
// No children? Delete
|
||||
|
|
Loading…
Reference in New Issue