Skip to content

Commit d52c5b5

Browse files
Merge pull request #1124 from mintlayer/latch_ibd_flag
Latch `is_initial_block_download_finished` flag
2 parents 3e1dad0 + d42ceaa commit d52c5b5

File tree

7 files changed

+53
-28
lines changed

7 files changed

+53
-28
lines changed

chainstate/src/detail/mod.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ use common::{
7676
use logging::log;
7777
use utils::{
7878
eventhandler::{EventHandler, EventsController},
79+
set_flag::SetFlag,
7980
tap_error_log::LogError,
8081
};
8182
use utxo::UtxosDB;
@@ -103,7 +104,7 @@ pub struct Chainstate<S, V> {
103104
custom_orphan_error_hook: Option<Arc<OrphanErrorHandler>>,
104105
events_controller: EventsController<ChainstateEvent>,
105106
time_getter: TimeGetter,
106-
is_initial_block_download_finished: bool,
107+
is_initial_block_download_finished: SetFlag,
107108
}
108109

109110
#[derive(Copy, Clone, Eq, Debug, PartialEq)]
@@ -189,6 +190,8 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> Chainstate<S, V>
189190
chainstate.check_genesis().map_err(crate::ChainstateError::from)?;
190191
}
191192

193+
chainstate.update_initial_block_download_flag()?;
194+
192195
Ok(chainstate)
193196
}
194197

@@ -210,7 +213,7 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> Chainstate<S, V>
210213
custom_orphan_error_hook,
211214
events_controller: EventsController::new(),
212215
time_getter,
213-
is_initial_block_download_finished: false,
216+
is_initial_block_download_finished: SetFlag::new(),
214217
}
215218
}
216219

@@ -621,7 +624,8 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> Chainstate<S, V>
621624
bi.block_timestamp(),
622625
);
623626

624-
self.is_initial_block_download_finished = self.is_fresh_block(&bi.block_timestamp());
627+
self.update_initial_block_download_flag()
628+
.map_err(BlockError::BestBlockIdQueryError)?;
625629
}
626630

627631
Ok(result)
@@ -744,28 +748,38 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> Chainstate<S, V>
744748
&self.events_controller
745749
}
746750

