diff --git a/applications/tari_base_node/src/builder.rs b/applications/tari_base_node/src/builder.rs index b347f2f219..2e508336fd 100644 --- a/applications/tari_base_node/src/builder.rs +++ b/applications/tari_base_node/src/builder.rs @@ -60,6 +60,7 @@ use tari_core::{ create_lmdb_database, BlockchainBackend, BlockchainDatabase, + BlockchainDatabaseConfig, LMDBDatabase, MemoryDatabase, Validators, @@ -403,7 +404,9 @@ where FullConsensusValidator::new(rules.clone(), factories.clone()), StatelessBlockValidator::new(&rules.consensus_constants()), ); - let db = BlockchainDatabase::new(backend, &rules, validators).map_err(|e| e.to_string())?; + // TODO - make BlockchainDatabaseConfig configurable + let db = BlockchainDatabase::new(backend, &rules, validators, BlockchainDatabaseConfig::default()) + .map_err(|e| e.to_string())?; let mempool_validator = MempoolValidators::new(FullTxValidator::new(factories.clone()), TxInputAndMaturityValidator {}); let mempool = Mempool::new(db.clone(), MempoolConfig::default(), mempool_validator); diff --git a/base_layer/core/src/chain_storage/blockchain_database.rs b/base_layer/core/src/chain_storage/blockchain_database.rs index 444b0ae383..a22a7264f6 100644 --- a/base_layer/core/src/chain_storage/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/blockchain_database.rs @@ -23,6 +23,7 @@ use crate::{ blocks::{blockheader::BlockHash, Block, BlockHeader, NewBlockTemplate}, chain_storage::{ + consts::BLOCKCHAIN_DATABASE_ORPHAN_STORAGE_CAPACITY, db_transaction::{DbKey, DbKeyValuePair, DbTransaction, DbValue, MetadataKey, MetadataValue, MmrTree}, error::ChainStorageError, ChainMetadata, @@ -53,6 +54,20 @@ use tari_mmr::{Hash, MerkleCheckPoint, MerkleProof, MutableMmrLeafNodes}; const LOG_TARGET: &str = "c::cs::database"; +/// Configuration for the BlockchainDatabase. +#[derive(Clone, Copy)] +pub struct BlockchainDatabaseConfig { + pub orphan_storage_capacity: usize, +} + +impl Default for BlockchainDatabaseConfig { + fn default() -> Self { + Self { + orphan_storage_capacity: BLOCKCHAIN_DATABASE_ORPHAN_STORAGE_CAPACITY, + } + } +} + #[derive(Clone, Debug, PartialEq, Display)] pub enum BlockAddResult { Ok, @@ -141,6 +156,8 @@ pub trait BlockchainBackend: Send + Sync { where Self: Sized, F: FnMut(Result<(HashOutput, Block), ChainStorageError>); + /// Returns the number of blocks in the block orphan pool. + fn get_orphan_count(&self) -> Result; /// Performs the function F for each transaction kernel. fn for_each_kernel(&self, f: F) -> Result<(), ChainStorageError> where @@ -186,7 +203,7 @@ macro_rules! fetch { /// /// ``` /// use tari_core::{ -/// chain_storage::{BlockchainDatabase, MemoryDatabase, Validators}, +/// chain_storage::{BlockchainDatabase, BlockchainDatabaseConfig, MemoryDatabase, Validators}, /// consensus::{ConsensusManagerBuilder, Network}, /// transactions::types::HashDigest, /// validation::{mocks::MockValidator, Validation}, @@ -196,7 +213,7 @@ macro_rules! fetch { /// let db = MemoryDatabase::::default(); /// let network = Network::LocalNet; /// let rules = ConsensusManagerBuilder::new(network).build(); -/// let db = BlockchainDatabase::new(db_backend, &rules, validators).unwrap(); +/// let db = BlockchainDatabase::new(db_backend, &rules, validators, BlockchainDatabaseConfig::default()).unwrap(); /// // Do stuff with db /// ``` pub struct BlockchainDatabase @@ -204,6 +221,7 @@ where T: BlockchainBackend { db: Arc>, validators: Validators, + config: BlockchainDatabaseConfig, } impl BlockchainDatabase @@ -214,11 +232,13 @@ where T: BlockchainBackend db: T, consensus_manager: &ConsensusManager, validators: Validators, + config: BlockchainDatabaseConfig, ) -> Result { let blockchain_db = BlockchainDatabase { db: Arc::new(RwLock::new(db)), validators, + config, }; if blockchain_db.get_height()?.is_none() { let genesis_block = consensus_manager.get_genesis_block(); @@ -397,7 +417,12 @@ where T: BlockchainBackend .map_err(ChainStorageError::ValidationError)?; let mut db = self.db_write_access()?; - add_block(&mut db, &self.validators.block, block) + add_block( + &mut db, + &self.validators.block, + block, + self.config.orphan_storage_capacity, + ) } fn store_new_block(&self, block: Block) -> Result<(), ChainStorageError> { @@ -565,14 +590,22 @@ fn add_block( db: &mut RwLockWriteGuard, block_validator: &Arc>, block: Block, + orphan_storage_capacity: usize, ) -> Result { let block_hash = block.hash(); if db.contains(&DbKey::BlockHash(block_hash))? { return Ok(BlockAddResult::BlockExists); } - - handle_possible_reorg(db, block_validator, block) + let block_add_result = handle_possible_reorg(db, block_validator, block)?; + // Cleanup orphan block pool + match block_add_result { + BlockAddResult::Ok => {}, + BlockAddResult::BlockExists => {}, + BlockAddResult::OrphanBlock => cleanup_orphans_single(db, orphan_storage_capacity)?, + BlockAddResult::ChainReorg(_) => cleanup_orphans_comprehensive(db, orphan_storage_capacity)?, + } + Ok(block_add_result) } // Adds a new block onto the chain tip. @@ -1150,6 +1183,73 @@ fn find_strongest_orphan_tip( Ok((best_accum_difficulty, best_tip_hash)) } +// Discards the orphan block with the minimum height from the block orphan pool to maintain the configured orphan pool +// storage limit. +fn cleanup_orphans_single( + db: &mut RwLockWriteGuard, + orphan_storage_capacity: usize, +) -> Result<(), ChainStorageError> +{ + if db.get_orphan_count()? > orphan_storage_capacity { + trace!( + target: LOG_TARGET, + "Orphan block storage limit reached, performing simple cleanup.", + ); + let mut min_height: u64 = u64::max_value(); + let mut remove_hash: Option = None; + db.for_each_orphan(|pair| { + let (_, block) = pair.unwrap(); + if block.header.height < min_height { + min_height = block.header.height; + remove_hash = Some(block.hash()); + } + }) + .expect("Unexpected result for database query"); + if let Some(hash) = remove_hash { + trace!(target: LOG_TARGET, "Discarding orphan block ({}).", hash.to_hex()); + remove_orphan(db, hash)?; + } + } + Ok(()) +} + +// Perform a comprehensive search to remove all the minimum height orphans to maintain the configured orphan pool +// storage limit. +fn cleanup_orphans_comprehensive( + db: &mut RwLockWriteGuard, + orphan_storage_capacity: usize, +) -> Result<(), ChainStorageError> +{ + let orphan_count = db.get_orphan_count()?; + if orphan_count > orphan_storage_capacity { + trace!( + target: LOG_TARGET, + "Orphan block storage limit reached, performing comprehensive cleanup.", + ); + let remove_count = orphan_count - orphan_storage_capacity; + + let mut orphans = Vec::<(u64, BlockHash)>::with_capacity(orphan_count); + db.for_each_orphan(|pair| { + let (_, block) = pair.unwrap(); + orphans.push((block.header.height, block.hash())); + }) + .expect("Unexpected result for database query"); + orphans.sort_by(|a, b| a.0.cmp(&b.0)); + + let mut txn = DbTransaction::new(); + for i in 0..remove_count { + trace!( + target: LOG_TARGET, + "Discarding orphan block ({}).", + orphans[i].1.to_hex() + ); + txn.delete(DbKey::OrphanBlock(orphans[i].1.clone())); + } + commit(db, txn)?; + } + Ok(()) +} + fn log_error(req: DbKey, err: ChainStorageError) -> Result { error!( target: LOG_TARGET, @@ -1167,6 +1267,7 @@ where T: BlockchainBackend BlockchainDatabase { db: self.db.clone(), validators: self.validators.clone(), + config: self.config.clone(), } } } diff --git a/base_layer/core/src/chain_storage/consts.rs b/base_layer/core/src/chain_storage/consts.rs new file mode 100644 index 0000000000..7eb3bfe74f --- /dev/null +++ b/base_layer/core/src/chain_storage/consts.rs @@ -0,0 +1,24 @@ +// Copyright 2020. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// The maximum number of orphans that can be stored in the Orphan block pool. +pub const BLOCKCHAIN_DATABASE_ORPHAN_STORAGE_CAPACITY: usize = 720; diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs index b23e128908..dda9eae7e8 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs @@ -656,6 +656,11 @@ where D: Digest + Send + Sync lmdb_for_each::(&self.env, &self.orphans_db, f) } + /// Returns the number of blocks in the block orphan pool. + fn get_orphan_count(&self) -> Result { + lmdb_len(&self.env, &self.orphans_db) + } + /// Iterate over all the stored transaction kernels and execute the function `f` for each kernel. fn for_each_kernel(&self, f: F) -> Result<(), ChainStorageError> where F: FnMut(Result<(HashOutput, TransactionKernel), ChainStorageError>) { diff --git a/base_layer/core/src/chain_storage/memory_db/memory_db.rs b/base_layer/core/src/chain_storage/memory_db/memory_db.rs index cb1d20790d..0099362e75 100644 --- a/base_layer/core/src/chain_storage/memory_db/memory_db.rs +++ b/base_layer/core/src/chain_storage/memory_db/memory_db.rs @@ -473,6 +473,12 @@ where D: Digest + Send + Sync Ok(()) } + /// Returns the number of blocks in the block orphan pool. + fn get_orphan_count(&self) -> Result { + let db = self.db_access()?; + Ok(db.orphans.len()) + } + /// Iterate over all the stored transaction kernels and execute the function `f` for each kernel. fn for_each_kernel(&self, mut f: F) -> Result<(), ChainStorageError> where F: FnMut(Result<(HashOutput, TransactionKernel), ChainStorageError>) { diff --git a/base_layer/core/src/chain_storage/mod.rs b/base_layer/core/src/chain_storage/mod.rs index ae7e99a9c2..797159a90e 100644 --- a/base_layer/core/src/chain_storage/mod.rs +++ b/base_layer/core/src/chain_storage/mod.rs @@ -27,6 +27,7 @@ //! backed by LMDB, while the merkle trees are stored in flat files for example. mod blockchain_database; +mod consts; mod db_transaction; mod error; mod historical_block; @@ -46,6 +47,7 @@ pub use blockchain_database::{ BlockAddResult, BlockchainBackend, BlockchainDatabase, + BlockchainDatabaseConfig, MutableMmrState, Validators, }; diff --git a/base_layer/core/src/helpers/mock_backend.rs b/base_layer/core/src/helpers/mock_backend.rs index 5409d8827b..f5dbd5ad2a 100644 --- a/base_layer/core/src/helpers/mock_backend.rs +++ b/base_layer/core/src/helpers/mock_backend.rs @@ -85,6 +85,10 @@ impl BlockchainBackend for MockBackend { unimplemented!() } + fn get_orphan_count(&self) -> Result { + unimplemented!() + } + fn for_each_kernel(&self, _f: F) -> Result<(), ChainStorageError> where Self: Sized, diff --git a/base_layer/core/src/helpers/mod.rs b/base_layer/core/src/helpers/mod.rs index 449cc26756..b924426c15 100644 --- a/base_layer/core/src/helpers/mod.rs +++ b/base_layer/core/src/helpers/mod.rs @@ -27,7 +27,7 @@ mod mock_backend; use crate::{ blocks::{Block, BlockHeader}, - chain_storage::{BlockchainDatabase, MemoryDatabase, Validators}, + chain_storage::{BlockchainDatabase, BlockchainDatabaseConfig, MemoryDatabase, Validators}, consensus::{ConsensusConstants, ConsensusManager}, transactions::{transaction::Transaction, types::HashDigest}, validation::mocks::MockValidator, @@ -51,5 +51,5 @@ pub fn create_orphan_block( pub fn create_mem_db(consensus_manager: &ConsensusManager) -> BlockchainDatabase> { let validators = Validators::new(MockValidator::new(true), MockValidator::new(true)); let db = MemoryDatabase::::default(); - BlockchainDatabase::new(db, consensus_manager, validators).unwrap() + BlockchainDatabase::new(db, consensus_manager, validators, BlockchainDatabaseConfig::default()).unwrap() } diff --git a/base_layer/core/src/mempool/consts.rs b/base_layer/core/src/mempool/consts.rs index ea8c2b7125..ec6e2cacca 100644 --- a/base_layer/core/src/mempool/consts.rs +++ b/base_layer/core/src/mempool/consts.rs @@ -19,7 +19,6 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// use std::time::Duration; diff --git a/base_layer/core/tests/block_validation.rs b/base_layer/core/tests/block_validation.rs index dc57c3fc62..ff07813b90 100644 --- a/base_layer/core/tests/block_validation.rs +++ b/base_layer/core/tests/block_validation.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use tari_core::{ - chain_storage::{BlockchainDatabase, MemoryDatabase, Validators}, + chain_storage::{BlockchainDatabase, BlockchainDatabaseConfig, MemoryDatabase, Validators}, consensus::{ConsensusManagerBuilder, Network}, proof_of_work::DiffAdjManager, transactions::types::{CryptoFactories, HashDigest}, @@ -38,7 +38,7 @@ fn test_genesis_block() { FullConsensusValidator::new(rules.clone(), factories), StatelessBlockValidator::new(&rules.consensus_constants()), ); - let db = BlockchainDatabase::new(backend, &rules, validators).unwrap(); + let db = BlockchainDatabase::new(backend, &rules, validators, BlockchainDatabaseConfig::default()).unwrap(); let diff_adj_manager = DiffAdjManager::new(&rules.consensus_constants()).unwrap(); rules.set_diff_manager(diff_adj_manager).unwrap(); let block = rules.get_genesis_block(); diff --git a/base_layer/core/tests/chain_storage_tests/chain_storage.rs b/base_layer/core/tests/chain_storage_tests/chain_storage.rs index 07427a44e6..b74a544c93 100644 --- a/base_layer/core/tests/chain_storage_tests/chain_storage.rs +++ b/base_layer/core/tests/chain_storage_tests/chain_storage.rs @@ -38,7 +38,9 @@ use tari_core::{ chain_storage::{ create_lmdb_database, BlockAddResult, + BlockchainBackend, BlockchainDatabase, + BlockchainDatabaseConfig, ChainStorageError, DbKey, DbTransaction, @@ -757,7 +759,7 @@ fn store_and_retrieve_blocks() { let network = Network::LocalNet; let rules = ConsensusManagerBuilder::new(network).build(); let db = MemoryDatabase::::new(mmr_cache_config); - let store = BlockchainDatabase::new(db, &rules, validators).unwrap(); + let store = BlockchainDatabase::new(db, &rules, validators, BlockchainDatabaseConfig::default()).unwrap(); let block0 = store.fetch_block(0).unwrap().block().clone(); let block1 = append_block(&store, &block0, vec![], &rules.consensus_constants(), 1.into()).unwrap(); @@ -780,7 +782,7 @@ fn store_and_retrieve_chain_and_orphan_blocks_with_hashes() { let network = Network::LocalNet; let rules = ConsensusManagerBuilder::new(network).build(); let db = MemoryDatabase::::new(mmr_cache_config); - let store = BlockchainDatabase::new(db, &rules, validators).unwrap(); + let store = BlockchainDatabase::new(db, &rules, validators, BlockchainDatabaseConfig::default()).unwrap(); let block0 = store.fetch_block(0).unwrap().block().clone(); let block1 = append_block(&store, &block0, vec![], &rules.consensus_constants(), 1.into()).unwrap(); @@ -880,7 +882,8 @@ fn restore_metadata() { let block_hash: BlockHash; { let db = create_lmdb_database(&path, MmrCacheConfig::default()).unwrap(); - let db = BlockchainDatabase::new(db, &rules, validators.clone()).unwrap(); + let db = + BlockchainDatabase::new(db, &rules, validators.clone(), BlockchainDatabaseConfig::default()).unwrap(); let block0 = db.fetch_block(0).unwrap().block().clone(); let block1 = append_block(&db, &block0, vec![], &rules.consensus_constants(), 1.into()).unwrap(); @@ -892,7 +895,7 @@ fn restore_metadata() { } // Restore blockchain db let db = create_lmdb_database(&path, MmrCacheConfig::default()).unwrap(); - let db = BlockchainDatabase::new(db, &rules, validators).unwrap(); + let db = BlockchainDatabase::new(db, &rules, validators, BlockchainDatabaseConfig::default()).unwrap(); let metadata = db.get_metadata().unwrap(); assert_eq!(metadata.height_of_longest_chain, Some(1)); @@ -924,7 +927,8 @@ fn invalid_block() { StatelessBlockValidator::new(&consensus_manager.consensus_constants()), ); let db = create_lmdb_database(&temp_path, MmrCacheConfig::default()).unwrap(); - let mut store = BlockchainDatabase::new(db, &consensus_manager, validators).unwrap(); + let mut store = + BlockchainDatabase::new(db, &consensus_manager, validators, BlockchainDatabaseConfig::default()).unwrap(); let mut blocks = vec![block0]; let mut outputs = vec![vec![output]]; let block0_hash = blocks[0].hash(); @@ -1033,3 +1037,170 @@ fn invalid_block() { std::fs::remove_dir_all(&temp_path).unwrap(); } } + +#[test] +fn orphan_cleanup_on_block_add() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let validators = Validators::new(MockValidator::new(true), MockValidator::new(true)); + let db = MemoryDatabase::::default(); + let config = BlockchainDatabaseConfig { + orphan_storage_capacity: 3, + }; + let store = BlockchainDatabase::new(db, &consensus_manager, validators, config).unwrap(); + + let orphan1 = create_orphan_block(500, vec![], &consensus_manager.consensus_constants()); + let orphan2 = create_orphan_block(5, vec![], &consensus_manager.consensus_constants()); + let orphan3 = create_orphan_block(30, vec![], &consensus_manager.consensus_constants()); + let orphan4 = create_orphan_block(700, vec![], &consensus_manager.consensus_constants()); + let orphan5 = create_orphan_block(43, vec![], &consensus_manager.consensus_constants()); + let orphan6 = create_orphan_block(75, vec![], &consensus_manager.consensus_constants()); + let orphan7 = create_orphan_block(150, vec![], &consensus_manager.consensus_constants()); + let orphan1_hash = orphan1.hash(); + let orphan2_hash = orphan2.hash(); + let orphan3_hash = orphan3.hash(); + let orphan4_hash = orphan4.hash(); + let orphan5_hash = orphan5.hash(); + let orphan6_hash = orphan6.hash(); + let orphan7_hash = orphan7.hash(); + assert_eq!(store.add_block(orphan1.clone()), Ok(BlockAddResult::OrphanBlock)); + assert_eq!(store.add_block(orphan2), Ok(BlockAddResult::OrphanBlock)); + assert_eq!(store.add_block(orphan3), Ok(BlockAddResult::OrphanBlock)); + assert_eq!(store.add_block(orphan4.clone()), Ok(BlockAddResult::OrphanBlock)); + assert_eq!(store.add_block(orphan5), Ok(BlockAddResult::OrphanBlock)); + assert_eq!(store.add_block(orphan6), Ok(BlockAddResult::OrphanBlock)); + assert_eq!(store.add_block(orphan7.clone()), Ok(BlockAddResult::OrphanBlock)); + + assert_eq!(store.db_read_access().unwrap().get_orphan_count(), Ok(3)); + assert_eq!(store.fetch_orphan(orphan1_hash), Ok(orphan1)); + assert!(store.fetch_orphan(orphan2_hash).is_err()); + assert!(store.fetch_orphan(orphan3_hash).is_err()); + assert_eq!(store.fetch_orphan(orphan4_hash), Ok(orphan4)); + assert!(store.fetch_orphan(orphan5_hash).is_err()); + assert!(store.fetch_orphan(orphan6_hash).is_err()); + assert_eq!(store.fetch_orphan(orphan7_hash), Ok(orphan7)); +} + +#[test] +fn orphan_cleanup_on_reorg() { + // Create Main Chain + let network = Network::LocalNet; + let factories = CryptoFactories::default(); + let consensus_constants = ConsensusConstantsBuilder::new(network).build(); + let (block0, output) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let validators = Validators::new(MockValidator::new(true), MockValidator::new(true)); + let db = MemoryDatabase::::default(); + let config = BlockchainDatabaseConfig { + orphan_storage_capacity: 3, + }; + let mut store = BlockchainDatabase::new(db, &consensus_manager, validators, config).unwrap(); + let mut blocks = vec![block0]; + let mut outputs = vec![vec![output]]; + + // Block A1 + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + vec![], + Difficulty::from(2), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block A2 + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + vec![], + Difficulty::from(3), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block A3 + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + vec![], + Difficulty::from(3), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block A4 + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + vec![], + Difficulty::from(3), + &consensus_manager.consensus_constants() + ) + .is_ok()); + + // Create Forked Chain + let consensus_manager_fork = ConsensusManagerBuilder::new(network) + .with_block(blocks[0].clone()) + .build(); + let mut orphan_store = create_mem_db(&consensus_manager_fork); + let mut orphan_blocks = vec![blocks[0].clone()]; + let mut orphan_outputs = vec![outputs[0].clone()]; + // Block B1 + assert!(generate_new_block_with_achieved_difficulty( + &mut orphan_store, + &mut orphan_blocks, + &mut orphan_outputs, + vec![], + Difficulty::from(2), + &consensus_manager_fork.consensus_constants() + ) + .is_ok()); + // Block B2 + assert!(generate_new_block_with_achieved_difficulty( + &mut orphan_store, + &mut orphan_blocks, + &mut orphan_outputs, + vec![], + Difficulty::from(10), + &consensus_manager_fork.consensus_constants() + ) + .is_ok()); + // Block B3 + assert!(generate_new_block_with_achieved_difficulty( + &mut orphan_store, + &mut orphan_blocks, + &mut orphan_outputs, + vec![], + Difficulty::from(15), + &consensus_manager_fork.consensus_constants() + ) + .is_ok()); + + // Fill orphan block pool + let orphan1 = create_orphan_block(1, vec![], &consensus_manager.consensus_constants()); + let orphan2 = create_orphan_block(1, vec![], &consensus_manager.consensus_constants()); + assert_eq!(store.add_block(orphan1.clone()), Ok(BlockAddResult::OrphanBlock)); + assert_eq!(store.add_block(orphan2.clone()), Ok(BlockAddResult::OrphanBlock)); + + // Adding B1 and B2 to the main chain will produce a reorg from GB->A1->A2->A3->A4 to GB->B1->B2->B3. + assert_eq!( + store.add_block(orphan_blocks[1].clone()), + Ok(BlockAddResult::OrphanBlock) + ); + if let Ok(BlockAddResult::ChainReorg(_)) = store.add_block(orphan_blocks[2].clone()) { + assert!(true); + } else { + assert!(false); + } + + // Check that A2, A3 and A4 is in the orphan block pool, A1 and the other orphans were discarded by the orphan + // cleanup. + assert_eq!(store.db_read_access().unwrap().get_orphan_count(), Ok(3)); + assert_eq!(store.fetch_orphan(blocks[2].hash()), Ok(blocks[2].clone())); + assert_eq!(store.fetch_orphan(blocks[3].hash()), Ok(blocks[3].clone())); + assert_eq!(store.fetch_orphan(blocks[4].hash()), Ok(blocks[4].clone())); +} diff --git a/base_layer/core/tests/helpers/nodes.rs b/base_layer/core/tests/helpers/nodes.rs index 5706e83f5c..1206b1910a 100644 --- a/base_layer/core/tests/helpers/nodes.rs +++ b/base_layer/core/tests/helpers/nodes.rs @@ -37,7 +37,7 @@ use tari_core::{ OutboundNodeCommsInterface, }, blocks::Block, - chain_storage::{BlockchainDatabase, MemoryDatabase, Validators}, + chain_storage::{BlockchainDatabase, BlockchainDatabaseConfig, MemoryDatabase, Validators}, consensus::{ConsensusManager, ConsensusManagerBuilder, Network}, mempool::{ Mempool, @@ -184,7 +184,8 @@ impl BaseNodeBuilder { .consensus_manager .unwrap_or(ConsensusManagerBuilder::new(self.network).build()); let db = MemoryDatabase::::new(mmr_cache_config); - let blockchain_db = BlockchainDatabase::new(db, &consensus_manager, validators).unwrap(); + let blockchain_db = + BlockchainDatabase::new(db, &consensus_manager, validators, BlockchainDatabaseConfig::default()).unwrap(); let mempool_validator = MempoolValidators::new(TxInputAndMaturityValidator {}, TxInputAndMaturityValidator {}); let mempool = Mempool::new( blockchain_db.clone(),