chain: move sha256d to serialization module.

This extracts the SHA256d code from being split across two modules and puts it
in one module, under serialization.

The code is unchanged except for three deleted tests:

* `sha256d_flush` in `sha256d_writer` (not a meaningful test);
* `transactionhash_debug` (constructs an invalid transaction hash, and the
  behavior is tested in the next test);
* `decode_state_debug` (we do not need to test the Debug output of
  DecodeState);
This commit is contained in:
Henry de Valence 2020-08-14 22:45:11 -07:00
parent 9f31e551c9
commit b36fe8f937
10 changed files with 97 additions and 162 deletions

View File

@ -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");

View File

@ -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());

View File

@ -13,7 +13,6 @@ extern crate serde;
mod merkle_tree;
mod serde_helpers;
mod sha256d_writer;
pub mod addresses;
pub mod block;

View File

@ -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<MerkleTree<Transaction>> for MerkleTreeRootHash {
fn from(merkle_tree: MerkleTree<Transaction>) -> 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");

View File

@ -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;

View File

@ -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<usize> {
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\")");
}
}

View File

@ -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<usize> {
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();
}
}

View File

@ -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<Transaction> 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 =

View File

@ -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::<LockTime>()) {
let mut bytes = Cursor::new(Vec::new());
@ -297,6 +250,5 @@ mod proptests {
prop_assert_eq![script, other_script];
}
}
}

View File

@ -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::<LittleEndian>(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::<LittleEndian>()? 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<EFBFBD>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;