2020-08-15 16:12:26 -07:00
|
|
|
//! Blocks and block-related structures (heights, headers, etc.)
|
2020-08-16 18:31:28 -07:00
|
|
|
#![allow(clippy::unit_arg)]
|
2019-09-10 13:27:10 -07:00
|
|
|
|
2021-03-30 16:51:42 -07:00
|
|
|
mod commitment;
|
2020-06-25 09:18:05 -07:00
|
|
|
mod hash;
|
|
|
|
mod header;
|
2020-08-14 23:51:41 -07:00
|
|
|
mod height;
|
2020-06-25 09:18:05 -07:00
|
|
|
mod serialize;
|
|
|
|
|
2020-08-15 20:57:59 -07:00
|
|
|
pub mod merkle;
|
|
|
|
|
2020-09-23 18:52:52 -07:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
mod arbitrary;
|
2020-01-31 21:27:51 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
2020-01-31 18:02:02 -08:00
|
|
|
|
2020-10-07 20:07:32 -07:00
|
|
|
use std::fmt;
|
|
|
|
|
2021-03-30 16:51:42 -07:00
|
|
|
pub use commitment::Commitment;
|
2020-08-15 23:20:01 -07:00
|
|
|
pub use hash::Hash;
|
2020-10-12 14:33:32 -07:00
|
|
|
pub use header::BlockTimeError;
|
2020-12-01 15:52:09 -08:00
|
|
|
pub use header::{CountedHeader, Header};
|
2020-08-16 11:42:02 -07:00
|
|
|
pub use height::Height;
|
2021-04-05 16:49:42 -07:00
|
|
|
pub use serialize::MAX_BLOCK_BYTES;
|
2019-09-24 22:14:48 -07:00
|
|
|
|
2020-08-15 22:25:30 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2021-04-05 16:49:42 -07:00
|
|
|
use crate::{
|
|
|
|
fmt::DisplayToDebug,
|
|
|
|
parameters::Network,
|
|
|
|
serialization::{TrustedPreallocate, MAX_PROTOCOL_MESSAGE_LEN},
|
|
|
|
transaction::Transaction,
|
|
|
|
transparent,
|
|
|
|
};
|
2020-08-15 22:25:30 -07:00
|
|
|
|
2020-08-16 12:08:24 -07:00
|
|
|
/// A Zcash block, containing a header and a list of transactions.
|
2020-08-15 22:25:30 -07:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub struct Block {
|
|
|
|
/// The block header, containing block metadata.
|
2020-08-16 11:48:00 -07:00
|
|
|
pub header: Header,
|
2020-08-15 22:25:30 -07:00
|
|
|
/// The block transactions.
|
|
|
|
pub transactions: Vec<std::sync::Arc<Transaction>>,
|
|
|
|
}
|
|
|
|
|
2020-10-07 20:07:32 -07:00
|
|
|
impl fmt::Display for Block {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
let mut fmter = f.debug_struct("Block");
|
|
|
|
if let Some(height) = self.coinbase_height() {
|
|
|
|
fmter.field("height", &height);
|
|
|
|
}
|
|
|
|
|
|
|
|
fmter.field("hash", &DisplayToDebug(self.hash())).finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-15 22:25:30 -07:00
|
|
|
impl Block {
|
|
|
|
/// Return the block height reported in the coinbase transaction, if any.
|
2020-08-16 11:42:02 -07:00
|
|
|
pub fn coinbase_height(&self) -> Option<Height> {
|
2020-08-15 22:25:30 -07:00
|
|
|
self.transactions
|
|
|
|
.get(0)
|
|
|
|
.and_then(|tx| tx.inputs().get(0))
|
|
|
|
.and_then(|input| match input {
|
2020-08-17 01:24:33 -07:00
|
|
|
transparent::Input::Coinbase { ref height, .. } => Some(*height),
|
2020-08-15 22:25:30 -07:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-02 14:59:16 -07:00
|
|
|
/// Compute the hash of this block.
|
2020-08-15 23:20:01 -07:00
|
|
|
pub fn hash(&self) -> Hash {
|
|
|
|
Hash::from(self)
|
2020-08-15 22:25:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the parsed root hash for this block.
|
|
|
|
///
|
|
|
|
/// The interpretation of the root hash depends on the
|
|
|
|
/// configured `network`, and this block's height.
|
|
|
|
///
|
|
|
|
/// Returns None if this block does not have a block height.
|
2021-03-30 16:51:42 -07:00
|
|
|
pub fn commitment(&self, network: Network) -> Option<Commitment> {
|
2021-03-01 18:50:22 -08:00
|
|
|
self.coinbase_height()
|
2021-03-30 16:51:42 -07:00
|
|
|
.map(|height| Commitment::from_bytes(self.header.commitment_bytes, network, height))
|
2020-08-15 22:25:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-15 23:20:01 -07:00
|
|
|
impl<'a> From<&'a Block> for Hash {
|
|
|
|
fn from(block: &'a Block) -> Hash {
|
2020-08-15 22:25:30 -07:00
|
|
|
(&block.header).into()
|
|
|
|
}
|
|
|
|
}
|
2021-04-05 16:49:42 -07:00
|
|
|
/// A serialized Block hash takes 32 bytes
|
|
|
|
const BLOCK_HASH_SIZE: u64 = 32;
|
|
|
|
/// The maximum number of hashes in a valid Zcash protocol message.
|
|
|
|
impl TrustedPreallocate for Hash {
|
|
|
|
fn max_allocation() -> u64 {
|
|
|
|
// Every vector type requires a length field of at least one byte for de/serialization.
|
|
|
|
// Since a block::Hash takes 32 bytes, we can never receive more than (MAX_PROTOCOL_MESSAGE_LEN - 1) / 32 hashes in a single message
|
|
|
|
((MAX_PROTOCOL_MESSAGE_LEN - 1) as u64) / BLOCK_HASH_SIZE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test_trusted_preallocate {
|
|
|
|
use super::{Hash, BLOCK_HASH_SIZE, MAX_PROTOCOL_MESSAGE_LEN};
|
|
|
|
use crate::serialization::{TrustedPreallocate, ZcashSerialize};
|
|
|
|
use proptest::prelude::*;
|
|
|
|
use std::convert::TryInto;
|
|
|
|
proptest! {
|
|
|
|
#![proptest_config(ProptestConfig::with_cases(10_000))]
|
|
|
|
/// Verify that the serialized size of a block hash used to calculate the allocation limit is correct
|
|
|
|
#[test]
|
|
|
|
fn block_hash_size_is_correct(hash in Hash::arbitrary()) {
|
|
|
|
let serialized = hash.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
|
|
prop_assert!(serialized.len() as u64 == BLOCK_HASH_SIZE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
proptest! {
|
|
|
|
|
|
|
|
#![proptest_config(ProptestConfig::with_cases(200))]
|
|
|
|
|
|
|
|
/// Verify that...
|
|
|
|
/// 1. The smallest disallowed vector of `Hash`s is too large to send via the Zcash Wire Protocol
|
|
|
|
/// 2. The largest allowed vector is small enough to fit in a legal Zcash Wire Protocol message
|
|
|
|
#[test]
|
|
|
|
fn block_hash_max_allocation(hash in Hash::arbitrary_with(())) {
|
|
|
|
let max_allocation: usize = Hash::max_allocation().try_into().unwrap();
|
|
|
|
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
|
|
|
|
for _ in 0..(Hash::max_allocation()+1) {
|
|
|
|
smallest_disallowed_vec.push(hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
let smallest_disallowed_serialized = smallest_disallowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
|
|
// Check that our smallest_disallowed_vec is only one item larger than the limit
|
|
|
|
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == Hash::max_allocation());
|
|
|
|
// Check that our smallest_disallowed_vec is too big to send as a protocol message
|
|
|
|
prop_assert!(smallest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
|
|
|
|
|
|
|
|
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
|
|
|
|
smallest_disallowed_vec.pop();
|
|
|
|
let largest_allowed_vec = smallest_disallowed_vec;
|
|
|
|
let largest_allowed_serialized = largest_allowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
|
|
|
|
|
|
// Check that our largest_allowed_vec contains the maximum number of hashes
|
|
|
|
prop_assert!((largest_allowed_vec.len() as u64) == Hash::max_allocation());
|
|
|
|
// Check that our largest_allowed_vec is small enough to send as a protocol message
|
|
|
|
prop_assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|