diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 6ec8f0fde1b5..8150c6e2b127 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -133,6 +133,67 @@ where let latest_historical = self.database.history_by_block_hash(anchor_hash)?; Ok(self.canonical_in_memory_state.state_provider(state.hash(), latest_historical)) } + + /// Returns: + /// 1. The block state as [`Some`] if the block is in memory, and [`None`] if the block is in + /// database. + /// 2. The in-block transaction index. + fn block_state_by_tx_id( + &self, + provider: &DatabaseProviderRO, + id: TxNumber, + ) -> ProviderResult>, usize)>> { + // Get the last block number stored in the database + let last_database_block_number = provider.last_block_number()?; + + // Get the next tx number for the last block stored in the database and consider it the + // first tx number of the in-memory state + let Some(last_block_body_index) = + provider.block_body_indices(last_database_block_number)? + else { + return Ok(None); + }; + let mut in_memory_tx_num = last_block_body_index.next_tx_num(); + + if id < in_memory_tx_num { + // If the transaction number is less than the first in-memory transaction number, make a + // database lookup + let Some(block_number) = provider.transaction_block(id)? else { return Ok(None) }; + let Some(body_index) = provider.block_body_indices(block_number)? else { + return Ok(None) + }; + let tx_index = id - body_index.last_tx_num(); + Ok(Some((None, tx_index as usize))) + } else { + // Otherwise, iterate through in-memory blocks and find the transaction with the + // matching number + + let first_in_memory_block_number = last_database_block_number.saturating_add(1); + let last_in_memory_block_number = + self.canonical_in_memory_state.get_canonical_block_number(); + + for block_number in first_in_memory_block_number..=last_in_memory_block_number { + let Some(block_state) = + self.canonical_in_memory_state.state_by_number(block_number) + else { + return Ok(None); + }; + + let executed_block = block_state.block(); + let block = executed_block.block(); + + for tx_index in 0..block.body.len() { + if id == in_memory_tx_num { + return Ok(Some((Some(block_state), tx_index))) + } + + in_memory_tx_num += 1; + } + } + + Ok(None) + } + } } impl BlockchainProvider2 @@ -692,14 +753,35 @@ where } fn transaction_by_id(&self, id: TxNumber) -> ProviderResult> { - self.database.transaction_by_id(id) + let provider = self.database.provider()?; + let Some((block_state, tx_index)) = self.block_state_by_tx_id(&provider, id)? else { + return Ok(None) + }; + + if let Some(block_state) = block_state { + let transaction = block_state.block().block().body.get(tx_index).cloned(); + Ok(transaction) + } else { + provider.transaction_by_id(id) + } } fn transaction_by_id_no_hash( &self, id: TxNumber, ) -> ProviderResult> { - self.database.transaction_by_id_no_hash(id) + let provider = self.database.provider()?; + let Some((block_state, tx_index)) = self.block_state_by_tx_id(&provider, id)? else { + return Ok(None) + }; + + if let Some(block_state) = block_state { + let transaction = + block_state.block().block().body.get(tx_index).cloned().map(Into::into); + Ok(transaction) + } else { + provider.transaction_by_id_no_hash(id) + } } fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult> { @@ -724,7 +806,11 @@ where } fn transaction_block(&self, id: TxNumber) -> ProviderResult> { - self.database.transaction_block(id) + let provider = self.database.provider()?; + Ok(self + .block_state_by_tx_id(&provider, id)? + .and_then(|(block_state, _)| block_state) + .map(|block_state| block_state.block().block().number)) } fn transactions_by_block( @@ -796,7 +882,22 @@ where } fn transaction_sender(&self, id: TxNumber) -> ProviderResult> { - self.database.transaction_sender(id) + let provider = self.database.provider()?; + let Some((block_state, tx_index)) = self.block_state_by_tx_id(&provider, id)? else { + return Ok(None) + }; + + if let Some(block_state) = block_state { + let sender = block_state + .block() + .block() + .body + .get(tx_index) + .and_then(|transaction| transaction.recover_signer()); + Ok(sender) + } else { + provider.transaction_sender(id) + } } } @@ -805,7 +906,17 @@ where DB: Database, { fn receipt(&self, id: TxNumber) -> ProviderResult> { - self.database.receipt(id) + let provider = self.database.provider()?; + let Some((block_state, tx_index)) = self.block_state_by_tx_id(&provider, id)? else { + return Ok(None) + }; + + if let Some(block_state) = block_state { + let receipt = block_state.executed_block_receipts().get(tx_index).cloned(); + Ok(receipt) + } else { + provider.receipt(id) + } } fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult> {