Document part of the block header consensus rules (#3296)

* document header version consensus rule

* document nbits threshold consensus rule

* document difficulty filter consensus rule

* document header solution consensus rule

* document header time consensus rule

* document upper time limit consensus rule

* document max block size consensus rule

* skip genesis in conesnsus rule check

* remove fixed comment

Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Alfredo Garcia 2021-12-29 20:07:27 -03:00 committed by GitHub
parent 041a2693b7
commit 918a337d8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 8 deletions

View File

@ -67,6 +67,12 @@ impl ZcashDeserialize for Header {
"high bit was set in version field",
));
}
// # Consensus
//
// > The block version number MUST be greater than or equal to 4.
//
// https://zips.z.cash/protocol/protocol.pdf#blockheader
if version < 4 {
return Err(SerializationError::Parse("version must be at least 4"));
}
@ -115,6 +121,12 @@ impl ZcashSerialize for Block {
impl ZcashDeserialize for Block {
fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
// # Consensus
//
// > The size of a block MUST be less than or equal to 2000000 bytes.
//
// https://zips.z.cash/protocol/protocol.pdf#blockheader
//
// If the limit is reached, we'll get an UnexpectedEof error
let limited_reader = &mut reader.take(MAX_BLOCK_BYTES);
Ok(Block {

View File

@ -71,13 +71,13 @@ pub fn difficulty_is_valid(
))?;
}
// The difficulty filter is also context-free.
// # Consensus
//
// ZIP 205 and ZIP 208 incorrectly describe testnet minimum difficulty blocks
// as a change to the difficulty filter. But in `zcashd`, it is implemented
// as a change to the difficulty adjustment algorithm. So we don't need to
// do anything special for testnet here.
// For details, see https://github.com/zcash/zips/issues/416
// > The block MUST pass the difficulty filter.
//
// https://zips.z.cash/protocol/protocol.pdf#blockheader
//
// The difficulty filter is also context-free.
if hash > &difficulty_threshold {
Err(BlockError::DifficultyFilter(
*height,
@ -92,6 +92,11 @@ pub fn difficulty_is_valid(
/// Returns `Ok(())` if the `EquihashSolution` is valid for `header`
pub fn equihash_solution_is_valid(header: &Header) -> Result<(), equihash::Error> {
// # Consensus
//
// > `solution` MUST represent a valid Equihash solution.
//
// https://zips.z.cash/protocol/protocol.pdf#blockheader
header.solution.check(header)
}

View File

@ -229,14 +229,31 @@ fn difficulty_threshold_is_valid(
let median_time_past = difficulty_adjustment.median_time_past();
let block_time_max =
median_time_past + Duration::seconds(difficulty::BLOCK_MAX_TIME_SINCE_MEDIAN);
if candidate_time <= median_time_past {
// # Consensus
//
// > For each block other than the genesis block, `nTime` MUST be strictly greater
// than the median-time-past of that block.
//
// https://zips.z.cash/protocol/protocol.pdf#blockheader
let genesis_height = NetworkUpgrade::Genesis
.activation_height(network)
.expect("Zebra always has a genesis height available");
if candidate_time <= median_time_past && candidate_height != genesis_height {
Err(ValidateContextError::TimeTooEarly {
candidate_time,
median_time_past,
})?
}
// The maximum time rule is only active on Testnet from a specific height
// # Consensus
//
// > For each block at block height 2 or greater on Mainnet, or block height 653_606
// or greater on Testnet, `nTime` MUST be less than or equal to the median-time-past
// of that block plus 90*60 seconds.
//
// https://zips.z.cash/protocol/protocol.pdf#blockheader
if NetworkUpgrade::is_max_block_time_enforced(network, candidate_height)
&& candidate_time > block_time_max
{
@ -246,6 +263,11 @@ fn difficulty_threshold_is_valid(
})?
}
// # Consensus
//
// > For a block at block height `Height`, `nBits` MUST be equal to `ThresholdBits(Height)`.
//
// https://zips.z.cash/protocol/protocol.pdf#blockheader
let expected_difficulty = difficulty_adjustment.expected_difficulty_threshold();
if difficulty_threshold != expected_difficulty {
Err(ValidateContextError::InvalidDifficultyThreshold {