diff --git a/chain/src/txhashset/txhashset.rs b/chain/src/txhashset/txhashset.rs index 1f45e680f..b711caf1c 100644 --- a/chain/src/txhashset/txhashset.rs +++ b/chain/src/txhashset/txhashset.rs @@ -204,8 +204,8 @@ impl TxHashSet { let pos = pmmr::insertion_to_pmmr_index(height + 1); let header_pmmr = ReadonlyPMMR::at(&self.header_pmmr_h.backend, self.header_pmmr_h.last_pos); - if let Some(hash) = header_pmmr.get_data(pos) { - let header = self.commit_index.get_block_header(&hash)?; + if let Some(entry) = header_pmmr.get_data(pos) { + let header = self.commit_index.get_block_header(&entry.hash())?; Ok(header) } else { Err(ErrorKind::Other(format!("get header by height")).into()) @@ -613,7 +613,7 @@ impl<'a> HeaderExtension<'a> { /// Get the header hash for the specified pos from the underlying MMR backend. fn get_header_hash(&self, pos: u64) -> Option { - self.pmmr.get_data(pos) + self.pmmr.get_data(pos).map(|x| x.hash()) } /// Get the header at the specified height based on the current state of the header extension. @@ -989,7 +989,7 @@ impl<'a> Extension<'a> { /// Get the header hash for the specified pos from the underlying MMR backend. fn get_header_hash(&self, pos: u64) -> Option { - self.header_pmmr.get_data(pos) + self.header_pmmr.get_data(pos).map(|x| x.hash()) } /// Get the header at the specified height based on the current state of the extension. diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 220d88bc2..abe6c1d7d 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -34,7 +34,7 @@ use crate::core::{ use crate::global; use crate::keychain::{self, BlindingFactor}; use crate::pow::{Difficulty, Proof, ProofOfWork}; -use crate::ser::{self, PMMRable, Readable, Reader, Writeable, Writer}; +use crate::ser::{self, FixedLength, PMMRable, Readable, Reader, Writeable, Writer}; use crate::util::{secp, static_secp_instance}; /// Errors thrown by Block validation @@ -109,6 +109,65 @@ impl fmt::Display for Error { } } +/// Header entry for storing in the header MMR. +/// Note: we hash the block header itself and maintain the hash in the entry. +/// This allows us to lookup the original header from the db as necessary. +pub struct HeaderEntry { + hash: Hash, + timestamp: u64, + total_difficulty: Difficulty, + secondary_scaling: u32, + is_secondary: bool, +} + +impl Readable for HeaderEntry { + fn read(reader: &mut Reader) -> Result { + let hash = Hash::read(reader)?; + let timestamp = reader.read_u64()?; + let total_difficulty = Difficulty::read(reader)?; + let secondary_scaling = reader.read_u32()?; + + // Using a full byte to represent the bool for now. + let is_secondary = reader.read_u8()? != 0; + + Ok(HeaderEntry { + hash, + timestamp, + total_difficulty, + secondary_scaling, + is_secondary, + }) + } +} + +impl Writeable for HeaderEntry { + fn write(&self, writer: &mut W) -> Result<(), ser::Error> { + self.hash.write(writer)?; + writer.write_u64(self.timestamp)?; + self.total_difficulty.write(writer)?; + writer.write_u32(self.secondary_scaling)?; + + // Using a full byte to represent the bool for now. + if self.is_secondary { + writer.write_u8(1)?; + } else { + writer.write_u8(0)?; + } + Ok(()) + } +} + +impl FixedLength for HeaderEntry { + const LEN: usize = Hash::LEN + 8 + Difficulty::LEN + 4 + 1; +} + +impl HeaderEntry { + /// The hash of the underlying block. + pub fn hash(&self) -> Hash { + self.hash + } +} + /// Block header, fairly standard compared to other blockchains. #[derive(Clone, Debug, PartialEq)] pub struct BlockHeader { @@ -160,10 +219,16 @@ impl Default for BlockHeader { } impl PMMRable for BlockHeader { - type E = Hash; + type E = HeaderEntry; fn as_elmt(&self) -> Self::E { - self.hash() + HeaderEntry { + hash: self.hash(), + timestamp: self.timestamp.timestamp() as u64, + total_difficulty: self.total_difficulty(), + secondary_scaling: self.pow.secondary_scaling, + is_secondary: self.pow.is_secondary(), + } } }