Skip to content

Commit

Permalink
fix: header sync must allow transition to archival/pruned if tip is b…
Browse files Browse the repository at this point in the history
…ehind (#3520)

Description
---
- Changes header sync to check the metadata height as there is still one case where it is valid for peers to enter header sync when all headers are synced
- Use last chain header instead of tip chain header when determining header sync state

Motivation and Context
---
Node would ban peer when headers are synced and sync is being resumed 

How Has This Been Tested?
---
Manually - header sync does not ban peer in this case
  • Loading branch information
sdbondi authored Nov 3, 2021
1 parent 48a54b0 commit e028386
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 11 deletions.
37 changes: 26 additions & 11 deletions base_layer/core/src/base_node/sync/header_sync/synchronizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,22 +246,37 @@ impl<'a, B: BlockchainBackend + 'static> HeaderSynchronizer<'a, B> {
);

// Fetch the local tip header at the beginning of the sync process
let local_tip_header = self.db.fetch_tip_header().await?;
let local_tip_header = self.db.fetch_last_chain_header().await?;
let local_total_accumulated_difficulty = local_tip_header.accumulated_data().total_accumulated_difficulty;
let header_tip_height = local_tip_header.height();
let sync_status = self
.determine_sync_status(sync_peer, local_tip_header, &mut client)
.await?;
match sync_status {
SyncStatus::InSync => Err(BlockHeaderSyncError::PeerSentInaccurateChainMetadata {
claimed: sync_peer.claimed_chain_metadata().accumulated_difficulty(),
actual: None,
local: local_total_accumulated_difficulty,
}),
SyncStatus::WereAhead => Err(BlockHeaderSyncError::PeerSentInaccurateChainMetadata {
claimed: sync_peer.claimed_chain_metadata().accumulated_difficulty(),
actual: None,
local: local_total_accumulated_difficulty,
}),
SyncStatus::InSync | SyncStatus::WereAhead => {
let metadata = self.db.get_chain_metadata().await?;
if metadata.height_of_longest_chain() < header_tip_height {
debug!(
target: LOG_TARGET,
"Headers are in sync at height {} but tip is {}. Proceeding to archival/pruned block sync",
header_tip_height,
metadata.height_of_longest_chain()
);
Ok(())
} else {
debug!(
target: LOG_TARGET,
"Headers and block state are already in-sync (Header Tip: {}, Block tip: {})",
header_tip_height,
metadata.height_of_longest_chain()
);
Err(BlockHeaderSyncError::PeerSentInaccurateChainMetadata {
claimed: sync_peer.claimed_chain_metadata().accumulated_difficulty(),
actual: None,
local: local_total_accumulated_difficulty,
})
}
},
SyncStatus::Lagging(split_info) => {
self.hooks.call_on_progress_header_hooks(
split_info.local_tip_header.height(),
Expand Down
2 changes: 2 additions & 0 deletions base_layer/core/src/chain_storage/async_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ impl<B: BlockchainBackend + 'static> AsyncBlockchainDb<B> {

make_async_fn!(fetch_last_header() -> BlockHeader, "fetch_last_header");

make_async_fn!(fetch_last_chain_header() -> ChainHeader, "fetch_last_chain_header");

make_async_fn!(fetch_tip_header() -> ChainHeader, "fetch_tip_header");

make_async_fn!(insert_valid_headers(headers: Vec<ChainHeader>) -> (), "insert_valid_headers");
Expand Down
2 changes: 2 additions & 0 deletions base_layer/core/src/chain_storage/blockchain_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ pub trait BlockchainBackend: Send + Sync {
fn orphan_count(&self) -> Result<usize, ChainStorageError>;
/// Returns the stored header with the highest corresponding height.
fn fetch_last_header(&self) -> Result<BlockHeader, ChainStorageError>;
/// Returns the stored header and accumulated data with the highest height.
fn fetch_last_chain_header(&self) -> Result<ChainHeader, ChainStorageError>;
/// Returns the stored header with the highest corresponding height.
fn fetch_tip_header(&self) -> Result<ChainHeader, ChainStorageError>;
/// Returns the stored chain metadata.
Expand Down
5 changes: 5 additions & 0 deletions base_layer/core/src/chain_storage/blockchain_database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,11 @@ where B: BlockchainBackend
db.fetch_last_header()
}

pub fn fetch_last_chain_header(&self) -> Result<ChainHeader, ChainStorageError> {
let db = self.db_read_access()?;
db.fetch_last_chain_header()
}

/// Returns the sum of all kernels
pub fn fetch_kernel_commitment_sum(&self, at_hash: &HashOutput) -> Result<Commitment, ChainStorageError> {
Ok(self.fetch_block_accumulated_data(at_hash.clone())?.kernel_sum)
Expand Down
25 changes: 25 additions & 0 deletions base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1942,6 +1942,31 @@ impl BlockchainBackend for LMDBDatabase {
})
}

/// Finds and returns the last stored header.
fn fetch_last_chain_header(&self) -> Result<ChainHeader, ChainStorageError> {
let txn = self.read_transaction()?;
let header = self.fetch_last_header_in_txn(&txn)?.ok_or_else(|| {
ChainStorageError::InvalidOperation("Cannot fetch last header because database is empty".to_string())
})?;
let height = header.height;
let accumulated_data = self
.fetch_header_accumulated_data_by_height(&txn, height)?
.ok_or_else(|| ChainStorageError::ValueNotFound {
entity: "BlockHeaderAccumulatedData",
field: "height",
value: height.to_string(),
})?;

let chain_header = ChainHeader::try_construct(header, accumulated_data).ok_or_else(|| {
ChainStorageError::DataInconsistencyDetected {
function: "fetch_tip_header",
details: format!("Accumulated data mismatch at height #{}", height),
}
})?;

Ok(chain_header)
}

fn fetch_tip_header(&self) -> Result<ChainHeader, ChainStorageError> {
let txn = self.read_transaction()?;

Expand Down
4 changes: 4 additions & 0 deletions base_layer/core/src/test_helpers/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,10 @@ impl BlockchainBackend for TempDatabase {
self.db.as_ref().unwrap().fetch_last_header()
}

fn fetch_last_chain_header(&self) -> Result<ChainHeader, ChainStorageError> {
self.db.as_ref().unwrap().fetch_last_chain_header()
}

fn fetch_tip_header(&self) -> Result<ChainHeader, ChainStorageError> {
self.db.as_ref().unwrap().fetch_tip_header()
}
Expand Down

0 comments on commit e028386

Please sign in to comment.