From 2bbd25b36b3467089af7b81b072993245e42c9a8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 1 May 2019 13:21:48 +0100 Subject: [PATCH] Add prevHash field to CompactBlock This enables basic verification of chain validity when CompactBlocks are received without the full header. --- .../proto/compact_formats.proto | 7 +-- zcash_client_backend/src/proto/mod.rs | 51 +++++++++++++++++++ zcash_primitives/src/block.rs | 14 +++++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/zcash_client_backend/proto/compact_formats.proto b/zcash_client_backend/proto/compact_formats.proto index d65b658..7e1bc54 100644 --- a/zcash_client_backend/proto/compact_formats.proto +++ b/zcash_client_backend/proto/compact_formats.proto @@ -13,9 +13,10 @@ message CompactBlock { uint32 protoVersion = 1; // the version of this wire format, for storage uint64 height = 2; // the height of this block bytes hash = 3; - uint32 time = 4; - bytes header = 5; // (hash and time) OR (full header) - repeated CompactTx vtx = 6; // compact transactions from this block + bytes prevHash = 4; + uint32 time = 5; + bytes header = 6; // (hash, prevHash, and time) OR (full header) + repeated CompactTx vtx = 7; // compact transactions from this block } message CompactTx { diff --git a/zcash_client_backend/src/proto/mod.rs b/zcash_client_backend/src/proto/mod.rs index e24ef5c..50d562b 100644 --- a/zcash_client_backend/src/proto/mod.rs +++ b/zcash_client_backend/src/proto/mod.rs @@ -1,3 +1,54 @@ //! Generated code for handling light client protobuf structs. +use zcash_primitives::block::{BlockHash, BlockHeader}; + pub mod compact_formats; + +impl compact_formats::CompactBlock { + /// Returns the [`BlockHash`] for this block. + /// + /// # Panics + /// + /// This function will panic if [`CompactBlock.header`] is not set and + /// [`CompactBlock.hash`] is not exactly 32 bytes. + /// + /// [`CompactBlock.header`]: #structfield.header + /// [`CompactBlock.hash`]: #structfield.hash + pub fn hash(&self) -> BlockHash { + if let Some(header) = self.header() { + header.hash() + } else { + BlockHash::from_slice(&self.hash) + } + } + + /// Returns the [`BlockHash`] for this block's parent. + /// + /// # Panics + /// + /// This function will panic if [`CompactBlock.header`] is not set and + /// [`CompactBlock.prevHash`] is not exactly 32 bytes. + /// + /// [`CompactBlock.header`]: #structfield.header + /// [`CompactBlock.prevHash`]: #structfield.prevHash + pub fn prev_hash(&self) -> BlockHash { + if let Some(header) = self.header() { + header.prev_block + } else { + BlockHash::from_slice(&self.prevHash) + } + } + + /// Returns the [`BlockHeader`] for this block if present. + /// + /// A convenience method that parses [`CompactBlock.header`] if present. + /// + /// [`CompactBlock.header`]: #structfield.header + pub fn header(&self) -> Option { + if self.header.is_empty() { + None + } else { + BlockHeader::read(&self.header[..]).ok() + } + } +} diff --git a/zcash_primitives/src/block.rs b/zcash_primitives/src/block.rs index 3140f9b..8432cd4 100644 --- a/zcash_primitives/src/block.rs +++ b/zcash_primitives/src/block.rs @@ -22,6 +22,20 @@ impl fmt::Display for BlockHash { } } +impl BlockHash { + /// Constructs a [`BlockHash`] from the given slice. + /// + /// # Panics + /// + /// This function will panic if the slice is not exactly 32 bytes. + pub fn from_slice(bytes: &[u8]) -> Self { + assert_eq!(bytes.len(), 32); + let mut hash = [0; 32]; + hash.copy_from_slice(&bytes); + BlockHash(hash) + } +} + /// A Zcash block header. pub struct BlockHeader { hash: BlockHash,