diff --git a/zebra-chain/src/block/hash.rs b/zebra-chain/src/block/hash.rs index 5b0472bdf..7c4198e80 100644 --- a/zebra-chain/src/block/hash.rs +++ b/zebra-chain/src/block/hash.rs @@ -4,9 +4,8 @@ use std::{fmt, io}; use proptest_derive::Arbitrary; use serde::{Deserialize, Serialize}; -use crate::{ - serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}, - sha256d_writer::Sha256dWriter, +use crate::serialization::{ + sha256d, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize, }; use super::BlockHeader; @@ -37,7 +36,7 @@ impl fmt::Debug for BlockHeaderHash { impl<'a> From<&'a BlockHeader> for BlockHeaderHash { fn from(block_header: &'a BlockHeader) -> Self { - let mut hash_writer = Sha256dWriter::default(); + let mut hash_writer = sha256d::Writer::default(); block_header .zcash_serialize(&mut hash_writer) .expect("Sha256dWriter is infallible"); diff --git a/zebra-chain/src/block/tests.rs b/zebra-chain/src/block/tests.rs index 97aec1f3c..faa67082e 100644 --- a/zebra-chain/src/block/tests.rs +++ b/zebra-chain/src/block/tests.rs @@ -4,11 +4,12 @@ use crate::block::{difficulty::CompactDifficulty, light_client::LightClientRootH use crate::equihash_solution::EquihashSolution; use crate::merkle_tree::MerkleTreeRootHash; use crate::serialization::{ - SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize, + sha256d, SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize, }; use crate::types::LockTime; use crate::Network; -use crate::{sha256d_writer::Sha256dWriter, test::generate}; + +use crate::test::generate; use chrono::{DateTime, Duration, LocalResult, TimeZone, Utc}; use proptest::{ @@ -79,7 +80,7 @@ impl Arbitrary for BlockHeader { #[test] fn blockheaderhash_debug() { let preimage = b"foo bar baz"; - let mut sha_writer = Sha256dWriter::default(); + let mut sha_writer = sha256d::Writer::default(); let _ = sha_writer.write_all(preimage); let hash = BlockHeaderHash(sha_writer.finish()); diff --git a/zebra-chain/src/lib.rs b/zebra-chain/src/lib.rs index 2259f48b2..2fc7fe67c 100644 --- a/zebra-chain/src/lib.rs +++ b/zebra-chain/src/lib.rs @@ -13,7 +13,6 @@ extern crate serde; mod merkle_tree; mod serde_helpers; -mod sha256d_writer; pub mod addresses; pub mod block; diff --git a/zebra-chain/src/merkle_tree.rs b/zebra-chain/src/merkle_tree.rs index 6af26ac08..9e6f7aa25 100644 --- a/zebra-chain/src/merkle_tree.rs +++ b/zebra-chain/src/merkle_tree.rs @@ -7,8 +7,7 @@ use std::{fmt, io}; #[cfg(test)] use proptest_derive::Arbitrary; -use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}; -use crate::sha256d_writer::Sha256dWriter; +use crate::serialization::{sha256d, SerializationError, ZcashDeserialize, ZcashSerialize}; use crate::transaction::Transaction; /// A binary hash tree of SHA256d (two rounds of SHA256) hashes for @@ -38,7 +37,7 @@ pub struct MerkleTreeRootHash(pub [u8; 32]); impl From> for MerkleTreeRootHash { fn from(merkle_tree: MerkleTree) -> Self { - let mut hash_writer = Sha256dWriter::default(); + let mut hash_writer = sha256d::Writer::default(); merkle_tree .zcash_serialize(&mut hash_writer) .expect("Sha256dWriter is infallible"); diff --git a/zebra-chain/src/serialization.rs b/zebra-chain/src/serialization.rs index 493af7cfa..834f2bee3 100644 --- a/zebra-chain/src/serialization.rs +++ b/zebra-chain/src/serialization.rs @@ -12,6 +12,8 @@ mod write_zcash; mod zcash_deserialize; mod zcash_serialize; +pub mod sha256d; + pub use error::SerializationError; pub use read_zcash::ReadZcashExt; pub use write_zcash::WriteZcashExt; diff --git a/zebra-chain/src/serialization/sha256d.rs b/zebra-chain/src/serialization/sha256d.rs new file mode 100644 index 000000000..2c6b511dc --- /dev/null +++ b/zebra-chain/src/serialization/sha256d.rs @@ -0,0 +1,77 @@ +//! SHA256d, a.k.a., double SHA2, a.k.a., 2 SHA 2 Furious + +use std::{fmt, io::prelude::*}; + +use sha2::{Digest, Sha256}; + +/// An `io::Write` instance that produces a SHA256d output. +#[derive(Default)] +pub struct Writer { + hash: Sha256, +} + +impl Writer { + /// Consume the Writer and produce the hash result. + pub fn finish(self) -> [u8; 32] { + let result1 = self.hash.result(); + let result2 = Sha256::digest(&result1); + let mut buffer = [0u8; 32]; + buffer[0..32].copy_from_slice(&result2[0..32]); + buffer + } +} + +impl Write for Writer { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.hash.input(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +/// A 4-byte checksum using truncated double-SHA256 (two rounds of SHA256). +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Checksum(pub [u8; 4]); + +impl<'a> From<&'a [u8]> for Checksum { + fn from(bytes: &'a [u8]) -> Self { + let hash1 = Sha256::digest(bytes); + let hash2 = Sha256::digest(&hash1); + let mut checksum = [0u8; 4]; + checksum[0..4].copy_from_slice(&hash2[0..4]); + Self(checksum) + } +} + +impl fmt::Debug for Checksum { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Sha256dChecksum") + .field(&hex::encode(&self.0)) + .finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sha256d_checksum() { + // https://en.bitcoin.it/wiki/Protocol_documentation#Hashes + let input = b"hello"; + let checksum = Checksum::from(&input[..]); + let expected = Checksum([0x95, 0x95, 0xc9, 0xdf]); + assert_eq!(checksum, expected); + } + + #[test] + fn sha256d_checksum_debug() { + let input = b"hello"; + let checksum = Checksum::from(&input[..]); + + assert_eq!(format!("{:?}", checksum), "Sha256dChecksum(\"9595c9df\")"); + } +} diff --git a/zebra-chain/src/sha256d_writer.rs b/zebra-chain/src/sha256d_writer.rs deleted file mode 100644 index 53c75bf28..000000000 --- a/zebra-chain/src/sha256d_writer.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! A Writer for Sha256d-related (two rounds of SHA256) types. - -use std::io::prelude::*; - -use sha2::{Digest, Sha256}; - -/// A type that lets you write out SHA256d (double-SHA256, as in two rounds). -#[derive(Default)] -pub struct Sha256dWriter { - hash: Sha256, -} - -impl Sha256dWriter { - /// Consume the Writer and produce the hash result. - pub fn finish(self) -> [u8; 32] { - let result1 = self.hash.result(); - let result2 = Sha256::digest(&result1); - let mut buffer = [0u8; 32]; - buffer[0..32].copy_from_slice(&result2[0..32]); - buffer - } -} - -impl Write for Sha256dWriter { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.hash.input(buf); - Ok(buf.len()) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} - -#[cfg(test)] -mod tests { - - use std::io::Write; - - use crate::sha256d_writer::Sha256dWriter; - - #[test] - // Just checking that `flush()` succeeds without error. - fn sha256d_flush() { - let preimage = b"foo bar baz"; - let mut sha_writer = Sha256dWriter::default(); - let _ = sha_writer.write_all(preimage); - - sha_writer.flush().unwrap(); - } -} diff --git a/zebra-chain/src/transaction/hash.rs b/zebra-chain/src/transaction/hash.rs index 6095d2491..94b50df3f 100644 --- a/zebra-chain/src/transaction/hash.rs +++ b/zebra-chain/src/transaction/hash.rs @@ -5,10 +5,7 @@ use std::fmt; use proptest_derive::Arbitrary; use serde::{Deserialize, Serialize}; -use crate::{ - serialization::{SerializationError, ZcashSerialize}, - sha256d_writer::Sha256dWriter, -}; +use crate::serialization::{sha256d, SerializationError, ZcashSerialize}; use super::Transaction; @@ -22,7 +19,7 @@ pub struct TransactionHash(pub [u8; 32]); impl From for TransactionHash { fn from(transaction: Transaction) -> Self { - let mut hash_writer = Sha256dWriter::default(); + let mut hash_writer = sha256d::Writer::default(); transaction .zcash_serialize(&mut hash_writer) .expect("Transactions must serialize into the hash."); @@ -53,26 +50,8 @@ impl std::str::FromStr for TransactionHash { #[cfg(test)] mod tests { - use std::io::Write; - - use crate::sha256d_writer::Sha256dWriter; - use super::*; - #[test] - fn transactionhash_debug() { - let preimage = b"foo bar baz"; - let mut sha_writer = Sha256dWriter::default(); - let _ = sha_writer.write_all(preimage); - - let hash = TransactionHash(sha_writer.finish()); - - assert_eq!( - format!("{:?}", hash), - r#"TransactionHash("bf46b4b5030752fedac6f884976162bbfb29a9398f104a280b3e34d51b416631")"# - ); - } - #[test] fn transactionhash_from_str() { let hash: TransactionHash = diff --git a/zebra-chain/src/types.rs b/zebra-chain/src/types.rs index 9f9375adf..ef37ceaa8 100644 --- a/zebra-chain/src/types.rs +++ b/zebra-chain/src/types.rs @@ -216,57 +216,11 @@ impl ZcashDeserialize for Script { } } -/// A 4-byte checksum using truncated double-SHA256 (two rounds of SHA256). -#[derive(Copy, Clone, Eq, PartialEq)] -pub struct Sha256dChecksum(pub [u8; 4]); - -impl<'a> From<&'a [u8]> for Sha256dChecksum { - fn from(bytes: &'a [u8]) -> Self { - use sha2::{Digest, Sha256}; - let hash1 = Sha256::digest(bytes); - let hash2 = Sha256::digest(&hash1); - let mut checksum = [0u8; 4]; - checksum[0..4].copy_from_slice(&hash2[0..4]); - Self(checksum) - } -} - -impl fmt::Debug for Sha256dChecksum { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Sha256dChecksum") - .field(&hex::encode(&self.0)) - .finish() - } -} - #[cfg(test)] use proptest::prelude::*; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sha256d_checksum() { - // https://en.bitcoin.it/wiki/Protocol_documentation#Hashes - let input = b"hello"; - let checksum = Sha256dChecksum::from(&input[..]); - let expected = Sha256dChecksum([0x95, 0x95, 0xc9, 0xdf]); - assert_eq!(checksum, expected); - } - - #[test] - fn sha256d_checksum_debug() { - let input = b"hello"; - let checksum = Sha256dChecksum::from(&input[..]); - - assert_eq!(format!("{:?}", checksum), "Sha256dChecksum(\"9595c9df\")"); - } -} - #[cfg(test)] mod proptests { - use std::io::Cursor; use proptest::prelude::*; @@ -275,7 +229,6 @@ mod proptests { use crate::serialization::{ZcashDeserialize, ZcashSerialize}; proptest! { - #[test] fn locktime_roundtrip(locktime in any::()) { let mut bytes = Cursor::new(Vec::new()); @@ -297,6 +250,5 @@ mod proptests { prop_assert_eq![script, other_script]; } - } } diff --git a/zebra-network/src/protocol/external/codec.rs b/zebra-network/src/protocol/external/codec.rs index 7cee5c19f..d81a0bf29 100644 --- a/zebra-network/src/protocol/external/codec.rs +++ b/zebra-network/src/protocol/external/codec.rs @@ -11,10 +11,11 @@ use tokio_util::codec::{Decoder, Encoder}; use zebra_chain::{ block::{Block, BlockHeaderHash}, serialization::{ - ReadZcashExt, SerializationError as Error, WriteZcashExt, ZcashDeserialize, ZcashSerialize, + sha256d, ReadZcashExt, SerializationError as Error, WriteZcashExt, ZcashDeserialize, + ZcashSerialize, }, transaction::Transaction, - types::{BlockHeight, Sha256dChecksum}, + types::BlockHeight, Network, }; @@ -162,7 +163,7 @@ impl Encoder for Codec { header_writer.write_all(&Magic::from(self.builder.network).0[..])?; header_writer.write_all(command)?; header_writer.write_u32::(body.len() as u32)?; - header_writer.write_all(&Sha256dChecksum::from(&body[..]).0)?; + header_writer.write_all(&sha256d::Checksum::from(&body[..]).0)?; dst.reserve(HEADER_LEN + body.len()); dst.extend_from_slice(&header); @@ -276,7 +277,7 @@ enum DecodeState { Body { body_len: usize, command: [u8; 12], - checksum: Sha256dChecksum, + checksum: sha256d::Checksum, }, } @@ -321,7 +322,7 @@ impl Decoder for Codec { let magic = Magic(header_reader.read_4_bytes()?); let command = header_reader.read_12_bytes()?; let body_len = header_reader.read_u32::()? as usize; - let checksum = Sha256dChecksum(header_reader.read_4_bytes()?); + let checksum = sha256d::Checksum(header_reader.read_4_bytes()?); trace!( ?self.state, ?magic, @@ -371,7 +372,7 @@ impl Decoder for Codec { let body = src.split_to(body_len); self.state = DecodeState::Head; - if checksum != Sha256dChecksum::from(&body[..]) { + if checksum != sha256d::Checksum::from(&body[..]) { return Err(Parse( "supplied message checksum does not match computed checksum", )); @@ -691,29 +692,6 @@ mod tests { }); } - #[test] - fn decode_state_debug() { - assert_eq!(format!("{:?}", DecodeState::Head), "DecodeState::Head"); - - let decode_state = DecodeState::Body { - body_len: 43, - command: [118, 101, 114, 115, 105, 111, 110, 0, 0, 0, 0, 0], - checksum: Sha256dChecksum([186, 250, 162, 227]), - }; - - assert_eq!(format!("{:?}", decode_state), - "DecodeState::Body { body_len: 43, command: \"version\\u{0}\\u{0}\\u{0}\\u{0}\\u{0}\", checksum: Sha256dChecksum(\"bafaa2e3\") }"); - - let decode_state = DecodeState::Body { - body_len: 43, - command: [118, 240, 144, 128, 105, 111, 110, 0, 0, 0, 0, 0], - checksum: Sha256dChecksum([186, 250, 162, 227]), - }; - - assert_eq!(format!("{:?}", decode_state), - "DecodeState::Body { body_len: 43, command: \"v�ion\\u{0}\\u{0}\\u{0}\\u{0}\\u{0}\", checksum: Sha256dChecksum(\"bafaa2e3\") }"); - } - #[test] fn max_msg_size_round_trip() { use std::sync::Arc;