feat(provider): receipt and transaction by id in BlockchainProvider2 (#10281)

This commit is contained in:
Alexey Shekhirin
2024-08-15 12:22:05 -07:00
committed by GitHub
parent 9f1567011b
commit 821d3e611d

View File

@ -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<DB>,
id: TxNumber,
) -> ProviderResult<Option<(Option<Arc<BlockState>>, 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<DB> BlockchainProvider2<DB>
@ -692,14 +753,35 @@ where
}
fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<TransactionSigned>> {
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<Option<TransactionSignedNoHash>> {
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<Option<TransactionSigned>> {
@ -724,7 +806,11 @@ where
}
fn transaction_block(&self, id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
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<Option<Address>> {
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<Option<Receipt>> {
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<Option<Receipt>> {