chain: move Block into a leaf module.
This might make things a little easier to rearrange. In the future it would probably be good to change to block::Hash, block::Header, etc.
This commit is contained in:
parent
948b067808
commit
b296d1e2a3
|
@ -1,6 +1,9 @@
|
||||||
//! Definitions of block datastructures.
|
//! Definitions of block datastructures.
|
||||||
#![allow(clippy::unit_arg)]
|
|
||||||
|
|
||||||
|
// block::block is done on purpose and is the most representative name
|
||||||
|
#![allow(clippy::module_inception)]
|
||||||
|
|
||||||
|
mod block;
|
||||||
mod hash;
|
mod hash;
|
||||||
mod header;
|
mod header;
|
||||||
mod height;
|
mod height;
|
||||||
|
@ -10,108 +13,12 @@ mod serialize;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
pub use block::Block;
|
||||||
use std::{error, sync::Arc};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
use proptest_derive::Arbitrary;
|
|
||||||
|
|
||||||
use crate::parameters::Network;
|
|
||||||
use crate::transaction::Transaction;
|
|
||||||
|
|
||||||
pub use hash::BlockHeaderHash;
|
pub use hash::BlockHeaderHash;
|
||||||
pub use header::BlockHeader;
|
pub use header::BlockHeader;
|
||||||
pub use height::BlockHeight;
|
pub use height::BlockHeight;
|
||||||
pub use light_client::LightClientRootHash;
|
pub use light_client::LightClientRootHash;
|
||||||
|
|
||||||
/// 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)]
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
pub struct Block {
|
|
||||||
/// The block header, containing block metadata.
|
|
||||||
pub header: BlockHeader,
|
|
||||||
/// The block transactions.
|
|
||||||
pub transactions: Vec<Arc<Transaction>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.)
|
|
||||||
pub const MAX_BLOCK_BYTES: u64 = 2_000_000;
|
|
||||||
|
|
||||||
/// The error type for Block checks.
|
/// The error type for Block checks.
|
||||||
// TODO(jlusby): Error = Report ?
|
// XXX try to remove this -- block checks should be done in zebra-consensus
|
||||||
type Error = Box<dyn error::Error + Send + Sync + 'static>;
|
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||||
|
|
||||||
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().get(0))
|
|
||||||
.and_then(|input| match input {
|
|
||||||
TransparentInput::Coinbase { ref height, .. } => Some(*height),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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." [§3.10][3.10]
|
|
||||||
///
|
|
||||||
/// [3.10]: https://zips.z.cash/protocol/protocol.pdf#coinbasetransactions
|
|
||||||
pub fn is_coinbase_first(&self) -> Result<(), Error> {
|
|
||||||
let first = self
|
|
||||||
.transactions
|
|
||||||
.get(0)
|
|
||||||
.ok_or_else(|| "block has no transactions")?;
|
|
||||||
let mut rest = self.transactions.iter().skip(1);
|
|
||||||
if !first.is_coinbase() {
|
|
||||||
Err("first transaction must be coinbase")?;
|
|
||||||
}
|
|
||||||
if rest.any(|tx| tx.contains_coinbase_input()) {
|
|
||||||
Err("coinbase input found in non-coinbase transaction")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the hash for the current 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<LightClientRootHash> {
|
|
||||||
match self.coinbase_height() {
|
|
||||||
Some(height) => Some(LightClientRootHash::from_bytes(
|
|
||||||
self.header.light_client_root_bytes,
|
|
||||||
network,
|
|
||||||
height,
|
|
||||||
)),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Block> for BlockHeaderHash {
|
|
||||||
fn from(block: &'a Block) -> BlockHeaderHash {
|
|
||||||
(&block.header).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::parameters::Network;
|
||||||
|
use crate::transaction::Transaction;
|
||||||
|
|
||||||
|
use super::{BlockHeader, BlockHeaderHash, BlockHeight, Error, LightClientRootHash};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use proptest_derive::Arbitrary;
|
||||||
|
|
||||||
|
/// 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)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
pub struct Block {
|
||||||
|
/// The block header, containing block metadata.
|
||||||
|
pub header: BlockHeader,
|
||||||
|
/// The block transactions.
|
||||||
|
pub transactions: Vec<Arc<Transaction>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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().get(0))
|
||||||
|
.and_then(|input| match input {
|
||||||
|
TransparentInput::Coinbase { ref height, .. } => Some(*height),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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." [§3.10][3.10]
|
||||||
|
///
|
||||||
|
/// [3.10]: https://zips.z.cash/protocol/protocol.pdf#coinbasetransactions
|
||||||
|
pub fn is_coinbase_first(&self) -> Result<(), Error> {
|
||||||
|
let first = self
|
||||||
|
.transactions
|
||||||
|
.get(0)
|
||||||
|
.ok_or_else(|| "block has no transactions")?;
|
||||||
|
let mut rest = self.transactions.iter().skip(1);
|
||||||
|
if !first.is_coinbase() {
|
||||||
|
Err("first transaction must be coinbase")?;
|
||||||
|
}
|
||||||
|
if rest.any(|tx| tx.contains_coinbase_input()) {
|
||||||
|
Err("coinbase input found in non-coinbase transaction")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the hash for the current 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<LightClientRootHash> {
|
||||||
|
match self.coinbase_height() {
|
||||||
|
Some(height) => Some(LightClientRootHash::from_bytes(
|
||||||
|
self.header.light_client_root_bytes,
|
||||||
|
network,
|
||||||
|
height,
|
||||||
|
)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Block> for BlockHeaderHash {
|
||||||
|
fn from(block: &'a Block) -> BlockHeaderHash {
|
||||||
|
(&block.header).into()
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,14 @@ use crate::work::{difficulty::CompactDifficulty, equihash};
|
||||||
use super::Block;
|
use super::Block;
|
||||||
use super::BlockHeader;
|
use super::BlockHeader;
|
||||||
use super::BlockHeaderHash;
|
use super::BlockHeaderHash;
|
||||||
use super::MAX_BLOCK_BYTES;
|
|
||||||
|
/// 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.)
|
||||||
|
pub const MAX_BLOCK_BYTES: u64 = 2_000_000;
|
||||||
|
|
||||||
impl ZcashSerialize for BlockHeader {
|
impl ZcashSerialize for BlockHeader {
|
||||||
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||||
|
|
|
@ -3,11 +3,12 @@ use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{Block, BlockHeader, MAX_BLOCK_BYTES},
|
|
||||||
serialization::{ZcashDeserialize, ZcashSerialize},
|
serialization::{ZcashDeserialize, ZcashSerialize},
|
||||||
transaction::{LockTime, Transaction, TransparentInput, TransparentOutput},
|
transaction::{LockTime, Transaction, TransparentInput, TransparentOutput},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::super::{serialize::MAX_BLOCK_BYTES, Block, BlockHeader};
|
||||||
|
|
||||||
/// Generate a block header
|
/// Generate a block header
|
||||||
pub fn block_header() -> BlockHeader {
|
pub fn block_header() -> BlockHeader {
|
||||||
BlockHeader::zcash_deserialize(&zebra_test::vectors::DUMMY_HEADER[..]).unwrap()
|
BlockHeader::zcash_deserialize(&zebra_test::vectors::DUMMY_HEADER[..]).unwrap()
|
||||||
|
|
|
@ -6,7 +6,7 @@ use proptest::{arbitrary::any, prelude::*, test_runner::Config};
|
||||||
use crate::parameters::Network;
|
use crate::parameters::Network;
|
||||||
use crate::serialization::{SerializationError, ZcashDeserializeInto, ZcashSerialize};
|
use crate::serialization::{SerializationError, ZcashDeserializeInto, ZcashSerialize};
|
||||||
|
|
||||||
use super::super::*;
|
use super::super::{serialize::MAX_BLOCK_BYTES, *};
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use std::io::{Cursor, Write};
|
use std::{
|
||||||
|
io::{Cursor, Write},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use chrono::{DateTime, Duration, LocalResult, TimeZone, Utc};
|
use chrono::{DateTime, Duration, LocalResult, TimeZone, Utc};
|
||||||
|
|
||||||
use crate::serialization::{sha256d, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize};
|
use crate::serialization::{sha256d, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize};
|
||||||
use crate::transaction::LockTime;
|
use crate::transaction::LockTime;
|
||||||
|
|
||||||
use super::super::*;
|
use super::super::{serialize::MAX_BLOCK_BYTES, *};
|
||||||
use super::generate; // XXX this should be rewritten as strategies
|
use super::generate; // XXX this should be rewritten as strategies
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -145,7 +145,6 @@ fn main() -> Result<()> {
|
||||||
assert!(height <= BlockHeight::MAX);
|
assert!(height <= BlockHeight::MAX);
|
||||||
assert_eq!(x, height.0);
|
assert_eq!(x, height.0);
|
||||||
let size = v["size"].as_u64().unwrap();
|
let size = v["size"].as_u64().unwrap();
|
||||||
assert!(size <= zebra_chain::block::MAX_BLOCK_BYTES);
|
|
||||||
|
|
||||||
// compute
|
// compute
|
||||||
cumulative_bytes += size;
|
cumulative_bytes += size;
|
||||||
|
|
Loading…
Reference in New Issue