block_header.rs 5.15 KiB
Newer Older
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
use std::io;
use std::fmt;
use hex::{ToHex, FromHex};
use ser::{deserialize, serialize};
use crypto::dhash256;
Marek Kotewicz's avatar
Marek Kotewicz committed
use hash::H256;
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
use primitives::bytes::Bytes;
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
use ser::{Error, Serializable, Deserializable, Stream, Reader};
Marek Kotewicz's avatar
Marek Kotewicz committed

#[derive(Debug, PartialEq, Default, Clone)]
pub struct EquihashSolution(pub Vec<u8>); // TODO: len = 1344

Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
#[derive(PartialEq, Clone)]
Marek Kotewicz's avatar
Marek Kotewicz committed
pub struct BlockHeader {
	pub version: u32,
	pub previous_header_hash: H256,
	pub merkle_root_hash: H256,
	pub time: u32,
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
	pub nonce: BlockHeaderNonce,
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
	pub hash_final_sapling_root: Option<H256>,
	pub equihash_solution: Option<EquihashSolution>,
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
#[derive(Debug, PartialEq, Clone)]
pub enum BlockHeaderNonce {
	U32(u32),
	H256(H256),
}

impl From<u32> for BlockHeaderNonce {
	fn from(nonce: u32) -> Self {
		BlockHeaderNonce::U32(nonce)
	}
}

impl BlockHeader {
	pub fn hash(&self) -> H256 {
		dhash256(&serialize(self))
	}
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed

	pub fn equihash_input(&self) -> Bytes {
		let mut stream = Stream::new();
		stream
			.append(&self.version)
			.append(&self.previous_header_hash)
			.append(&self.merkle_root_hash);

		if let Some(hash_final_sapling_root) = self.hash_final_sapling_root.as_ref() {
			stream.append(hash_final_sapling_root);
		}

		stream
			.append(&self.time)
			.append(&self.bits);
		
		match self.nonce {
			BlockHeaderNonce::U32(ref v) => stream.append(v),
			BlockHeaderNonce::H256(ref v) => stream.append(v),
		};

		stream.out()
	}
Marek Kotewicz's avatar
Marek Kotewicz committed
impl fmt::Debug for BlockHeader {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Marek Kotewicz's avatar
Marek Kotewicz committed
		f.debug_struct("BlockHeader")
			.field("version", &self.version)
			.field("previous_header_hash", &self.previous_header_hash.reversed())
			.field("merkle_root_hash", &self.merkle_root_hash.reversed())
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
			.field("hash_final_sapling_root", &self.hash_final_sapling_root)
Marek Kotewicz's avatar
Marek Kotewicz committed
			.field("time", &self.time)
			.field("bits", &self.bits)
Marek Kotewicz's avatar
Marek Kotewicz committed
			.field("nonce", &self.nonce)
			.field("equihash_solution", &self.equihash_solution.as_ref().map(|s| s.0.to_hex::<String>()))
Marek Kotewicz's avatar
Marek Kotewicz committed
			.finish()
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
impl From<&'static str> for BlockHeader {
	fn from(s: &'static str) -> Self {
		deserialize(&s.from_hex::<Vec<u8>>().unwrap() as &[u8]).unwrap()
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
	}
}

Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
impl Serializable for BlockHeader {
	fn serialize(&self, stream: &mut Stream) {
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		stream
			.append(&self.version)
			.append(&self.previous_header_hash)
			.append(&self.merkle_root_hash);
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		if let Some(hash_final_sapling_root) = self.hash_final_sapling_root.as_ref() {
			stream.append(hash_final_sapling_root);
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		}
		stream
			.append(&self.time)
			.append(&self.bits);
		
		match self.nonce {
			BlockHeaderNonce::U32(ref v) => stream.append(v),
			BlockHeaderNonce::H256(ref v) => stream.append(v),
		};

		if let Some(ref equihash_solution) = self.equihash_solution {
			stream.append_list(&equihash_solution.0);
		}
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
	}
}

impl Deserializable for BlockHeader {
	fn deserialize<T>(reader: &mut Reader<T>) -> Result<Self, Error> where Self: Sized, T: io::Read {
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		let is_zcash_format = reader.is_zcash_reader();

Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		let version = reader.read()?;
		let previous_header_hash = reader.read()?;
		let merkle_root_hash = reader.read()?;

		// TODO: rename to transaction format - original, witness, zcash, must be enum, not flags
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		let hash_final_sapling_root = if is_zcash_format {
			Some(reader.read()?)
		} else {
			None
		};
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed

		let time = reader.read()?;
		let bits = reader.read()?;
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		let nonce = match is_zcash_format {
			true => BlockHeaderNonce::H256(reader.read()?),
			false => BlockHeaderNonce::U32(reader.read()?),
		};
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed

Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		let equihash_solution = if is_zcash_format {
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
			Some(EquihashSolution(reader.read_list()?))
		} else {
			None
		};

		Ok(BlockHeader {
			version,
			previous_header_hash,
			merkle_root_hash,
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
			hash_final_sapling_root,
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
			time,
			bits,
			nonce,
			equihash_solution,
		})
	}
}


Marek Kotewicz's avatar
Marek Kotewicz committed
#[cfg(test)]
mod tests {
Marek Kotewicz's avatar
Marek Kotewicz committed
	use ser::{Reader, Error as ReaderError, Stream};
Marek Kotewicz's avatar
Marek Kotewicz committed
	use super::BlockHeader;

	#[test]
	fn test_block_header_stream() {
		let block_header = BlockHeader {
			version: 1,
Marek Kotewicz's avatar
Marek Kotewicz committed
			previous_header_hash: [2; 32].into(),
			merkle_root_hash: [3; 32].into(),
Marek Kotewicz's avatar
Marek Kotewicz committed
			time: 4,
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
			nonce: 6.into(),
			equihash_solution: None,
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		let mut stream = Stream::new();
Marek Kotewicz's avatar
Marek Kotewicz committed
		stream.append(&block_header);

		let expected = vec![
			1, 0, 0, 0,
			2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
			3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
			4, 0, 0, 0,
			5, 0, 0, 0,
			6, 0, 0, 0,
Marek Kotewicz's avatar
Marek Kotewicz committed
		].into();
Marek Kotewicz's avatar
Marek Kotewicz committed

		assert_eq!(stream.out(), expected);
	}
Marek Kotewicz's avatar
Marek Kotewicz committed

	#[test]
	fn test_block_header_reader() {
		let buffer = vec![
			1, 0, 0, 0,
			2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
			3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
			4, 0, 0, 0,
			5, 0, 0, 0,
			6, 0, 0, 0,
		];

Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		let mut reader = Reader::new(&buffer, 0);
Marek Kotewicz's avatar
Marek Kotewicz committed

		let expected = BlockHeader {
			version: 1,
Marek Kotewicz's avatar
Marek Kotewicz committed
			previous_header_hash: [2; 32].into(),
			merkle_root_hash: [3; 32].into(),
Marek Kotewicz's avatar
Marek Kotewicz committed
			time: 4,
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
			nonce: 6.into(),
			equihash_solution: None,
Marek Kotewicz's avatar
Marek Kotewicz committed
		};

		assert_eq!(expected, reader.read().unwrap());
		assert_eq!(ReaderError::UnexpectedEnd, reader.read::<BlockHeader>().unwrap_err());
	}
Marek Kotewicz's avatar
Marek Kotewicz committed
}