From a96aa26fed003ec37e3aa99ebf29d78d50eb3c4e Mon Sep 17 00:00:00 2001 From: redsh4de <25299353+redsh4de@users.noreply.github.com> Date: Sat, 2 May 2026 19:21:32 +0300 Subject: [PATCH 1/3] check alt blocks when handling missing tx request in DB --- storage/blockchain/src/ops/alt_block/block.rs | 18 +++++- storage/blockchain/src/ops/alt_block/mod.rs | 2 +- storage/blockchain/src/service/read.rs | 56 ++++++++++++++----- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/storage/blockchain/src/ops/alt_block/block.rs b/storage/blockchain/src/ops/alt_block/block.rs index a6ad1da36..d74cf2617 100644 --- a/storage/blockchain/src/ops/alt_block/block.rs +++ b/storage/blockchain/src/ops/alt_block/block.rs @@ -94,7 +94,7 @@ pub fn add_alt_block( /// /// This function will look at only the blocks with the given [`AltBlockHeight::chain_id`], no others /// even if they are technically part of this chain. -pub fn get_alt_block( +pub fn get_alt_block_information( db: &BlockchainDatabase, alt_block_height: &AltBlockHeight, tx_ro: &fjall::Snapshot, @@ -134,6 +134,22 @@ pub fn get_alt_block( }) } +/// Retrieve an alt [`Block`] via its [`AltBlockHeight`]. +/// +/// This function will look at only the blocks with the given [`AltBlockHeight::chain_id`], no others +/// even if they are technically part of this chain. +pub fn get_alt_block( + db: &BlockchainDatabase, + alt_block_height: &AltBlockHeight, + tx_ro: &fjall::Snapshot, +) -> DbResult { + let block_blob = tx_ro + .get(&db.alt_block_blobs, bytemuck::bytes_of(alt_block_height))? + .ok_or(BlockchainError::NotFound)?; + + Ok(Block::read(&mut block_blob.as_ref()).unwrap()) +} + /// Retrieves the hash of the block at the given `block_height` on the alt chain with /// the given [`ChainId`]. /// diff --git a/storage/blockchain/src/ops/alt_block/mod.rs b/storage/blockchain/src/ops/alt_block/mod.rs index 4d57f29ad..96c764045 100644 --- a/storage/blockchain/src/ops/alt_block/mod.rs +++ b/storage/blockchain/src/ops/alt_block/mod.rs @@ -53,7 +53,7 @@ mod tx; pub use block::{ add_alt_block, flush_alt_blocks, get_alt_block, get_alt_block_extended_header_from_height, - get_alt_block_hash, + get_alt_block_hash, get_alt_block_information, }; pub use chain::{get_alt_chain_history_ranges, update_alt_chain_info}; pub use tx::{add_alt_transaction_blob, get_alt_transaction}; diff --git a/storage/blockchain/src/service/read.rs b/storage/blockchain/src/service/read.rs index 621b00d5a..915bba856 100644 --- a/storage/blockchain/src/service/read.rs +++ b/storage/blockchain/src/service/read.rs @@ -45,7 +45,7 @@ use crate::{ ops::{ alt_block::{ get_alt_block, get_alt_block_extended_header_from_height, get_alt_block_hash, - get_alt_chain_history_ranges, + get_alt_block_information, get_alt_chain_history_ranges, }, block::{ block_exists, block_height, get_block, get_block_by_hash, get_block_complete_entry, @@ -710,21 +710,37 @@ fn txs_in_block( let tx_ro = db.fjall.snapshot(); let tapes = db.linear_tapes.reader(); - let block_height = usize::from_le_bytes( - tx_ro - .get(&db.block_heights, block_hash)? - .ok_or(BlockchainError::NotFound)? - .as_ref() - .try_into() - .unwrap(), - ); + // Check the main chain first. + if let Some(block_height) = block_height(db, &tx_ro, &block_hash)? { + let block_info = tapes + .read_entry(&db.block_infos, usize_to_u64(block_height))? + .ok_or(BlockchainError::NotFound)?; + + let block = get_block(&block_height, Some(&block_info), &tapes, db)?; + let first_tx_index = block_info.mining_tx_index + 1; + + if block.transactions.len() < missing_txs.len() { + return Ok(BlockchainResponse::TxsInBlock(None)); + } + + let txs = missing_txs + .into_iter() + .map(|index_offset| get_tx_blob_from_id(&(first_tx_index + index_offset), &tapes, db)) + .collect::>()?; + + return Ok(BlockchainResponse::TxsInBlock(Some(TxsInBlock { + block: block.serialize(), + txs, + }))); + } - let block_info = tapes - .read_entry(&db.block_infos, usize_to_u64(block_height))? + // Fall back to alt blocks. + let alt_block_height = tx_ro + .get(&db.alt_block_heights, block_hash)? .ok_or(BlockchainError::NotFound)?; + let alt_block_height: AltBlockHeight = bytemuck::pod_read_unaligned(alt_block_height.as_ref()); - let block = get_block(&block_height, None, &tapes, db)?; - let first_tx_index = block_info.mining_tx_index + 1; + let block = get_alt_block(db, &alt_block_height, &tx_ro)?; if block.transactions.len() < missing_txs.len() { return Ok(BlockchainResponse::TxsInBlock(None)); @@ -732,7 +748,17 @@ fn txs_in_block( let txs = missing_txs .into_iter() - .map(|index_offset| get_tx_blob_from_id(&(first_tx_index + index_offset), &tapes, db)) + .map(|index_offset| { + let tx_hash = block + .transactions + .get(u64_to_usize(index_offset)) + .ok_or(BlockchainError::NotFound)?; + + tx_ro + .get(&db.alt_transaction_blobs, tx_hash)? + .map(|blob| blob.to_vec()) + .ok_or(BlockchainError::NotFound) + }) .collect::>()?; Ok(BlockchainResponse::TxsInBlock(Some(TxsInBlock { @@ -759,7 +785,7 @@ fn alt_blocks_in_chain(db: &BlockchainDatabase, chain_id: ChainId) -> ResponseRe }; range.clone().map(|height| { - get_alt_block( + get_alt_block_information( db, &AltBlockHeight { chain_id: (*chain_id).into(), From 06cd26ba86d66b50986f64b1ffe7dd822c286a8c Mon Sep 17 00:00:00 2001 From: redsh4de <25299353+redsh4de@users.noreply.github.com> Date: Thu, 7 May 2026 12:28:36 +0300 Subject: [PATCH 2/3] add and use alt_block_height helper --- storage/blockchain/src/ops/alt_block/block.rs | 13 +++ storage/blockchain/src/ops/alt_block/mod.rs | 1 + storage/blockchain/src/service/read.rs | 83 ++++++++----------- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/storage/blockchain/src/ops/alt_block/block.rs b/storage/blockchain/src/ops/alt_block/block.rs index d74cf2617..3e45a4b68 100644 --- a/storage/blockchain/src/ops/alt_block/block.rs +++ b/storage/blockchain/src/ops/alt_block/block.rs @@ -243,3 +243,16 @@ pub fn get_alt_block_extended_header_from_height( long_term_weight: block_info.long_term_weight, }) } + +/// Returns the [`AltBlockHeight`] of a block from its hash, only if it is in an alt chain. +pub(crate) fn alt_block_height( + db: &BlockchainDatabase, + tx_ro: &fjall::Snapshot, + hash: &BlockHash, +) -> DbResult> { + let Some(bytes) = tx_ro.get(&db.alt_block_heights, hash)? else { + return Ok(None); + }; + + Ok(Some(bytemuck::pod_read_unaligned(bytes.as_ref()))) +} diff --git a/storage/blockchain/src/ops/alt_block/mod.rs b/storage/blockchain/src/ops/alt_block/mod.rs index 96c764045..cc8339209 100644 --- a/storage/blockchain/src/ops/alt_block/mod.rs +++ b/storage/blockchain/src/ops/alt_block/mod.rs @@ -51,6 +51,7 @@ mod block; mod chain; mod tx; +pub(crate) use block::alt_block_height; pub use block::{ add_alt_block, flush_alt_blocks, get_alt_block, get_alt_block_extended_header_from_height, get_alt_block_hash, get_alt_block_information, diff --git a/storage/blockchain/src/service/read.rs b/storage/blockchain/src/service/read.rs index 915bba856..ce69fcf94 100644 --- a/storage/blockchain/src/service/read.rs +++ b/storage/blockchain/src/service/read.rs @@ -44,8 +44,8 @@ use crate::{ error::{BlockchainError, DbResult}, ops::{ alt_block::{ - get_alt_block, get_alt_block_extended_header_from_height, get_alt_block_hash, - get_alt_block_information, get_alt_chain_history_ranges, + alt_block_height, get_alt_block, get_alt_block_extended_header_from_height, + get_alt_block_hash, get_alt_block_information, get_alt_chain_history_ranges, }, block::{ block_exists, block_height, get_block, get_block_by_hash, get_block_complete_entry, @@ -290,22 +290,15 @@ fn block_hash_in_range( fn find_block(db: &BlockchainDatabase, block_hash: BlockHash) -> ResponseResult { let tx_ro = db.fjall.snapshot(); - // Check the main chain first. - if let Some(height) = block_height(db, &tx_ro, &block_hash)? { - return Ok(BlockchainResponse::FindBlock(Some((Chain::Main, height)))); - } - - match tx_ro.get(&db.alt_block_heights, block_hash)? { - Some(height) => { - let height: AltBlockHeight = bytemuck::pod_read_unaligned(height.as_ref()); + // Check the main chain first, then alt chains. + let location = if let Some(height) = block_height(db, &tx_ro, &block_hash)? { + Some((Chain::Main, height)) + } else { + alt_block_height(db, &tx_ro, &block_hash)? + .map(|alt| (Chain::Alt(alt.chain_id.into()), alt.height)) + }; - Ok(BlockchainResponse::FindBlock(Some(( - Chain::Alt(height.chain_id.into()), - height.height, - )))) - } - None => Ok(BlockchainResponse::FindBlock(None)), - } + Ok(BlockchainResponse::FindBlock(location)) } /// [`BlockchainReadRequest::FilterUnknownHashes`]. @@ -710,8 +703,8 @@ fn txs_in_block( let tx_ro = db.fjall.snapshot(); let tapes = db.linear_tapes.reader(); - // Check the main chain first. - if let Some(block_height) = block_height(db, &tx_ro, &block_hash)? { + // Check the main chain first, fall back to alt blocks if not found. + let (block, txs) = if let Some(block_height) = block_height(db, &tx_ro, &block_hash)? { let block_info = tapes .read_entry(&db.block_infos, usize_to_u64(block_height))? .ok_or(BlockchainError::NotFound)?; @@ -728,38 +721,34 @@ fn txs_in_block( .map(|index_offset| get_tx_blob_from_id(&(first_tx_index + index_offset), &tapes, db)) .collect::>()?; - return Ok(BlockchainResponse::TxsInBlock(Some(TxsInBlock { - block: block.serialize(), - txs, - }))); - } - - // Fall back to alt blocks. - let alt_block_height = tx_ro - .get(&db.alt_block_heights, block_hash)? - .ok_or(BlockchainError::NotFound)?; - let alt_block_height: AltBlockHeight = bytemuck::pod_read_unaligned(alt_block_height.as_ref()); + (block, txs) + } else { + let alt_block_height = + alt_block_height(db, &tx_ro, &block_hash)?.ok_or(BlockchainError::NotFound)?; - let block = get_alt_block(db, &alt_block_height, &tx_ro)?; + let block = get_alt_block(db, &alt_block_height, &tx_ro)?; - if block.transactions.len() < missing_txs.len() { - return Ok(BlockchainResponse::TxsInBlock(None)); - } + if block.transactions.len() < missing_txs.len() { + return Ok(BlockchainResponse::TxsInBlock(None)); + } - let txs = missing_txs - .into_iter() - .map(|index_offset| { - let tx_hash = block - .transactions - .get(u64_to_usize(index_offset)) - .ok_or(BlockchainError::NotFound)?; + let txs = missing_txs + .into_iter() + .map(|index_offset| { + let tx_hash = block + .transactions + .get(u64_to_usize(index_offset)) + .ok_or(BlockchainError::NotFound)?; + + tx_ro + .get(&db.alt_transaction_blobs, tx_hash)? + .map(|blob| blob.to_vec()) + .ok_or(BlockchainError::NotFound) + }) + .collect::>()?; - tx_ro - .get(&db.alt_transaction_blobs, tx_hash)? - .map(|blob| blob.to_vec()) - .ok_or(BlockchainError::NotFound) - }) - .collect::>()?; + (block, txs) + }; Ok(BlockchainResponse::TxsInBlock(Some(TxsInBlock { block: block.serialize(), From 327ce1a745dc417ff1070526ec4e3751cdddf65b Mon Sep 17 00:00:00 2001 From: redsh4de <25299353+redsh4de@users.noreply.github.com> Date: Thu, 7 May 2026 13:45:38 +0300 Subject: [PATCH 3/3] consistent usage of BlockHash --- storage/blockchain/src/ops/block.rs | 2 +- storage/blockchain/src/service/read.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/blockchain/src/ops/block.rs b/storage/blockchain/src/ops/block.rs index 15c27e96b..709524093 100644 --- a/storage/blockchain/src/ops/block.rs +++ b/storage/blockchain/src/ops/block.rs @@ -779,7 +779,7 @@ pub fn block_exists( pub(crate) fn block_height( db: &BlockchainDatabase, tx_ro: &fjall::Snapshot, - hash: &[u8; 32], + hash: &BlockHash, ) -> DbResult> { let Some(block_height) = tx_ro.get(&db.block_heights, hash)? else { return Ok(None); diff --git a/storage/blockchain/src/service/read.rs b/storage/blockchain/src/service/read.rs index ce69fcf94..f2df6aa09 100644 --- a/storage/blockchain/src/service/read.rs +++ b/storage/blockchain/src/service/read.rs @@ -697,7 +697,7 @@ fn find_first_unknown(db: &BlockchainDatabase, block_ids: &[BlockHash]) -> Respo /// [`BlockchainReadRequest::TxsInBlock`] fn txs_in_block( db: &BlockchainDatabase, - block_hash: [u8; 32], + block_hash: BlockHash, missing_txs: Vec, ) -> ResponseResult { let tx_ro = db.fjall.snapshot();