Re-use some block checks in the CheckpointVerifier

This commit is contained in:
teor 2021-03-11 12:10:47 +10:00
parent 252affdf84
commit c1a910942d
3 changed files with 33 additions and 21 deletions

View File

@ -33,7 +33,7 @@ use zebra_state as zs;
use crate::{error::*, transaction as tx};
use crate::{script, BoxError};
mod check;
pub mod check;
mod subsidy;
#[cfg(test)]
mod tests;

View File

@ -183,10 +183,22 @@ pub fn merkle_root_validity(
// Bitcoin's transaction Merkle trees are malleable, allowing blocks with
// duplicate transactions to have the same Merkle root as blocks without
// duplicate transactions. Duplicate transactions should cause a block to be
// duplicate transactions.
//
// Collecting into a HashSet deduplicates, so this checks that there are no
// duplicate transaction hashes, preventing Merkle root malleability.
//
// ## Full Block Validation
//
// Duplicate transactions should cause a block to be
// rejected, as duplicate transactions imply that the block contains a
// double-spend. As a defense-in-depth, however, we also check that there
// are no duplicate transaction hashes, by collecting into a HashSet.
// are no duplicate transaction hashes.
//
// ## Checkpoint Validation
//
// To prevent malleability (CVE-2012-2459), we also need to check
// whether the transaction hashes are unique.
use std::collections::HashSet;
if transaction_hashes.len() != transaction_hashes.iter().collect::<HashSet<_>>().len() {
return Err(BlockError::DuplicateTransaction);

View File

@ -14,7 +14,7 @@
//! block for the configured network.
use std::{
collections::{BTreeMap, HashSet},
collections::BTreeMap,
ops::{Bound, Bound::*},
pin::Pin,
sync::Arc,
@ -30,10 +30,11 @@ use tracing::instrument;
use zebra_chain::{
block::{self, Block},
parameters::{Network, GENESIS_PREVIOUS_BLOCK_HASH},
work::equihash,
};
use zebra_state as zs;
use crate::BoxError;
use crate::{block::VerifyBlockError, error::BlockError, BoxError};
pub(crate) mod list;
mod types;
@ -453,23 +454,8 @@ where
.iter()
.map(|tx| tx.hash())
.collect::<Vec<_>>();
let merkle_root = transaction_hashes.iter().cloned().collect();
// Check that the Merkle root is valid.
if block.header.merkle_root != merkle_root {
return Err(VerifyCheckpointError::BadMerkleRoot {
expected: block.header.merkle_root,
actual: merkle_root,
});
}
// To prevent malleability (CVE-2012-2459), we also need to check
// whether the transaction hashes are unique. Collecting into a HashSet
// deduplicates, so this checks that there are no duplicate transaction
// hashes, preventing Merkle root malleability.
if transaction_hashes.len() != transaction_hashes.iter().collect::<HashSet<_>>().len() {
return Err(VerifyCheckpointError::DuplicateTransaction);
}
crate::block::check::merkle_root_validity(&block, &transaction_hashes)?;
Ok(block_height)
}
@ -831,6 +817,8 @@ pub enum VerifyCheckpointError {
CommitFinalized(BoxError),
#[error(transparent)]
CheckpointList(BoxError),
#[error(transparent)]
VerifyBlock(BoxError),
#[error("too many queued blocks at this height")]
QueuedLimit,
#[error("the block hash does not match the chained checkpoint hash, expected {expected:?} found {found:?}")]
@ -842,6 +830,18 @@ pub enum VerifyCheckpointError {
ShuttingDown,
}
impl From<VerifyBlockError> for VerifyCheckpointError {
fn from(err: VerifyBlockError) -> VerifyCheckpointError {
VerifyCheckpointError::VerifyBlock(err.into())
}
}
impl From<BlockError> for VerifyCheckpointError {
fn from(err: BlockError) -> VerifyCheckpointError {
VerifyCheckpointError::VerifyBlock(err.into())
}
}
/// The CheckpointVerifier service implementation.
///
/// After verification, the block futures resolve to their hashes.