747-
pub fn is_initial_block_download(&self) -> Result<bool, PropertyQueryError> {
748-
if self.is_initial_block_download_finished {
749-
return Ok(false);
751+
pub fn is_initial_block_download(&self) -> bool {
752+
!self.is_initial_block_download_finished.test()
753+
}
754+
755+
/// Returns true if the given block timestamp is newer than `ChainstateConfig::max_tip_age`.
756+
fn is_fresh_block(&self, time: &BlockTimestamp) -> bool {
757+
let now = self.time_getter.get_time();
758+
time.as_duration_since_epoch() + self.chainstate_config.max_tip_age.clone().into() > now
759+
}
760+
761+
/// Update `is_initial_block_download_finished` when tip changes (can only be set once)
762+
fn update_initial_block_download_flag(&mut self) -> Result<(), PropertyQueryError> {
763+
if self.is_initial_block_download_finished.test() {
764+
return Ok(());
750765
}
751766

752767
// TODO: Add a check for importing and reindex.
753768

754769
// TODO: Add a check for the chain trust.
755770

756771
let tip_timestamp = match self.query()?.get_best_block_header() {
757-
Ok(h) => Ok(h.timestamp()),
772+
Ok(h) => h.timestamp(),
758773
// There is only the genesis block, so the initial block download isn't finished yet.
759-
Err(PropertyQueryError::GenesisHeaderRequested) => return Ok(true),
760-
Err(e) => Err(e),
761-
}?;
762-
Ok(!self.is_fresh_block(&tip_timestamp))
763-
}
774+
Err(PropertyQueryError::GenesisHeaderRequested) => return Ok(()),
775+
Err(e) => return Err(e),
776+
};
764777

765-
/// Returns true if the given block timestamp is newer than `ChainstateConfig::max_tip_age`.
766-
fn is_fresh_block(&self, time: &BlockTimestamp) -> bool {
767-
let now = self.time_getter.get_time();
768-
time.as_duration_since_epoch() + self.chainstate_config.max_tip_age.clone().into() > now
778+
if self.is_fresh_block(&tip_timestamp) {
779+
self.is_initial_block_download_finished.set();
780+
}
781+
782+
Ok(())
769783
}
770784

771785
fn check_legitimate_orphan(

chainstate/src/interface/chainstate_interface.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub trait ChainstateInterface: Send {
190190
fn utxo(&self, outpoint: &UtxoOutPoint) -> Result<Option<Utxo>, ChainstateError>;
191191

192192
/// Returns true if the initial block download isn't finished yet.
193-
fn is_initial_block_download(&self) -> Result<bool, ChainstateError>;
193+
fn is_initial_block_download(&self) -> bool;
194194

195195
/// Check whether stake pool with given ID exists.
196196
fn stake_pool_exists(&self, pool_id: PoolId) -> Result<bool, ChainstateError>;

chainstate/src/interface/chainstate_interface_impl.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,8 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> ChainstateInterfa
477477
.map_err(|e| ChainstateError::FailedToReadProperty(e.into()))
478478
}
479479

480-
fn is_initial_block_download(&self) -> Result<bool, ChainstateError> {
481-
self.chainstate.is_initial_block_download().map_err(ChainstateError::from)
480+
fn is_initial_block_download(&self) -> bool {
481+
self.chainstate.is_initial_block_download()
482482
}
483483

484484
fn stake_pool_exists(&self, pool_id: PoolId) -> Result<bool, ChainstateError> {
@@ -558,7 +558,7 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> ChainstateInterfa
558558

559559
let median_time = self.calculate_median_time_past(&best_block_id)?;
560560

561-
let is_initial_block_download = self.is_initial_block_download()?;
561+
let is_initial_block_download = self.is_initial_block_download();
562562

563563
Ok(ChainInfo {
564564
best_block_height,

chainstate/src/interface/chainstate_interface_impl_delegation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ where
280280
self.deref().utxo(outpoint)
281281
}
282282

283-
fn is_initial_block_download(&self) -> Result<bool, ChainstateError> {
283+
fn is_initial_block_download(&self) -> bool {
284284
self.deref().is_initial_block_download()
285285
}
286286

chainstate/test-suite/src/tests/syncing_tests.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ fn initial_block_download(#[case] seed: Seed) {
470470
.build();
471471

472472
// Only genesis block, so is_initial_block_download should return true.
473-
assert!(tf.chainstate.is_initial_block_download().unwrap());
473+
assert!(tf.chainstate.is_initial_block_download());
474474

475475
// Create a block with an "old" timestamp.
476476
let now = tf.current_time();
@@ -479,15 +479,26 @@ fn initial_block_download(#[case] seed: Seed) {
479479
.with_timestamp(BlockTimestamp::from_duration_since_epoch(now))
480480
.build_and_process()
481481
.unwrap();
482-
assert!(tf.chainstate.is_initial_block_download().unwrap());
482+
assert!(tf.chainstate.is_initial_block_download());
483483

484484
// Create a block with fresh timestamp.
485485
tf.make_block_builder().build_and_process().unwrap();
486-
assert!(!tf.chainstate.is_initial_block_download().unwrap());
486+
assert!(!tf.chainstate.is_initial_block_download());
487487

488488
// Add one more block.
489489
tf.make_block_builder().build_and_process().unwrap();
490-
assert!(!tf.chainstate.is_initial_block_download().unwrap());
490+
assert!(!tf.chainstate.is_initial_block_download());
491+
492+
// Check that receiving an "old" block does not revert `is_initial_block_download` back
493+
tf.progress_time_seconds_since_epoch(5);
494+
let now = tf.current_time();
495+
let block = tf
496+
.make_block_builder()
497+
.with_timestamp(BlockTimestamp::from_duration_since_epoch(now))
498+
.build();
499+
tf.progress_time_seconds_since_epoch(10);
500+
tf.process_block(block, BlockSource::Local).unwrap();
501+
assert!(!tf.chainstate.is_initial_block_download());
491502
});
492503
}
493504

mocks/src/chainstate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ mockall::mock! {
140140
include_orphans: bool,
141141
) -> Result<(), ChainstateError>;
142142
fn utxo(&self, outpoint: &UtxoOutPoint) -> Result<Option<Utxo>, ChainstateError>;
143-
fn is_initial_block_download(&self) -> Result<bool, ChainstateError>;
143+
fn is_initial_block_download(&self) -> bool;
144144
fn stake_pool_exists(&self, pool_id: PoolId) -> Result<bool, ChainstateError>;
145145
fn get_stake_pool_balance(&self, pool_id: PoolId) -> Result<Option<Amount>, ChainstateError>;
146146
fn get_stake_pool_data(&self, pool_id: PoolId) -> Result<Option<PoolData>, ChainstateError>;

p2p/src/sync/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ where
137137
.await
138138
// This shouldn't fail unless the chainstate subsystem is down which shouldn't
139139
// happen since subsystems are shutdown in reverse order.
140-
.expect("Chainstate call failed")?,
140+
.expect("Chainstate call failed"),
141141
);
142142

143143
let mut tx_processed_receiver = subscribe_to_tx_processed(&self.mempool_handle).await?;
@@ -215,7 +215,7 @@ where
215215
/// Announces the header of a new block to peers.
216216
async fn handle_new_tip(&mut self, block_id: Id<Block>) -> Result<()> {
217217
let is_initial_block_download = if self.is_initial_block_download.load() {
218-
let is_ibd = self.chainstate_handle.call(|c| c.is_initial_block_download()).await??;
218+
let is_ibd = self.chainstate_handle.call(|c| c.is_initial_block_download()).await?;
219219
self.is_initial_block_download.store(is_ibd);
220220
is_ibd
221221
} else {

0 commit comments

Comments
 (0)