From 7afd76f5fba4c95f948cb8180659a4896d470cbd Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 10 Aug 2020 10:26:49 +1000 Subject: [PATCH] feature: Add light_client_root_hash(network) to Block * use the right variant in LightClientRootHash::from_bytes() * make block.header.light_client_root_hash pub(super) * add tests for LightClientRootHash and block.light_client_root_hash --- zebra-chain/src/block.rs | 19 +++++++++++++++++++ zebra-chain/src/block/header.rs | 5 +++-- zebra-chain/src/block/light_client.rs | 16 ++++++++++++---- zebra-chain/src/block/tests.rs | 20 +++++++++++++++++++- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/zebra-chain/src/block.rs b/zebra-chain/src/block.rs index da3d1ccd..a21119d9 100644 --- a/zebra-chain/src/block.rs +++ b/zebra-chain/src/block.rs @@ -18,9 +18,11 @@ use proptest_derive::Arbitrary; use crate::transaction::Transaction; use crate::types::BlockHeight; +use crate::Network; pub use hash::BlockHeaderHash; pub use header::BlockHeader; +pub use light_client::LightClientRootHash; /// A block in your blockchain. /// @@ -91,6 +93,23 @@ impl Block { pub fn hash(&self) -> BlockHeaderHash { BlockHeaderHash::from(self) } + + /// Get the parsed light client root hash for this block. + /// + /// The interpretation of the light client root hash depends on the + /// configured `network`, and this block's height. + /// + /// Returns None if this block does not have a block height. + pub fn light_client_root_hash(&self, network: Network) -> Option { + match self.coinbase_height() { + Some(height) => Some(LightClientRootHash::from_bytes( + self.header.light_client_root_hash, + network, + height, + )), + None => None, + } + } } impl<'a> From<&'a Block> for BlockHeaderHash { diff --git a/zebra-chain/src/block/header.rs b/zebra-chain/src/block/header.rs index 80c2ac96..532714a0 100644 --- a/zebra-chain/src/block/header.rs +++ b/zebra-chain/src/block/header.rs @@ -37,8 +37,9 @@ pub struct BlockHeader { /// The light client root hash. /// /// This field is interpreted differently, based on the current - /// block height. See LightClientRootHash for details. - pub light_client_root_hash: [u8; 32], + /// block height. Use `block.light_client_root_hash(network)` to get the + /// parsed `LightClientRootHash` for this block. + pub(super) light_client_root_hash: [u8; 32], /// The block timestamp is a Unix epoch time (UTC) when the miner /// started hashing the header (according to the miner). diff --git a/zebra-chain/src/block/light_client.rs b/zebra-chain/src/block/light_client.rs index 862963af..41b1f99b 100644 --- a/zebra-chain/src/block/light_client.rs +++ b/zebra-chain/src/block/light_client.rs @@ -2,7 +2,7 @@ use crate::note_commitment_tree::SaplingNoteTreeRootHash; use crate::types::BlockHeight; -use crate::Network; +use crate::{Network, NetworkUpgrade, NetworkUpgrade::*}; /// Light client root hashes. /// @@ -50,12 +50,20 @@ impl LightClientRootHash { network: Network, height: BlockHeight, ) -> LightClientRootHash { - // TODO(teor): use the correct network upgrade here, after moving the - // network upgrades from zebra-consensus to zebra-chain. - LightClientRootHash::PreSaplingReserved(bytes) + use LightClientRootHash::*; + + match NetworkUpgrade::current(network, height) { + Genesis | BeforeOverwinter | Overwinter => PreSaplingReserved(bytes), + Sapling | Blossom => FinalSaplingRoot(SaplingNoteTreeRootHash(bytes)), + Heartwood if Some(height) == Heartwood.activation_height(network) => { + ChainHistoryActivationReserved(bytes) + } + Heartwood | Canopy => ChainHistoryRoot(ChainHistoryMmrRootHash(bytes)), + } } /// Returns the serialized bytes for this LightClientRootHash. + #[allow(dead_code)] pub fn to_bytes(self) -> [u8; 32] { use LightClientRootHash::*; diff --git a/zebra-chain/src/block/tests.rs b/zebra-chain/src/block/tests.rs index 98eb3308..333e55df 100644 --- a/zebra-chain/src/block/tests.rs +++ b/zebra-chain/src/block/tests.rs @@ -228,6 +228,14 @@ proptest! { prop_assert_eq![header, other_header]; } + + #[test] + fn light_client_roundtrip(bytes in any::<[u8; 32]>(), network in any::(), block_height in any::()) { + let light_hash = LightClientRootHash::from_bytes(bytes, network, block_height); + let other_bytes = light_hash.to_bytes(); + + prop_assert_eq![bytes, other_bytes]; + } } proptest! { @@ -239,12 +247,22 @@ proptest! { .unwrap_or(16)))] #[test] - fn block_roundtrip(block in any::()) { + fn block_roundtrip(block in any::(), network in any::()) { let bytes = block.zcash_serialize_to_vec()?; let bytes = &mut bytes.as_slice(); + // Check the light client root hash + let light_hash = block.light_client_root_hash(network); + if let Some(light_hash) = light_hash { + let light_hash_bytes = light_hash.to_bytes(); + prop_assert_eq![block.header.light_client_root_hash, light_hash_bytes]; + } else { + prop_assert_eq![block.coinbase_height(), None]; + } + // Check the block size limit if bytes.len() <= MAX_BLOCK_BYTES as _ { + // Check deserialization let other_block = bytes.zcash_deserialize_into()?; prop_assert_eq![block, other_block];