diff --git a/applications/minotari_app_grpc/proto/block.proto b/applications/minotari_app_grpc/proto/block.proto index afb6d7c855..e5968c0965 100644 --- a/applications/minotari_app_grpc/proto/block.proto +++ b/applications/minotari_app_grpc/proto/block.proto @@ -134,5 +134,8 @@ message NewBlockTemplate { // This flag indicates if the inputs, outputs and kernels have been sorted internally, that is, the sort() method // has been called. This may be false even if all components are sorted. AggregateBody body = 2; + // Sometimes the mempool has not synced to the latest tip, this flag indicates if the mempool is out of sync. + // In most cases the next call to get_new_block_template will return a block with the mempool in sync. + bool is_mempool_in_sync = 3; } diff --git a/applications/minotari_app_grpc/src/conversions/new_block_template.rs b/applications/minotari_app_grpc/src/conversions/new_block_template.rs index 1af353def3..ac6994160c 100644 --- a/applications/minotari_app_grpc/src/conversions/new_block_template.rs +++ b/applications/minotari_app_grpc/src/conversions/new_block_template.rs @@ -68,6 +68,7 @@ impl TryFrom for grpc::NewBlockTemplate { .collect(), }), header: Some(header), + is_mempool_in_sync: block.is_mempool_in_sync, }) } } @@ -105,6 +106,7 @@ impl TryFrom for NewBlockTemplate { target_difficulty: Default::default(), reward: Default::default(), total_fees: Default::default(), + is_mempool_in_sync: block.is_mempool_in_sync, }) } } diff --git a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs index 3976094bb0..2e0f51ba49 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs @@ -250,7 +250,15 @@ impl BlockTemplateProtocol<'_> { let (block_template_with_coinbase, height) = match block_templates.get_new_template(best_block_hash).await { None => { let new_template = match self.get_new_block_template().await { - Ok(val) => val, + Ok(val) => { + if !val.template.is_mempool_in_sync { + error!(target: LOG_TARGET, "Mempool is not in sync. Retrying to get new block template (try {})", loop_count); + return Err(MmProxyError::FailedToGetBlockTemplate( + "mempool not in sync".to_string(), + )); + } + val + }, Err(err) => { error!(target: LOG_TARGET, "grpc get_new_block_template ({})", err.to_string()); return Err(err); diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index 9b8c68f569..f4c5f8d539 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -258,6 +258,7 @@ where B: BlockchainBackend + 'static NodeCommsRequest::GetNewBlockTemplate(request) => { let best_block_header = self.blockchain_db.fetch_tip_header().await?; let last_seen_hash = self.mempool.get_last_seen_hash().await?; + let mut is_mempool_synced = true; if last_seen_hash != FixedHash::default() && best_block_header.hash() != &last_seen_hash { warn!( target: LOG_TARGET, @@ -265,11 +266,7 @@ where B: BlockchainBackend + 'static should auto correct with the next block template request", last_seen_hash, best_block_header.hash() ); - return Err(CommsInterfaceError::InternalError( - "Mempool out of sync, blockchain db advanced passed current tip in mempool storage; this \ - should auto correct with the next block template request" - .to_string(), - )); + is_mempool_synced = false; } let mut header = BlockHeader::from_previous(best_block_header.header()); let constants = self.consensus_manager.consensus_constants(header.height); @@ -313,6 +310,7 @@ where B: BlockchainBackend + 'static self.get_target_difficulty_for_next_block(request.algo, constants, prev_hash) .await?, self.consensus_manager.get_block_reward_at(height), + is_mempool_synced, )?; debug!(target: LOG_TARGET, diff --git a/base_layer/core/src/blocks/new_block_template.rs b/base_layer/core/src/blocks/new_block_template.rs index 370587117e..c0183ef88a 100644 --- a/base_layer/core/src/blocks/new_block_template.rs +++ b/base_layer/core/src/blocks/new_block_template.rs @@ -51,6 +51,9 @@ pub struct NewBlockTemplate { pub reward: MicroMinotari, /// The total fees is the sum of all the fees in the block. pub total_fees: MicroMinotari, + /// Sometimes the mempool has not synced to the latest tip, this flag indicates if the mempool is out of sync. + /// In most cases the next call to get_new_block_template will return a block with the mempool in sync. + pub is_mempool_in_sync: bool, } impl NewBlockTemplate { @@ -58,6 +61,7 @@ impl NewBlockTemplate { block: Block, target_difficulty: Difficulty, reward: MicroMinotari, + is_mempool_in_sync: bool, ) -> Result { let Block { header, body } = block; let total_fees = body.get_total_fee()?; @@ -67,6 +71,7 @@ impl NewBlockTemplate { target_difficulty, reward, total_fees, + is_mempool_in_sync, }) } } diff --git a/base_layer/core/src/chain_storage/tests/blockchain_database.rs b/base_layer/core/src/chain_storage/tests/blockchain_database.rs index 26ce2024c7..aa2ae0cc0d 100644 --- a/base_layer/core/src/chain_storage/tests/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/tests/blockchain_database.rs @@ -441,7 +441,8 @@ mod prepare_new_block { fn it_errors_for_genesis_block() { let db = setup(); let genesis = db.fetch_block(0, true).unwrap(); - let template = NewBlockTemplate::from_block(genesis.block().clone(), Difficulty::min(), 5000 * T).unwrap(); + let template = + NewBlockTemplate::from_block(genesis.block().clone(), Difficulty::min(), 5000 * T, true).unwrap(); let err = db.prepare_new_block(template).unwrap_err(); assert!(matches!(err, ChainStorageError::InvalidArguments { .. })); } @@ -452,7 +453,7 @@ mod prepare_new_block { let genesis = db.fetch_block(0, true).unwrap(); let next_block = BlockHeader::from_previous(genesis.header()); let mut template = - NewBlockTemplate::from_block(next_block.into_builder().build(), Difficulty::min(), 5000 * T).unwrap(); + NewBlockTemplate::from_block(next_block.into_builder().build(), Difficulty::min(), 5000 * T, true).unwrap(); // This would cause a panic if the sanity checks were not there template.header.height = 100; let err = db.prepare_new_block(template.clone()).unwrap_err(); @@ -468,7 +469,7 @@ mod prepare_new_block { let genesis = db.fetch_block(0, true).unwrap(); let next_block = BlockHeader::from_previous(genesis.header()); let template = - NewBlockTemplate::from_block(next_block.into_builder().build(), Difficulty::min(), 5000 * T).unwrap(); + NewBlockTemplate::from_block(next_block.into_builder().build(), Difficulty::min(), 5000 * T, true).unwrap(); let block = db.prepare_new_block(template).unwrap(); assert_eq!(block.header.height, 1); } diff --git a/base_layer/core/tests/helpers/block_builders.rs b/base_layer/core/tests/helpers/block_builders.rs index 6827a9048f..d098ca01c8 100644 --- a/base_layer/core/tests/helpers/block_builders.rs +++ b/base_layer/core/tests/helpers/block_builders.rs @@ -149,6 +149,7 @@ async fn genesis_template( header.into_builder().with_coinbase_utxo(utxo, kernel).build(), Difficulty::min(), coinbase_value, + true, ) .unwrap(); (block, output) @@ -315,6 +316,7 @@ pub async fn chain_block( .build(), Difficulty::min(), reward, + true, ) .unwrap() } @@ -339,6 +341,7 @@ pub fn chain_block_with_coinbase( .build(), achieved_difficulty.unwrap_or(Difficulty::min()), consensus.get_block_reward_at(height), + true, ) .unwrap() } @@ -377,6 +380,7 @@ pub async fn chain_block_with_new_coinbase( .build(), Difficulty::min(), reward, + true, ) .unwrap(); (template, coinbase_output) diff --git a/base_layer/core/tests/helpers/database.rs b/base_layer/core/tests/helpers/database.rs index 1ec525373c..32959db85f 100644 --- a/base_layer/core/tests/helpers/database.rs +++ b/base_layer/core/tests/helpers/database.rs @@ -59,6 +59,7 @@ pub async fn create_orphan_block( .build(), Difficulty::min(), coinbase_value, + true, ) .unwrap(); Block::new(template.header.into(), template.body) diff --git a/base_layer/core/tests/tests/block_validation.rs b/base_layer/core/tests/tests/block_validation.rs index aa7246fa22..57211c157d 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/base_layer/core/tests/tests/block_validation.rs @@ -1351,6 +1351,7 @@ async fn test_fee_overflow() { .build(), Difficulty::min(), consensus_manager.get_block_reward_at(height), + true, ); assert!(template_result.is_err()); assert_eq!( diff --git a/base_layer/tari_mining_helper_ffi/src/lib.rs b/base_layer/tari_mining_helper_ffi/src/lib.rs index 94e6e8f2c7..4b84960ed3 100644 --- a/base_layer/tari_mining_helper_ffi/src/lib.rs +++ b/base_layer/tari_mining_helper_ffi/src/lib.rs @@ -804,7 +804,7 @@ mod tests { let error_ptr = &mut error as *mut c_int; let header = BlockHeader::new(0); let block = - NewBlockTemplate::from_block(header.into_builder().build(), Difficulty::min(), 0.into()).unwrap(); + NewBlockTemplate::from_block(header.into_builder().build(), Difficulty::min(), 0.into(), true).unwrap(); let block_bytes = borsh::to_vec(&block).unwrap(); #[allow(clippy::cast_possible_truncation)]