Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 15 additions & 17 deletions src/chain/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,10 @@ use super::{
error::{CFHeaderSyncError, CFilterSyncError, HeaderSyncError},
graph::{AcceptHeaderChanges, BlockTree, HeaderRejection},
CFHeaderChanges, ChainState, Filter, FilterCheck, FilterHeaderRequest, FilterRequest,
FilterRequestState, HeightMonitor, PeerId,
FilterRequestState, HeaderValidationExt, HeightMonitor, PeerId,
};
use crate::IndexedFilter;
use crate::{
chain::{header_batch::HeadersBatch, BlockHeaderChanges},
messages::Event,
Dialog, Info, Progress,
};
use crate::{chain::BlockHeaderChanges, messages::Event, Dialog, Info, Progress};

const FILTER_BASIC: u8 = 0x00;
const CF_HEADER_BATCH_SIZE: u32 = 1_999;
Expand Down Expand Up @@ -91,11 +87,18 @@ impl Chain {
// Sync the chain with headers from a peer, adjusting to reorgs if needed
pub(crate) fn sync_chain(
&mut self,
message: Vec<Header>,
header_batch: Vec<Header>,
) -> Result<Vec<BlockHash>, HeaderSyncError> {
let header_batch = HeadersBatch::new(message).map_err(|_| HeaderSyncError::EmptyMessage)?;
if header_batch.is_empty() {
return Err(HeaderSyncError::EmptyMessage);
}
// If our chain already has the last header in the message there is no new information
if self.header_chain.contains(header_batch.last().block_hash()) {
if self.header_chain.contains(
header_batch
.last()
.expect("non-empty check above.")
.block_hash(),
) {
return Ok(Vec::new());
}
// We check first if the peer is sending us nonsense
Expand Down Expand Up @@ -153,21 +156,16 @@ impl Chain {
}

// These are invariants in all batches of headers we receive
fn sanity_check(&mut self, header_batch: &HeadersBatch) -> Result<(), HeaderSyncError> {
// All the headers connect with each other and is the difficulty adjustment not absurd
fn sanity_check(&mut self, header_batch: &[Header]) -> Result<(), HeaderSyncError> {
if !header_batch.connected() {
return Err(HeaderSyncError::HeadersNotConnected);
}

// All headers pass their own proof of work and the network minimum
if !header_batch.individually_valid_pow() {
if !header_batch.passes_own_pow() {
return Err(HeaderSyncError::InvalidHeaderWork);
}

if !header_batch.bits_adhere_transition(self.network) {
if !header_batch.bits_adhere_transition_threshold(self.network) {
return Err(HeaderSyncError::InvalidBits);
}

Ok(())
}

Expand Down
78 changes: 0 additions & 78 deletions src/chain/header_batch.rs

This file was deleted.

39 changes: 37 additions & 2 deletions src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pub mod checkpoints;
#[allow(dead_code)]
pub(crate) mod error;
pub(crate) mod graph;
pub(crate) mod header_batch;

use std::collections::HashMap;

Expand All @@ -20,7 +19,7 @@ use bitcoin::hashes::{sha256d, Hash};
use bitcoin::Amount;
use bitcoin::{
bip158::BlockFilter, block::Header, params::Params, BlockHash, FilterHash, FilterHeader,
ScriptBuf, Work,
ScriptBuf, Target, Work,
};

use crate::network::PeerId;
Expand Down Expand Up @@ -284,6 +283,42 @@ impl ZerolikeExt for Work {
}
}

trait HeaderValidationExt {
// Headers are logically connected.
fn connected(&self) -> bool;
// Each header passes its own work target.
fn passes_own_pow(&self) -> bool;
// Targets do not change out of the acceptable range.
fn bits_adhere_transition_threshold(&self, params: impl AsRef<Params>) -> bool;
}

impl HeaderValidationExt for &[Header] {
fn connected(&self) -> bool {
self.iter()
.zip(self.iter().skip(1))
.all(|(first, second)| first.block_hash().eq(&second.prev_blockhash))
}

fn passes_own_pow(&self) -> bool {
!self.iter().any(|header| {
let target = header.target();
let valid_pow = header.validate_pow(target);
valid_pow.is_err()
})
}

fn bits_adhere_transition_threshold(&self, params: impl AsRef<Params>) -> bool {
let params = params.as_ref();
if params.allow_min_difficulty_blocks {
return true;
}
self.iter().zip(self.iter().skip(1)).all(|(first, second)| {
let transition = Target::from_compact(first.bits).max_transition_threshold(params);
Target::from_compact(second.bits).le(&transition)
})
}
}

// Emulation of `GetBlockSubsidy` in Bitcoin Core: https://github.com/bitcoin/bitcoin/blob/master/src/validation.cpp#L1944
pub(crate) fn block_subsidy(height: u32) -> Amount {
let halvings = height / SUBSIDY_HALVING_INTERVAL;
Expand Down