consensus: tidy merkle validation in checkpointer
This moves it in with the existing `check_block` method and expands that method's contract to cover general block validation checks.
This commit is contained in:
parent
7c08c0c315
commit
706f1fff81
|
@ -423,15 +423,40 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the block height of `block` is valid, returns that height.
|
/// Check that the block height and Merkle root are valid.
|
||||||
///
|
///
|
||||||
/// Returns an error if the block's height is invalid, see `check_height()`
|
/// Checking the Merkle root ensures that the block hash binds the block
|
||||||
/// for details.
|
/// contents. To prevent malleability (CVE-2012-2459), we also need to check
|
||||||
|
/// whether the transaction hashes are unique.
|
||||||
fn check_block(&self, block: &Block) -> Result<block::Height, VerifyCheckpointError> {
|
fn check_block(&self, block: &Block) -> Result<block::Height, VerifyCheckpointError> {
|
||||||
let block_height = block
|
let block_height = block
|
||||||
.coinbase_height()
|
.coinbase_height()
|
||||||
.ok_or(VerifyCheckpointError::CoinbaseHeight { hash: block.hash() })?;
|
.ok_or(VerifyCheckpointError::CoinbaseHeight { hash: block.hash() })?;
|
||||||
self.check_height(block_height)?;
|
self.check_height(block_height)?;
|
||||||
|
|
||||||
|
let transaction_hashes = block
|
||||||
|
.transactions
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(block_height)
|
Ok(block_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,7 +475,7 @@ where
|
||||||
// Set up a oneshot channel to send results
|
// Set up a oneshot channel to send results
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
// Check for a valid height
|
// Check that the height and Merkle roots are valid.
|
||||||
let height = match self.check_block(&block) {
|
let height = match self.check_block(&block) {
|
||||||
Ok(height) => height,
|
Ok(height) => height,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
@ -459,33 +484,6 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for a valid Merkle root. To prevent malleability (CVE-2012-2459),
|
|
||||||
// we also need to check whether the transaction hashes are unique.
|
|
||||||
|
|
||||||
let transaction_hashes = block
|
|
||||||
.transactions
|
|
||||||
.iter()
|
|
||||||
.map(|tx| tx.hash())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let merkle_root = transaction_hashes.iter().cloned().collect();
|
|
||||||
|
|
||||||
if block.header.merkle_root != merkle_root {
|
|
||||||
tx.send(Err(VerifyCheckpointError::BadMerkleRoot {
|
|
||||||
expected: block.header.merkle_root,
|
|
||||||
actual: merkle_root,
|
|
||||||
}))
|
|
||||||
.expect("rx has not been dropped yet");
|
|
||||||
return rx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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() {
|
|
||||||
tx.send(Err(VerifyCheckpointError::DuplicateTransaction))
|
|
||||||
.expect("rx has not been dropped yet");
|
|
||||||
return rx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since we're using Arc<Block>, each entry is a single pointer to the
|
// Since we're using Arc<Block>, each entry is a single pointer to the
|
||||||
// Arc. But there are a lot of QueuedBlockLists in the queue, so we keep
|
// Arc. But there are a lot of QueuedBlockLists in the queue, so we keep
|
||||||
// allocations as small as possible.
|
// allocations as small as possible.
|
||||||
|
|
Loading…
Reference in New Issue