2019-09-10 13:27:10 -07:00
|
|
|
//! Definitions of block datastructures.
|
2020-05-26 18:00:58 -07:00
|
|
|
#![allow(clippy::unit_arg)]
|
2019-09-10 13:27:10 -07:00
|
|
|
|
2020-07-29 23:47:31 -07:00
|
|
|
mod difficulty;
|
2020-06-25 09:18:05 -07:00
|
|
|
mod hash;
|
|
|
|
mod header;
|
2020-08-07 02:24:00 -07:00
|
|
|
mod light_client;
|
2020-06-25 09:18:05 -07:00
|
|
|
mod serialize;
|
|
|
|
|
2020-01-31 21:27:51 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
2020-01-31 18:02:02 -08:00
|
|
|
|
2020-06-15 15:08:14 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
2020-07-14 20:21:06 -07:00
|
|
|
use std::{error, sync::Arc};
|
2019-09-24 22:14:48 -07:00
|
|
|
|
2020-01-31 18:09:46 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
use proptest_derive::Arbitrary;
|
|
|
|
|
2019-09-24 22:14:48 -07:00
|
|
|
use crate::transaction::Transaction;
|
2020-02-08 13:19:32 -08:00
|
|
|
use crate::types::BlockHeight;
|
2020-08-09 17:26:49 -07:00
|
|
|
use crate::Network;
|
2019-09-24 22:14:48 -07:00
|
|
|
|
2020-06-25 09:18:05 -07:00
|
|
|
pub use hash::BlockHeaderHash;
|
|
|
|
pub use header::BlockHeader;
|
2020-08-09 17:26:49 -07:00
|
|
|
pub use light_client::LightClientRootHash;
|
2019-09-24 22:14:48 -07:00
|
|
|
|
2020-06-15 15:08:14 -07:00
|
|
|
/// A block in your blockchain.
|
|
|
|
///
|
|
|
|
/// A block is a data structure with two fields:
|
|
|
|
///
|
|
|
|
/// Block header: a data structure containing the block's metadata
|
|
|
|
/// Transactions: an array (vector in Rust) of transactions
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
2020-01-31 21:27:51 -08:00
|
|
|
#[cfg_attr(test, derive(Arbitrary))]
|
2019-09-24 11:16:41 -07:00
|
|
|
pub struct Block {
|
2020-02-07 13:05:04 -08:00
|
|
|
/// The block header, containing block metadata.
|
2019-09-24 11:16:41 -07:00
|
|
|
pub header: BlockHeader,
|
2019-09-25 16:33:12 -07:00
|
|
|
/// The block transactions.
|
2020-06-05 10:58:01 -07:00
|
|
|
pub transactions: Vec<Arc<Transaction>>,
|
2019-09-24 11:16:41 -07:00
|
|
|
}
|
2019-09-26 19:38:18 -07:00
|
|
|
|
2020-06-15 04:14:09 -07:00
|
|
|
/// The maximum size of a Zcash block, in bytes.
|
|
|
|
///
|
|
|
|
/// Post-Sapling, this is also the maximum size of a transaction
|
|
|
|
/// in the Zcash specification. (But since blocks also contain a
|
|
|
|
/// block header and transaction count, the maximum size of a
|
|
|
|
/// transaction in the chain is approximately 1.5 kB smaller.)
|
2020-07-06 17:37:32 -07:00
|
|
|
pub const MAX_BLOCK_BYTES: u64 = 2_000_000;
|
2020-06-15 04:14:09 -07:00
|
|
|
|
2020-07-14 20:21:06 -07:00
|
|
|
/// The error type for Block checks.
|
|
|
|
// TODO(jlusby): Error = Report ?
|
|
|
|
type Error = Box<dyn error::Error + Send + Sync + 'static>;
|
|
|
|
|
2020-02-08 13:19:32 -08:00
|
|
|
impl Block {
|
|
|
|
/// Return the block height reported in the coinbase transaction, if any.
|
|
|
|
pub fn coinbase_height(&self) -> Option<BlockHeight> {
|
|
|
|
use crate::transaction::TransparentInput;
|
|
|
|
self.transactions
|
|
|
|
.get(0)
|
|
|
|
.and_then(|tx| tx.inputs().next())
|
|
|
|
.and_then(|input| match input {
|
|
|
|
TransparentInput::Coinbase { ref height, .. } => Some(*height),
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
}
|
2020-07-14 20:21:06 -07:00
|
|
|
|
|
|
|
/// Check that there is exactly one coinbase transaction in `Block`, and that
|
|
|
|
/// the coinbase transaction is the first transaction in the block.
|
|
|
|
///
|
|
|
|
/// "The first (and only the first) transaction in a block is a coinbase
|
|
|
|
/// transaction, which collects and spends any miner subsidy and transaction
|
|
|
|
/// fees paid by transactions included in this block."[S 3.10][3.10]
|
|
|
|
///
|
|
|
|
/// [3.10]: https://zips.z.cash/protocol/protocol.pdf#coinbasetransactions
|
|
|
|
pub fn is_coinbase_first(&self) -> Result<(), Error> {
|
|
|
|
if self.coinbase_height().is_some() {
|
|
|
|
// No coinbase inputs in additional transactions allowed
|
|
|
|
if self
|
|
|
|
.transactions
|
|
|
|
.iter()
|
|
|
|
.skip(1)
|
|
|
|
.any(|tx| tx.contains_coinbase_input())
|
|
|
|
{
|
|
|
|
Err("coinbase input found in additional transaction")?
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err("no coinbase transaction in block")?
|
|
|
|
}
|
|
|
|
}
|
2020-07-22 18:01:31 -07:00
|
|
|
|
|
|
|
/// Get the hash for the current block
|
|
|
|
pub fn hash(&self) -> BlockHeaderHash {
|
|
|
|
BlockHeaderHash::from(self)
|
|
|
|
}
|
2020-08-09 17:26:49 -07:00
|
|
|
|
|
|
|
/// 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<LightClientRootHash> {
|
|
|
|
match self.coinbase_height() {
|
|
|
|
Some(height) => Some(LightClientRootHash::from_bytes(
|
2020-08-11 15:19:46 -07:00
|
|
|
self.header.light_client_root_bytes,
|
2020-08-09 17:26:49 -07:00
|
|
|
network,
|
|
|
|
height,
|
|
|
|
)),
|
|
|
|
None => None,
|
|
|
|
}
|
|
|
|
}
|
2020-02-08 13:19:32 -08:00
|
|
|
}
|
|
|
|
|
2020-02-07 09:21:23 -08:00
|
|
|
impl<'a> From<&'a Block> for BlockHeaderHash {
|
|
|
|
fn from(block: &'a Block) -> BlockHeaderHash {
|
|
|
|
(&block.header).into()
|
|
|
|
}
|
|
|
|
}
|