/// Trait that converts [`CheckPoint`] `data` to [`BlockHash`].
///
-/// Implementations of [`ToBlockHash`] must always return the block's consensus-defined hash. If
-/// your type contains extra fields (timestamps, metadata, etc.), these must be ignored. For
-/// example, [`BlockHash`] trivially returns itself, [`Header`] calls its `block_hash()`, and a
-/// wrapper type around a [`Header`] should delegate to the header's hash rather than derive one
-/// from other fields.
+/// Implementations of [`ToBlockHash`] must always return the block's consensus-defined hash. For
+/// example, [`BlockHash`] trivially returns itself and [`Header`] calls its `block_hash()`.
+///
+/// The data must be fully determined by its block hash: it may only contain a subset of what the
+/// block hash commits to, so two values with the same [`BlockHash`] are always identical.
+/// [`CheckPoint`] treats such values as interchangeable and may keep either one. Data that the
+/// block hash does not commit to (local metadata, the time a block was seen, etc.) must be stored
+/// outside of the checkpoint.
pub trait ToBlockHash {
/// Returns the [`BlockHash`] for the associated [`CheckPoint`] `data` type.
fn to_blockhash(&self) -> BlockHash;
impl ToBlockHash for BlockWithTime {
fn to_blockhash(&self) -> BlockHash {
- // Generate a deterministic hash from the height
- let hash_bytes = bitcoin::hashes::sha256d::Hash::hash(&self.0.to_le_bytes());
+ // Commit to every field so the data is fully determined by its hash.
+ let mut bytes = self.0.to_le_bytes().to_vec();
+ bytes.extend(self.1.to_le_bytes());
+ let hash_bytes = bitcoin::hashes::sha256d::Hash::hash(&bytes);
BlockHash::from_raw_hash(hash_bytes)
}
}