feat: Refactor TransitionId tables, BlockBodyIndices table. (#2039)

This commit is contained in:
rakita
2023-04-05 13:10:18 +02:00
committed by GitHub
parent 9a011b4900
commit 633d84ded0
31 changed files with 476 additions and 367 deletions

View File

@ -164,7 +164,7 @@ impl Command {
HeaderTD, HeaderTD,
HeaderNumbers, HeaderNumbers,
Headers, Headers,
BlockBodies, BlockBodyIndices,
BlockOmmers, BlockOmmers,
BlockWithdrawals, BlockWithdrawals,
TransactionBlock, TransactionBlock,
@ -174,8 +174,6 @@ impl Command {
PlainStorageState, PlainStorageState,
PlainAccountState, PlainAccountState,
Bytecodes, Bytecodes,
BlockTransitionIndex,
TxTransitionIndex,
AccountHistory, AccountHistory,
StorageHistory, StorageHistory,
AccountChangeSet, AccountChangeSet,

View File

@ -52,7 +52,7 @@ fn import_tables_with_range<DB: Database>(
tx.import_table_with_range::<tables::Headers, _>(&db_tool.db.tx()?, Some(from), to) tx.import_table_with_range::<tables::Headers, _>(&db_tool.db.tx()?, Some(from), to)
})??; })??;
output_db.update(|tx| { output_db.update(|tx| {
tx.import_table_with_range::<tables::BlockBodies, _>(&db_tool.db.tx()?, Some(from), to) tx.import_table_with_range::<tables::BlockBodyIndices, _>(&db_tool.db.tx()?, Some(from), to)
})??; })??;
output_db.update(|tx| { output_db.update(|tx| {
tx.import_table_with_range::<tables::BlockOmmers, _>(&db_tool.db.tx()?, Some(from), to) tx.import_table_with_range::<tables::BlockOmmers, _>(&db_tool.db.tx()?, Some(from), to)
@ -60,15 +60,15 @@ fn import_tables_with_range<DB: Database>(
// Find range of transactions that need to be copied over // Find range of transactions that need to be copied over
let (from_tx, to_tx) = db_tool.db.view(|read_tx| { let (from_tx, to_tx) = db_tool.db.view(|read_tx| {
let mut read_cursor = read_tx.cursor_read::<tables::BlockBodies>()?; let mut read_cursor = read_tx.cursor_read::<tables::BlockBodyIndices>()?;
let (_, from_block) = let (_, from_block) =
read_cursor.seek(from)?.ok_or(eyre::eyre!("BlockBody {from} does not exist."))?; read_cursor.seek(from)?.ok_or(eyre::eyre!("BlockBody {from} does not exist."))?;
let (_, to_block) = let (_, to_block) =
read_cursor.seek(to)?.ok_or(eyre::eyre!("BlockBody {to} does not exist."))?; read_cursor.seek(to)?.ok_or(eyre::eyre!("BlockBody {to} does not exist."))?;
Ok::<(u64, u64), eyre::ErrReport>(( Ok::<(u64, u64), eyre::ErrReport>((
from_block.start_tx_id, from_block.first_tx_num,
to_block.start_tx_id + to_block.tx_count, to_block.first_tx_num + to_block.tx_count,
)) ))
})??; })??;

View File

@ -21,10 +21,14 @@ pub(crate) async fn dump_hashing_account_stage<DB: Database>(
// Import relevant AccountChangeSets // Import relevant AccountChangeSets
let tx = db_tool.db.tx()?; let tx = db_tool.db.tx()?;
let from_transition_rev = let from_transition_rev = tx
tx.get::<tables::BlockTransitionIndex>(from)?.expect("there should be at least one."); .get::<tables::BlockBodyIndices>(from)?
let to_transition_rev = .expect("there should be at least one.")
tx.get::<tables::BlockTransitionIndex>(to)?.expect("there should be at least one."); .transition_at_block();
let to_transition_rev = tx
.get::<tables::BlockBodyIndices>(to)?
.expect("there should be at least one.")
.transition_after_block();
output_db.update(|tx| { output_db.update(|tx| {
tx.import_table_with_range::<tables::AccountChangeSet, _>( tx.import_table_with_range::<tables::AccountChangeSet, _>(
&db_tool.db.tx()?, &db_tool.db.tx()?,

View File

@ -28,10 +28,14 @@ pub(crate) async fn dump_merkle_stage<DB: Database>(
})??; })??;
let tx = db_tool.db.tx()?; let tx = db_tool.db.tx()?;
let from_transition_rev = let from_transition_rev = tx
tx.get::<tables::BlockTransitionIndex>(from)?.expect("there should be at least one."); .get::<tables::BlockBodyIndices>(from)?
let to_transition_rev = .expect("there should be at least one.")
tx.get::<tables::BlockTransitionIndex>(to)?.expect("there should be at least one."); .transition_at_block();
let to_transition_rev = tx
.get::<tables::BlockBodyIndices>(to)?
.expect("there should be at least one.")
.transition_after_block();
output_db.update(|tx| { output_db.update(|tx| {
tx.import_table_with_range::<tables::AccountChangeSet, _>( tx.import_table_with_range::<tables::AccountChangeSet, _>(

View File

@ -108,8 +108,8 @@ impl Command {
} }
} }
/// Sets up the database and initial state on `BlockTransitionIndex`. Also returns the tip block /// Sets up the database and initial state on [`tables::BlockBodyIndices`]. Also returns the tip
/// number. /// block number.
pub(crate) fn setup<DB: Database>( pub(crate) fn setup<DB: Database>(
from: u64, from: u64,
to: u64, to: u64,
@ -123,17 +123,15 @@ pub(crate) fn setup<DB: Database>(
let output_db = init_db(output_db)?; let output_db = init_db(output_db)?;
output_db.update(|tx| { output_db.update(|tx| {
tx.import_table_with_range::<tables::BlockTransitionIndex, _>( tx.import_table_with_range::<tables::BlockBodyIndices, _>(
&db_tool.db.tx()?, &db_tool.db.tx()?,
Some(from - 1), Some(from - 1),
to + 1, to + 1,
) )
})??; })??;
let (tip_block_number, _) = db_tool let (tip_block_number, _) =
.db db_tool.db.view(|tx| tx.cursor_read::<tables::BlockBodyIndices>()?.last())??.expect("some");
.view(|tx| tx.cursor_read::<tables::BlockTransitionIndex>()?.last())??
.expect("some");
Ok((output_db, tip_block_number)) Ok((output_db, tip_block_number))
} }

View File

@ -60,11 +60,9 @@ pub(crate) fn generate_vectors(mut tables: Vec<String>) -> Result<()> {
(HeaderTD, PER_TABLE, TABLE), (HeaderTD, PER_TABLE, TABLE),
(HeaderNumbers, PER_TABLE, TABLE), (HeaderNumbers, PER_TABLE, TABLE),
(Headers, PER_TABLE, TABLE), (Headers, PER_TABLE, TABLE),
(BlockBodies, PER_TABLE, TABLE), (BlockBodyIndices, PER_TABLE, TABLE),
(BlockOmmers, 100, TABLE), (BlockOmmers, 100, TABLE),
(TxHashNumber, PER_TABLE, TABLE), (TxHashNumber, PER_TABLE, TABLE),
(BlockTransitionIndex, PER_TABLE, TABLE),
(TxTransitionIndex, PER_TABLE, TABLE),
(Transactions, 100, TABLE), (Transactions, 100, TABLE),
(PlainStorageState, PER_TABLE, DUPSORT), (PlainStorageState, PER_TABLE, DUPSORT),
(PlainAccountState, PER_TABLE, TABLE) (PlainAccountState, PER_TABLE, TABLE)

View File

@ -17,8 +17,8 @@ pub enum ProviderError {
#[error("Block hash {block_hash:?} does not exist in Headers table")] #[error("Block hash {block_hash:?} does not exist in Headers table")]
BlockHash { block_hash: BlockHash }, BlockHash { block_hash: BlockHash },
/// A block body is missing. /// A block body is missing.
#[error("Block body not found for block #{number}")] #[error("Block meta not found for block #{number}")]
BlockBody { number: BlockNumber }, BlockBodyIndices { number: BlockNumber },
/// The block transition id for a certain block number is missing. /// The block transition id for a certain block number is missing.
#[error("Block transition id does not exist for block #{block_number}")] #[error("Block transition id does not exist for block #{block_number}")]
BlockTransition { block_number: BlockNumber }, BlockTransition { block_number: BlockNumber },

View File

@ -64,8 +64,7 @@ pub fn init_genesis<DB: Database>(
// Insert header // Insert header
tx.put::<tables::CanonicalHeaders>(0, hash)?; tx.put::<tables::CanonicalHeaders>(0, hash)?;
tx.put::<tables::HeaderNumbers>(hash, 0)?; tx.put::<tables::HeaderNumbers>(hash, 0)?;
tx.put::<tables::BlockBodies>(0, Default::default())?; tx.put::<tables::BlockBodyIndices>(0, Default::default())?;
tx.put::<tables::BlockTransitionIndex>(0, 0)?;
tx.put::<tables::HeaderTD>(0, header.difficulty.into())?; tx.put::<tables::HeaderTD>(0, header.difficulty.into())?;
tx.put::<tables::Headers>(0, header)?; tx.put::<tables::Headers>(0, header)?;

View File

@ -98,7 +98,7 @@ fn merkle(c: &mut Criterion) {
&mut group, &mut group,
setup::unwind_hashes, setup::unwind_hashes,
stage, stage,
1..DEFAULT_NUM_BLOCKS + 1, 1..DEFAULT_NUM_BLOCKS,
"Merkle-incremental".to_string(), "Merkle-incremental".to_string(),
); );
@ -107,7 +107,7 @@ fn merkle(c: &mut Criterion) {
&mut group, &mut group,
setup::unwind_hashes, setup::unwind_hashes,
stage, stage,
1..DEFAULT_NUM_BLOCKS + 1, 1..DEFAULT_NUM_BLOCKS,
"Merkle-fullhash".to_string(), "Merkle-fullhash".to_string(),
); );
} }

View File

@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
/// Prepares a database for [`AccountHashingStage`] /// Prepares a database for [`AccountHashingStage`]
/// If the environment variable [`constants::ACCOUNT_HASHING_DB`] is set, it will use that one and /// If the environment variable [`constants::ACCOUNT_HASHING_DB`] is set, it will use that one and
/// will get the stage execution range from [`tables::BlockTransitionIndex`]. Otherwise, it will /// will get the stage execution range from [`tables::BlockBodyIndices`]. Otherwise, it will
/// generate its own random data. /// generate its own random data.
/// ///
/// Returns the path to the database file, stage and range of stage execution if it exists. /// Returns the path to the database file, stage and range of stage execution if it exists.
@ -33,7 +33,7 @@ fn find_stage_range(db: &Path) -> StageRange {
TestTransaction::new(db) TestTransaction::new(db)
.tx .tx
.view(|tx| { .view(|tx| {
let mut cursor = tx.cursor_read::<tables::BlockTransitionIndex>()?; let mut cursor = tx.cursor_read::<tables::BlockBodyIndices>()?;
let from = cursor.first()?.unwrap().0; let from = cursor.first()?.unwrap().0;
let to = cursor.last()?.unwrap().0; let to = cursor.last()?.unwrap().0;
@ -55,7 +55,7 @@ fn find_stage_range(db: &Path) -> StageRange {
fn generate_testdata_db(num_blocks: u64) -> (PathBuf, StageRange) { fn generate_testdata_db(num_blocks: u64) -> (PathBuf, StageRange) {
let opts = SeedOpts { let opts = SeedOpts {
blocks: 0..num_blocks + 1, blocks: 0..num_blocks + 1,
accounts: 0..10_000, accounts: 0..100_000,
txs: 100..150, txs: 100..150,
transitions: 10_000 + 1, transitions: 10_000 + 1,
}; };

View File

@ -730,15 +730,15 @@ mod tests {
let db = test_utils::create_test_db::<mdbx::WriteMap>(EnvKind::RW); let db = test_utils::create_test_db::<mdbx::WriteMap>(EnvKind::RW);
let mut pipeline: Pipeline<_, NoopSyncStateUpdate> = Pipeline::builder() let mut pipeline: Pipeline<_, NoopSyncStateUpdate> = Pipeline::builder()
.add_stage(TestStage::new(StageId("Fatal")).add_exec(Err( .add_stage(TestStage::new(StageId("Fatal")).add_exec(Err(
StageError::DatabaseIntegrity(ProviderError::BlockBody { number: 5 }), StageError::DatabaseIntegrity(ProviderError::BlockBodyIndices { number: 5 }),
))) )))
.build(); .build();
let result = pipeline.run(db).await; let result = pipeline.run(db).await;
assert_matches!( assert_matches!(
result, result,
Err(PipelineError::Stage(StageError::DatabaseIntegrity(ProviderError::BlockBody { Err(PipelineError::Stage(StageError::DatabaseIntegrity(
number: 5 ProviderError::BlockBodyIndices { number: 5 }
}))) )))
); );
} }
} }

View File

@ -6,7 +6,7 @@ use futures_util::TryStreamExt;
use reth_db::{ use reth_db::{
cursor::{DbCursorRO, DbCursorRW}, cursor::{DbCursorRO, DbCursorRW},
database::Database, database::Database,
models::{StoredBlockBody, StoredBlockOmmers, StoredBlockWithdrawals}, models::{StoredBlockBodyIndices, StoredBlockOmmers, StoredBlockWithdrawals},
tables, tables,
transaction::{DbTx, DbTxMut}, transaction::{DbTx, DbTxMut},
}; };
@ -42,11 +42,9 @@ pub const BODIES: StageId = StageId("Bodies");
/// The bodies are processed and data is inserted into these tables: /// The bodies are processed and data is inserted into these tables:
/// ///
/// - [`BlockOmmers`][reth_db::tables::BlockOmmers] /// - [`BlockOmmers`][reth_db::tables::BlockOmmers]
/// - [`BlockBodies`][reth_db::tables::BlockBodies] /// - [`BlockBodies`][reth_db::tables::BlockBodyIndices]
/// - [`Transactions`][reth_db::tables::Transactions] /// - [`Transactions`][reth_db::tables::Transactions]
/// - [`TransactionBlock`][reth_db::tables::TransactionBlock] /// - [`TransactionBlock`][reth_db::tables::TransactionBlock]
/// - [`BlockTransitionIndex`][reth_db::tables::BlockTransitionIndex]
/// - [`TxTransitionIndex`][reth_db::tables::TxTransitionIndex]
/// ///
/// # Genesis /// # Genesis
/// ///
@ -54,10 +52,8 @@ pub const BODIES: StageId = StageId("Bodies");
/// ///
/// - The header tables (see [`HeaderStage`][crate::stages::HeaderStage]) /// - The header tables (see [`HeaderStage`][crate::stages::HeaderStage])
/// - The [`BlockOmmers`][reth_db::tables::BlockOmmers] table /// - The [`BlockOmmers`][reth_db::tables::BlockOmmers] table
/// - The [`BlockBodies`][reth_db::tables::BlockBodies] table /// - The [`BlockBodies`][reth_db::tables::BlockBodyIndices] table
/// - The [`Transactions`][reth_db::tables::Transactions] table /// - The [`Transactions`][reth_db::tables::Transactions] table
/// - The [`BlockTransitionIndex`][reth_db::tables::BlockTransitionIndex] table
/// - The [`TxTransitionIndex`][reth_db::tables::TxTransitionIndex] table
#[derive(Debug)] #[derive(Debug)]
pub struct BodyStage<D: BodyDownloader> { pub struct BodyStage<D: BodyDownloader> {
/// The body downloader. /// The body downloader.
@ -89,21 +85,17 @@ impl<DB: Database, D: BodyDownloader> Stage<DB> for BodyStage<D> {
let mut td_cursor = tx.cursor_read::<tables::HeaderTD>()?; let mut td_cursor = tx.cursor_read::<tables::HeaderTD>()?;
// Cursors used to write bodies, ommers and transactions // Cursors used to write bodies, ommers and transactions
let mut body_cursor = tx.cursor_write::<tables::BlockBodies>()?; let mut block_meta_cursor = tx.cursor_write::<tables::BlockBodyIndices>()?;
let mut tx_cursor = tx.cursor_write::<tables::Transactions>()?; let mut tx_cursor = tx.cursor_write::<tables::Transactions>()?;
let mut tx_block_cursor = tx.cursor_write::<tables::TransactionBlock>()?; let mut tx_block_cursor = tx.cursor_write::<tables::TransactionBlock>()?;
let mut ommers_cursor = tx.cursor_write::<tables::BlockOmmers>()?; let mut ommers_cursor = tx.cursor_write::<tables::BlockOmmers>()?;
let mut withdrawals_cursor = tx.cursor_write::<tables::BlockWithdrawals>()?; let mut withdrawals_cursor = tx.cursor_write::<tables::BlockWithdrawals>()?;
// Cursors used to write state transition mapping
let mut block_transition_cursor = tx.cursor_write::<tables::BlockTransitionIndex>()?;
let mut tx_transition_cursor = tx.cursor_write::<tables::TxTransitionIndex>()?;
// Get id for the first transaction and first transition in the block // Get id for the first transaction and first transition in the block
let (mut current_tx_id, mut transition_id) = tx.get_next_block_ids(start_block)?; let (mut next_tx_num, mut next_transition_id) = tx.get_next_block_ids(start_block)?;
let mut highest_block = input.stage_progress.unwrap_or_default(); let mut highest_block = input.stage_progress.unwrap_or_default();
debug!(target: "sync::stages::bodies", stage_progress = highest_block, target = end_block, start_tx_id = current_tx_id, transition_id, "Commencing sync"); debug!(target: "sync::stages::bodies", stage_progress = highest_block, target = end_block, start_tx_id = next_tx_num, next_transition_id, "Commencing sync");
// Task downloader can return `None` only if the response relaying channel was closed. This // Task downloader can return `None` only if the response relaying channel was closed. This
// is a fatal error to prevent the pipeline from running forever. // is a fatal error to prevent the pipeline from running forever.
@ -116,29 +108,24 @@ impl<DB: Database, D: BodyDownloader> Stage<DB> for BodyStage<D> {
let block_number = response.block_number(); let block_number = response.block_number();
let difficulty = response.difficulty(); let difficulty = response.difficulty();
let first_tx_num = next_tx_num;
let first_transition_id = next_transition_id;
let mut tx_count = 0;
let mut has_withdrawals = false; let mut has_withdrawals = false;
match response { match response {
BlockResponse::Full(block) => { BlockResponse::Full(block) => {
let body = StoredBlockBody { tx_count = block.body.len() as u64;
start_tx_id: current_tx_id,
tx_count: block.body.len() as u64,
};
body_cursor.append(block_number, body.clone())?;
// write transaction block index // write transaction block index
if !body.is_empty() { if !block.body.is_empty() {
tx_block_cursor.append(body.last_tx_index(), block.number)?; tx_block_cursor.append(first_tx_num, block.number)?;
} }
// Write transactions // Write transactions
for transaction in block.body { for transaction in block.body {
// Append the transaction // Append the transaction
tx_cursor.append(current_tx_id, transaction)?; tx_cursor.append(next_tx_num, transaction)?;
tx_transition_cursor.append(current_tx_id, transition_id)?;
// Increment transaction id for each transaction. // Increment transaction id for each transaction.
current_tx_id += 1; next_tx_num += 1;
// Increment transition id for each transaction.
transition_id += 1;
} }
// Write ommers if any // Write ommers if any
@ -164,12 +151,7 @@ impl<DB: Database, D: BodyDownloader> Stage<DB> for BodyStage<D> {
} }
} }
} }
BlockResponse::Empty(_) => { BlockResponse::Empty(_) => {}
body_cursor.append(
block_number,
StoredBlockBody { start_tx_id: current_tx_id, tx_count: 0 },
)?;
}
}; };
// The block transition marks the final state at the end of the block. // The block transition marks the final state at the end of the block.
@ -181,11 +163,22 @@ impl<DB: Database, D: BodyDownloader> Stage<DB> for BodyStage<D> {
.ok_or(ProviderError::TotalDifficulty { number: block_number })? .ok_or(ProviderError::TotalDifficulty { number: block_number })?
.1; .1;
let has_reward = self.consensus.has_block_reward(td.into(), difficulty); let has_reward = self.consensus.has_block_reward(td.into(), difficulty);
let has_post_block_transition = has_reward || has_withdrawals; let has_block_change = has_reward || has_withdrawals;
if has_post_block_transition {
transition_id += 1; // Increment transition id for each transaction,
} // and by +1 if the block has its own state change (an block reward or withdrawals).
block_transition_cursor.append(block_number, transition_id)?; next_transition_id += tx_count + has_block_change as u64;
// insert block meta
block_meta_cursor.append(
block_number,
StoredBlockBodyIndices {
first_tx_num,
first_transition_id,
has_block_change,
tx_count,
},
)?;
highest_block = block_number; highest_block = block_number;
} }
@ -206,17 +199,15 @@ impl<DB: Database, D: BodyDownloader> Stage<DB> for BodyStage<D> {
) -> Result<UnwindOutput, StageError> { ) -> Result<UnwindOutput, StageError> {
info!(target: "sync::stages::bodies", to_block = input.unwind_to, "Unwinding"); info!(target: "sync::stages::bodies", to_block = input.unwind_to, "Unwinding");
// Cursors to unwind bodies, ommers // Cursors to unwind bodies, ommers
let mut body_cursor = tx.cursor_write::<tables::BlockBodies>()?; let mut body_cursor = tx.cursor_write::<tables::BlockBodyIndices>()?;
let mut transaction_cursor = tx.cursor_write::<tables::Transactions>()?; let mut transaction_cursor = tx.cursor_write::<tables::Transactions>()?;
let mut ommers_cursor = tx.cursor_write::<tables::BlockOmmers>()?; let mut ommers_cursor = tx.cursor_write::<tables::BlockOmmers>()?;
let mut withdrawals_cursor = tx.cursor_write::<tables::BlockWithdrawals>()?; let mut withdrawals_cursor = tx.cursor_write::<tables::BlockWithdrawals>()?;
// Cursors to unwind transitions // Cursors to unwind transitions
let mut block_transition_cursor = tx.cursor_write::<tables::BlockTransitionIndex>()?;
let mut tx_transition_cursor = tx.cursor_write::<tables::TxTransitionIndex>()?;
let mut tx_block_cursor = tx.cursor_write::<tables::TransactionBlock>()?; let mut tx_block_cursor = tx.cursor_write::<tables::TransactionBlock>()?;
let mut rev_walker = body_cursor.walk_back(None)?; let mut rev_walker = body_cursor.walk_back(None)?;
while let Some((number, body)) = rev_walker.next().transpose()? { while let Some((number, block_meta)) = rev_walker.next().transpose()? {
if number <= input.unwind_to { if number <= input.unwind_to {
break break
} }
@ -231,30 +222,23 @@ impl<DB: Database, D: BodyDownloader> Stage<DB> for BodyStage<D> {
withdrawals_cursor.delete_current()?; withdrawals_cursor.delete_current()?;
} }
// Delete the block transition if any
if block_transition_cursor.seek_exact(number)?.is_some() {
block_transition_cursor.delete_current()?;
}
// Delete all transaction to block values. // Delete all transaction to block values.
if !body.is_empty() && tx_block_cursor.seek_exact(body.last_tx_index())?.is_some() { if !block_meta.is_empty() &&
tx_block_cursor.seek_exact(block_meta.last_tx_num())?.is_some()
{
tx_block_cursor.delete_current()?; tx_block_cursor.delete_current()?;
} }
// Delete all transactions that belong to this block // Delete all transactions that belong to this block
for tx_id in body.tx_id_range() { for tx_id in block_meta.tx_num_range() {
// First delete the transaction // First delete the transaction
if transaction_cursor.seek_exact(tx_id)?.is_some() { if transaction_cursor.seek_exact(tx_id)?.is_some() {
transaction_cursor.delete_current()?; transaction_cursor.delete_current()?;
} }
// Delete the transaction transition if any
if tx_transition_cursor.seek_exact(tx_id)?.is_some() {
tx_transition_cursor.delete_current()?;
}
} }
// Delete the current body value // Delete the current body value
tx.delete::<tables::BlockBodies>(number, None)?; tx.delete::<tables::BlockBodyIndices>(number, None)?;
} }
Ok(UnwindOutput { stage_progress: input.unwind_to }) Ok(UnwindOutput { stage_progress: input.unwind_to })
@ -443,7 +427,7 @@ mod tests {
cursor::DbCursorRO, cursor::DbCursorRO,
database::Database, database::Database,
mdbx::{Env, WriteMap}, mdbx::{Env, WriteMap},
models::{StoredBlockBody, StoredBlockOmmers}, models::{StoredBlockBodyIndices, StoredBlockOmmers},
tables, tables,
transaction::{DbTx, DbTxMut}, transaction::{DbTx, DbTxMut},
}; };
@ -548,30 +532,26 @@ mod tests {
if let Some(progress) = blocks.first() { if let Some(progress) = blocks.first() {
// Insert last progress data // Insert last progress data
self.tx.commit(|tx| { self.tx.commit(|tx| {
let body = StoredBlockBody { let body = StoredBlockBodyIndices {
start_tx_id: 0, first_tx_num: 0,
first_transition_id: 0,
tx_count: progress.body.len() as u64, tx_count: progress.body.len() as u64,
has_block_change: true,
}; };
body.tx_id_range().try_for_each(|tx_id| { body.tx_num_range().try_for_each(|tx_num| {
let transaction = random_signed_tx(); let transaction = random_signed_tx();
tx.put::<tables::Transactions>(tx_id, transaction)?; tx.put::<tables::Transactions>(tx_num, transaction)
tx.put::<tables::TxTransitionIndex>(tx_id, tx_id)
})?; })?;
let last_transition_id = progress.body.len() as u64;
let block_transition_id = last_transition_id + 1; // for block reward
tx.put::<tables::BlockTransitionIndex>(
progress.number,
block_transition_id,
)?;
if body.tx_count != 0 { if body.tx_count != 0 {
tx.put::<tables::TransactionBlock>( tx.put::<tables::TransactionBlock>(
body.first_tx_index(), body.first_tx_num(),
progress.number, progress.number,
)?; )?;
} }
tx.put::<tables::BlockBodies>(progress.number, body)?;
tx.put::<tables::BlockBodyIndices>(progress.number, body)?;
if !progress.ommers_hash_is_empty() { if !progress.ommers_hash_is_empty() {
tx.put::<tables::BlockOmmers>( tx.put::<tables::BlockOmmers>(
progress.number, progress.number,
@ -606,21 +586,15 @@ mod tests {
impl UnwindStageTestRunner for BodyTestRunner { impl UnwindStageTestRunner for BodyTestRunner {
fn validate_unwind(&self, input: UnwindInput) -> Result<(), TestRunnerError> { fn validate_unwind(&self, input: UnwindInput) -> Result<(), TestRunnerError> {
self.tx self.tx.ensure_no_entry_above::<tables::BlockBodyIndices, _>(
.ensure_no_entry_above::<tables::BlockBodies, _>(input.unwind_to, |key| key)?;
self.tx
.ensure_no_entry_above::<tables::BlockOmmers, _>(input.unwind_to, |key| key)?;
self.tx.ensure_no_entry_above::<tables::BlockTransitionIndex, _>(
input.unwind_to, input.unwind_to,
|key| key, |key| key,
)?; )?;
self.tx
.ensure_no_entry_above::<tables::BlockOmmers, _>(input.unwind_to, |key| key)?;
if let Some(last_tx_id) = self.get_last_tx_id()? { if let Some(last_tx_id) = self.get_last_tx_id()? {
self.tx self.tx
.ensure_no_entry_above::<tables::Transactions, _>(last_tx_id, |key| key)?; .ensure_no_entry_above::<tables::Transactions, _>(last_tx_id, |key| key)?;
self.tx.ensure_no_entry_above::<tables::TxTransitionIndex, _>(
last_tx_id,
|key| key,
)?;
self.tx.ensure_no_entry_above::<tables::TransactionBlock, _>( self.tx.ensure_no_entry_above::<tables::TransactionBlock, _>(
last_tx_id, last_tx_id,
|key| key, |key| key,
@ -634,12 +608,12 @@ mod tests {
/// Get the last available tx id if any /// Get the last available tx id if any
pub(crate) fn get_last_tx_id(&self) -> Result<Option<TxNumber>, TestRunnerError> { pub(crate) fn get_last_tx_id(&self) -> Result<Option<TxNumber>, TestRunnerError> {
let last_body = self.tx.query(|tx| { let last_body = self.tx.query(|tx| {
let v = tx.cursor_read::<tables::BlockBodies>()?.last()?; let v = tx.cursor_read::<tables::BlockBodyIndices>()?.last()?;
Ok(v) Ok(v)
})?; })?;
Ok(match last_body { Ok(match last_body {
Some((_, body)) if body.tx_count != 0 => { Some((_, body)) if body.tx_count != 0 => {
Some(body.start_tx_id + body.tx_count - 1) Some(body.first_tx_num + body.tx_count - 1)
} }
_ => None, _ => None,
}) })
@ -655,11 +629,9 @@ mod tests {
// Acquire cursors on body related tables // Acquire cursors on body related tables
let mut headers_cursor = tx.cursor_read::<tables::Headers>()?; let mut headers_cursor = tx.cursor_read::<tables::Headers>()?;
let mut td_cursor = tx.cursor_read::<tables::HeaderTD>()?; let mut td_cursor = tx.cursor_read::<tables::HeaderTD>()?;
let mut bodies_cursor = tx.cursor_read::<tables::BlockBodies>()?; let mut bodies_cursor = tx.cursor_read::<tables::BlockBodyIndices>()?;
let mut ommers_cursor = tx.cursor_read::<tables::BlockOmmers>()?; let mut ommers_cursor = tx.cursor_read::<tables::BlockOmmers>()?;
let mut block_transition_cursor = tx.cursor_read::<tables::BlockTransitionIndex>()?;
let mut transaction_cursor = tx.cursor_read::<tables::Transactions>()?; let mut transaction_cursor = tx.cursor_read::<tables::Transactions>()?;
let mut tx_transition_cursor = tx.cursor_read::<tables::TxTransitionIndex>()?;
let mut tx_block_cursor = tx.cursor_read::<tables::TransactionBlock>()?; let mut tx_block_cursor = tx.cursor_read::<tables::TransactionBlock>()?;
let first_body_key = match bodies_cursor.first()? { let first_body_key = match bodies_cursor.first()? {
@ -696,19 +668,18 @@ mod tests {
assert!(stored_ommers.is_some(), "Missing ommers entry"); assert!(stored_ommers.is_some(), "Missing ommers entry");
} }
let tx_block_id = tx_block_cursor.seek_exact(body.last_tx_index())?.map(|(_,b)| b); let tx_block_id = tx_block_cursor.seek_exact(body.last_tx_num())?.map(|(_,b)| b);
if body.tx_count == 0 { if body.tx_count == 0 {
assert_ne!(tx_block_id,Some(number)); assert_ne!(tx_block_id,Some(number));
} else { } else {
assert_eq!(tx_block_id, Some(number)); assert_eq!(tx_block_id, Some(number));
} }
for tx_id in body.tx_id_range() { assert_eq!(body.first_transition_id, expected_transition_id);
for tx_id in body.tx_num_range() {
let tx_entry = transaction_cursor.seek_exact(tx_id)?; let tx_entry = transaction_cursor.seek_exact(tx_id)?;
assert!(tx_entry.is_some(), "Transaction is missing."); assert!(tx_entry.is_some(), "Transaction is missing.");
assert_eq!(
tx_transition_cursor.seek_exact(tx_id).expect("to be okay").expect("to be present").1, expected_transition_id
);
// Increment expected id for each transaction transition. // Increment expected id for each transaction transition.
expected_transition_id += 1; expected_transition_id += 1;
} }
@ -722,10 +693,6 @@ mod tests {
expected_transition_id += 1; expected_transition_id += 1;
} }
// Validate that block transition exists
assert_eq!(block_transition_cursor.seek_exact(number).expect("To be okay").expect("Block transition to be present").1,expected_transition_id);
prev_number = Some(number); prev_number = Some(number);
} }
Ok(()) Ok(())

View File

@ -36,7 +36,7 @@ pub struct ExecutionStageMetrics {
/// - [tables::CanonicalHeaders] get next block to execute. /// - [tables::CanonicalHeaders] get next block to execute.
/// - [tables::Headers] get for revm environment variables. /// - [tables::Headers] get for revm environment variables.
/// - [tables::HeaderTD] /// - [tables::HeaderTD]
/// - [tables::BlockBodies] to get tx number /// - [tables::BlockBodyIndices] to get tx number
/// - [tables::Transactions] to execute /// - [tables::Transactions] to execute
/// ///
/// For state access [LatestStateProviderRef] provides us latest state and history state /// For state access [LatestStateProviderRef] provides us latest state and history state
@ -53,7 +53,7 @@ pub struct ExecutionStageMetrics {
/// - [tables::StorageChangeSet] /// - [tables::StorageChangeSet]
/// ///
/// For unwinds we are accessing: /// For unwinds we are accessing:
/// - [tables::BlockBodies] get tx index to know what needs to be unwinded /// - [tables::BlockBodyIndices] get tx index to know what needs to be unwinded
/// - [tables::AccountHistory] to remove change set and apply old values to /// - [tables::AccountHistory] to remove change set and apply old values to
/// - [tables::PlainAccountState] [tables::StorageHistory] to remove change set and apply old values /// - [tables::PlainAccountState] [tables::StorageHistory] to remove change set and apply old values
/// to [tables::PlainStorageState] /// to [tables::PlainStorageState]

View File

@ -1,4 +1,6 @@
use crate::{ExecInput, ExecOutput, Stage, StageError, StageId, UnwindInput, UnwindOutput}; use crate::{ExecInput, ExecOutput, Stage, StageError, StageId, UnwindInput, UnwindOutput};
use itertools::Itertools;
use rayon::slice::ParallelSliceMut;
use reth_codecs::Compact; use reth_codecs::Compact;
use reth_db::{ use reth_db::{
cursor::{DbCursorRO, DbCursorRW}, cursor::{DbCursorRO, DbCursorRW},
@ -8,7 +10,8 @@ use reth_db::{
}; };
use reth_primitives::{keccak256, AccountHashingCheckpoint}; use reth_primitives::{keccak256, AccountHashingCheckpoint};
use reth_provider::Transaction; use reth_provider::Transaction;
use std::{collections::BTreeMap, fmt::Debug, ops::Range}; use std::{fmt::Debug, ops::Range};
use tokio::sync::mpsc;
use tracing::*; use tracing::*;
/// The [`StageId`] of the account hashing stage. /// The [`StageId`] of the account hashing stage.
@ -21,7 +24,7 @@ pub struct AccountHashingStage {
/// The threshold (in number of state transitions) for switching between incremental /// The threshold (in number of state transitions) for switching between incremental
/// hashing and full storage hashing. /// hashing and full storage hashing.
pub clean_threshold: u64, pub clean_threshold: u64,
/// The maximum number of blocks to process before committing. /// The maximum number of accounts to process before committing.
pub commit_threshold: u64, pub commit_threshold: u64,
} }
@ -126,8 +129,12 @@ impl AccountHashingStage {
} }
// seed account changeset // seed account changeset
let (_, last_transition) = let last_transition = tx
tx.cursor_read::<tables::BlockTransitionIndex>()?.last()?.unwrap(); .cursor_read::<tables::BlockBodyIndices>()?
.last()?
.unwrap()
.1
.transition_after_block();
let first_transition = last_transition.checked_sub(transitions).unwrap_or_default(); let first_transition = last_transition.checked_sub(transitions).unwrap_or_default();
@ -192,13 +199,38 @@ impl<DB: Database> Stage<DB> for AccountHashingStage {
let start_address = checkpoint.address.take(); let start_address = checkpoint.address.take();
let next_address = { let next_address = {
let mut accounts = tx.cursor_read::<tables::PlainAccountState>()?; let mut accounts_cursor = tx.cursor_read::<tables::PlainAccountState>()?;
let hashed_batch = accounts // channels used to return result of account hashing
let mut channels = Vec::new();
for chunk in &accounts_cursor
.walk(start_address)? .walk(start_address)?
.take(self.commit_threshold as usize) .take(self.commit_threshold as usize)
.map(|res| res.map(|(address, account)| (keccak256(address), account))) .chunks(self.commit_threshold as usize / rayon::current_num_threads())
.collect::<Result<BTreeMap<_, _>, _>>()?; {
// An _unordered_ channel to receive results from a rayon job
let (tx, rx) = mpsc::unbounded_channel();
channels.push(rx);
let chunk = chunk.collect::<Result<Vec<_>, _>>()?;
// Spawn the hashing task onto the global rayon pool
rayon::spawn(move || {
for (address, account) in chunk.into_iter() {
let _ = tx.send((keccak256(address), account));
}
});
}
let mut hashed_batch = Vec::with_capacity(self.commit_threshold as usize);
// Iterate over channels and append the hashed accounts.
for mut channel in channels {
while let Some(hashed) = channel.recv().await {
hashed_batch.push(hashed);
}
}
// sort it all in parallel
hashed_batch.par_sort_unstable_by(|a, b| a.0.cmp(&b.0));
let mut hashed_account_cursor = tx.cursor_write::<tables::HashedAccount>()?; let mut hashed_account_cursor = tx.cursor_write::<tables::HashedAccount>()?;
@ -214,7 +246,7 @@ impl<DB: Database> Stage<DB> for AccountHashingStage {
} }
// next key of iterator // next key of iterator
accounts.next()? accounts_cursor.next()?
}; };
if let Some((next_address, _)) = &next_address { if let Some((next_address, _)) = &next_address {

View File

@ -226,7 +226,7 @@ mod tests {
use reth_db::{ use reth_db::{
cursor::{DbCursorRO, DbCursorRW}, cursor::{DbCursorRO, DbCursorRW},
mdbx::{tx::Tx, WriteMap, RW}, mdbx::{tx::Tx, WriteMap, RW},
models::{StoredBlockBody, TransitionIdAddress}, models::{StoredBlockBodyIndices, TransitionIdAddress},
}; };
use reth_interfaces::test_utils::generators::{ use reth_interfaces::test_utils::generators::{
random_block_range, random_contract_account_range, random_block_range, random_contract_account_range,
@ -321,20 +321,15 @@ mod tests {
self.tx.insert_headers(blocks.iter().map(|block| &block.header))?; self.tx.insert_headers(blocks.iter().map(|block| &block.header))?;
let iter = blocks.iter(); let iter = blocks.iter();
let (mut transition_id, mut tx_id) = (0, 0); let (mut next_transition_id, mut next_tx_num) = (0, 0);
let mut first_transition_id = next_transition_id;
let mut first_tx_num = next_tx_num;
for progress in iter { for progress in iter {
// Insert last progress data // Insert last progress data
self.tx.commit(|tx| { self.tx.commit(|tx| {
let body = StoredBlockBody {
start_tx_id: tx_id,
tx_count: progress.body.len() as u64,
};
progress.body.iter().try_for_each(|transaction| { progress.body.iter().try_for_each(|transaction| {
tx.put::<tables::TxHashNumber>(transaction.hash(), tx_id)?; tx.put::<tables::TxHashNumber>(transaction.hash(), next_tx_num)?;
tx.put::<tables::Transactions>(tx_id, transaction.clone())?; tx.put::<tables::Transactions>(next_tx_num, transaction.clone())?;
tx.put::<tables::TxTransitionIndex>(tx_id, transition_id)?;
let (addr, _) = accounts let (addr, _) = accounts
.get_mut(rand::random::<usize>() % n_accounts as usize) .get_mut(rand::random::<usize>() % n_accounts as usize)
@ -347,14 +342,14 @@ mod tests {
}; };
self.insert_storage_entry( self.insert_storage_entry(
tx, tx,
(transition_id, *addr).into(), (next_transition_id, *addr).into(),
new_entry, new_entry,
progress.header.number == stage_progress, progress.header.number == stage_progress,
)?; )?;
} }
tx_id += 1; next_tx_num += 1;
transition_id += 1; next_transition_id += 1;
Ok(()) Ok(())
})?; })?;
@ -363,18 +358,27 @@ mod tests {
if has_reward { if has_reward {
self.insert_storage_entry( self.insert_storage_entry(
tx, tx,
(transition_id, Address::random()).into(), (next_transition_id, Address::random()).into(),
StorageEntry { StorageEntry {
key: keccak256("mining"), key: keccak256("mining"),
value: U256::from(rand::random::<u32>()), value: U256::from(rand::random::<u32>()),
}, },
progress.header.number == stage_progress, progress.header.number == stage_progress,
)?; )?;
transition_id += 1; next_transition_id += 1;
} }
tx.put::<tables::BlockTransitionIndex>(progress.number, transition_id)?; let body = StoredBlockBodyIndices {
tx.put::<tables::BlockBodies>(progress.number, body) first_tx_num,
first_transition_id,
tx_count: progress.body.len() as u64,
has_block_change: has_reward,
};
first_transition_id = next_transition_id;
first_tx_num = next_tx_num;
tx.put::<tables::BlockBodyIndices>(progress.number, body)
})?; })?;
} }

View File

@ -80,7 +80,10 @@ mod tests {
use super::*; use super::*;
use crate::test_utils::{TestTransaction, PREV_STAGE_ID}; use crate::test_utils::{TestTransaction, PREV_STAGE_ID};
use reth_db::{ use reth_db::{
models::{sharded_key::NUM_OF_INDICES_IN_SHARD, AccountBeforeTx, ShardedKey}, models::{
sharded_key::NUM_OF_INDICES_IN_SHARD, AccountBeforeTx, ShardedKey,
StoredBlockBodyIndices,
},
tables, tables,
transaction::DbTxMut, transaction::DbTxMut,
TransitionList, TransitionList,
@ -118,8 +121,25 @@ mod tests {
// setup // setup
tx.commit(|tx| { tx.commit(|tx| {
// we just need first and last // we just need first and last
tx.put::<tables::BlockTransitionIndex>(0, 3).unwrap(); tx.put::<tables::BlockBodyIndices>(
tx.put::<tables::BlockTransitionIndex>(5, 7).unwrap(); 0,
StoredBlockBodyIndices {
first_transition_id: 0,
tx_count: 3,
..Default::default()
},
)
.unwrap();
tx.put::<tables::BlockBodyIndices>(
5,
StoredBlockBodyIndices {
first_transition_id: 3,
tx_count: 5,
..Default::default()
},
)
.unwrap();
// setup changeset that are going to be applied to history index // setup changeset that are going to be applied to history index
tx.put::<tables::AccountChangeSet>(4, acc()).unwrap(); tx.put::<tables::AccountChangeSet>(4, acc()).unwrap();

View File

@ -86,7 +86,7 @@ mod tests {
use reth_db::{ use reth_db::{
models::{ models::{
storage_sharded_key::{StorageShardedKey, NUM_OF_INDICES_IN_SHARD}, storage_sharded_key::{StorageShardedKey, NUM_OF_INDICES_IN_SHARD},
ShardedKey, TransitionIdAddress, ShardedKey, StoredBlockBodyIndices, TransitionIdAddress,
}, },
tables, tables,
transaction::DbTxMut, transaction::DbTxMut,
@ -135,8 +135,25 @@ mod tests {
// setup // setup
tx.commit(|tx| { tx.commit(|tx| {
// we just need first and last // we just need first and last
tx.put::<tables::BlockTransitionIndex>(0, 3).unwrap(); tx.put::<tables::BlockBodyIndices>(
tx.put::<tables::BlockTransitionIndex>(5, 7).unwrap(); 0,
StoredBlockBodyIndices {
first_transition_id: 0,
tx_count: 3,
..Default::default()
},
)
.unwrap();
tx.put::<tables::BlockBodyIndices>(
5,
StoredBlockBodyIndices {
first_transition_id: 3,
tx_count: 5,
..Default::default()
},
)
.unwrap();
// setup changeset that are going to be applied to history index // setup changeset that are going to be applied to history index
tx.put::<tables::StorageChangeSet>(trns(4), storage(STORAGE_KEY)).unwrap(); tx.put::<tables::StorageChangeSet>(trns(4), storage(STORAGE_KEY)).unwrap();

View File

@ -43,7 +43,7 @@ impl<DB: Database> Stage<DB> for SenderRecoveryStage {
} }
/// Retrieve the range of transactions to iterate over by querying /// Retrieve the range of transactions to iterate over by querying
/// [`BlockBodies`][reth_db::tables::BlockBodies], /// [`BlockBodyIndices`][reth_db::tables::BlockBodyIndices],
/// collect transactions within that range, /// collect transactions within that range,
/// recover signer for each transaction and store entries in /// recover signer for each transaction and store entries in
/// the [`TxSenders`][reth_db::tables::TxSenders] table. /// the [`TxSenders`][reth_db::tables::TxSenders] table.
@ -57,14 +57,14 @@ impl<DB: Database> Stage<DB> for SenderRecoveryStage {
let done = !capped; let done = !capped;
// Look up the start index for the transaction range // Look up the start index for the transaction range
let start_tx_index = tx.get_block_body(start_block)?.start_tx_id; let first_tx_num = tx.get_block_meta(start_block)?.first_tx_num();
// Look up the end index for transaction range (inclusive) // Look up the end index for transaction range (inclusive)
let end_tx_index = tx.get_block_body(end_block)?.last_tx_index(); let last_tx_num = tx.get_block_meta(end_block)?.last_tx_num();
// No transactions to walk over // No transactions to walk over
if start_tx_index > end_tx_index { if first_tx_num > last_tx_num {
info!(target: "sync::stages::sender_recovery", start_tx_index, end_tx_index, "Target transaction already reached"); info!(target: "sync::stages::sender_recovery", first_tx_num, last_tx_num, "Target transaction already reached");
return Ok(ExecOutput { stage_progress: end_block, done }) return Ok(ExecOutput { stage_progress: end_block, done })
} }
@ -74,10 +74,10 @@ impl<DB: Database> Stage<DB> for SenderRecoveryStage {
// Acquire the cursor over the transactions // Acquire the cursor over the transactions
let mut tx_cursor = tx.cursor_read::<tables::Transactions>()?; let mut tx_cursor = tx.cursor_read::<tables::Transactions>()?;
// Walk the transactions from start to end index (inclusive) // Walk the transactions from start to end index (inclusive)
let tx_walker = tx_cursor.walk_range(start_tx_index..=end_tx_index)?; let tx_walker = tx_cursor.walk_range(first_tx_num..=last_tx_num)?;
// Iterate over transactions in chunks // Iterate over transactions in chunks
info!(target: "sync::stages::sender_recovery", start_tx_index, end_tx_index, "Recovering senders"); info!(target: "sync::stages::sender_recovery", first_tx_num, last_tx_num, "Recovering senders");
// channels used to return result of sender recovery. // channels used to return result of sender recovery.
let mut channels = Vec::new(); let mut channels = Vec::new();
@ -136,7 +136,7 @@ impl<DB: Database> Stage<DB> for SenderRecoveryStage {
) -> Result<UnwindOutput, StageError> { ) -> Result<UnwindOutput, StageError> {
info!(target: "sync::stages::sender_recovery", to_block = input.unwind_to, "Unwinding"); info!(target: "sync::stages::sender_recovery", to_block = input.unwind_to, "Unwinding");
// Lookup latest tx id that we should unwind to // Lookup latest tx id that we should unwind to
let latest_tx_id = tx.get_block_body(input.unwind_to)?.last_tx_index(); let latest_tx_id = tx.get_block_meta(input.unwind_to)?.last_tx_num();
tx.unwind_table_by_num::<tables::TxSenders>(latest_tx_id)?; tx.unwind_table_by_num::<tables::TxSenders>(latest_tx_id)?;
Ok(UnwindOutput { stage_progress: input.unwind_to }) Ok(UnwindOutput { stage_progress: input.unwind_to })
} }
@ -267,13 +267,11 @@ mod tests {
/// 2. If the is no requested block entry in the bodies table, /// 2. If the is no requested block entry in the bodies table,
/// but [tables::TxSenders] is not empty. /// but [tables::TxSenders] is not empty.
fn ensure_no_senders_by_block(&self, block: BlockNumber) -> Result<(), TestRunnerError> { fn ensure_no_senders_by_block(&self, block: BlockNumber) -> Result<(), TestRunnerError> {
let body_result = self.tx.inner().get_block_body(block); let body_result = self.tx.inner().get_block_meta(block);
match body_result { match body_result {
Ok(body) => self Ok(body) => self
.tx .tx
.ensure_no_entry_above::<tables::TxSenders, _>(body.last_tx_index(), |key| { .ensure_no_entry_above::<tables::TxSenders, _>(body.last_tx_num(), |key| key)?,
key
})?,
Err(_) => { Err(_) => {
assert!(self.tx.table_is_empty::<tables::TxSenders>()?); assert!(self.tx.table_is_empty::<tables::TxSenders>()?);
} }
@ -321,11 +319,11 @@ mod tests {
return Ok(()) return Ok(())
} }
let mut body_cursor = tx.cursor_read::<tables::BlockBodies>()?; let mut body_cursor = tx.cursor_read::<tables::BlockBodyIndices>()?;
body_cursor.seek_exact(start_block)?; body_cursor.seek_exact(start_block)?;
while let Some((_, body)) = body_cursor.next()? { while let Some((_, body)) = body_cursor.next()? {
for tx_id in body.tx_id_range() { for tx_id in body.tx_num_range() {
let transaction = tx let transaction = tx
.get::<tables::Transactions>(tx_id)? .get::<tables::Transactions>(tx_id)?
.expect("no transaction entry"); .expect("no transaction entry");

View File

@ -56,19 +56,17 @@ impl<DB: Database> Stage<DB> for TransactionLookupStage {
debug!(target: "sync::stages::transaction_lookup", start_block, end_block, "Commencing sync"); debug!(target: "sync::stages::transaction_lookup", start_block, end_block, "Commencing sync");
let mut cursor_bodies = tx.cursor_read::<tables::BlockBodies>()?; let mut block_meta_cursor = tx.cursor_read::<tables::BlockBodyIndices>()?;
let mut tx_cursor = tx.cursor_write::<tables::Transactions>()?; let mut tx_cursor = tx.cursor_read::<tables::Transactions>()?;
// Walk over block bodies within a specified range. // Walk over block bodies within a specified range.
let bodies = cursor_bodies.walk(Some(start_block))?.take_while(|entry| { let bodies = block_meta_cursor.walk_range(start_block..=end_block)?;
entry.as_ref().map(|(num, _)| *num <= end_block).unwrap_or_default()
});
// Collect transactions for each body // Collect transactions for each body
let mut tx_list = vec![]; let mut tx_list = vec![];
for body_entry in bodies { for block_meta_entry in bodies {
let (_, body) = body_entry?; let (_, block_meta) = block_meta_entry?;
let transactions = tx_cursor.walk(Some(body.start_tx_id))?.take(body.tx_count as usize); let transactions = tx_cursor.walk_range(block_meta.tx_num_range())?;
for tx_entry in transactions { for tx_entry in transactions {
let (id, transaction) = tx_entry?; let (id, transaction) = tx_entry?;
@ -81,20 +79,23 @@ impl<DB: Database> Stage<DB> for TransactionLookupStage {
let mut txhash_cursor = tx.cursor_write::<tables::TxHashNumber>()?; let mut txhash_cursor = tx.cursor_write::<tables::TxHashNumber>()?;
// If the last inserted element in the database is smaller than the first in our set, then // If the last inserted element in the database is equal or bigger than the first
// we can just append into the DB. This probably only ever happens during sync, on // in our set, then we need to insert inside the DB. If it is smaller then last
// the first table insertion. // element in the DB, we can append to the DB.
let append = tx_list // Append probably only ever happens during sync, on the first table insertion.
let insert = tx_list
.first() .first()
.zip(txhash_cursor.last()?) .zip(txhash_cursor.last()?)
.map(|((first, _), (last, _))| &last < first) .map(|((first, _), (last, _))| first <= &last)
.unwrap_or_default(); .unwrap_or_default();
// if txhash_cursor.last() is None we will do insert. `zip` would return none if any item is
// none. if it is some and if first is smaller than last, we will do append.
for (tx_hash, id) in tx_list { for (tx_hash, id) in tx_list {
if append { if insert {
txhash_cursor.append(tx_hash, id)?;
} else {
txhash_cursor.insert(tx_hash, id)?; txhash_cursor.insert(tx_hash, id)?;
} else {
txhash_cursor.append(tx_hash, id)?;
} }
} }
@ -111,7 +112,7 @@ impl<DB: Database> Stage<DB> for TransactionLookupStage {
) -> Result<UnwindOutput, StageError> { ) -> Result<UnwindOutput, StageError> {
info!(target: "sync::stages::transaction_lookup", to_block = input.unwind_to, "Unwinding"); info!(target: "sync::stages::transaction_lookup", to_block = input.unwind_to, "Unwinding");
// Cursors to unwind tx hash to number // Cursors to unwind tx hash to number
let mut body_cursor = tx.cursor_read::<tables::BlockBodies>()?; let mut body_cursor = tx.cursor_read::<tables::BlockBodyIndices>()?;
let mut tx_hash_number_cursor = tx.cursor_write::<tables::TxHashNumber>()?; let mut tx_hash_number_cursor = tx.cursor_write::<tables::TxHashNumber>()?;
let mut transaction_cursor = tx.cursor_read::<tables::Transactions>()?; let mut transaction_cursor = tx.cursor_read::<tables::Transactions>()?;
let mut rev_walker = body_cursor.walk_back(None)?; let mut rev_walker = body_cursor.walk_back(None)?;
@ -121,7 +122,7 @@ impl<DB: Database> Stage<DB> for TransactionLookupStage {
} }
// Delete all transactions that belong to this block // Delete all transactions that belong to this block
for tx_id in body.tx_id_range() { for tx_id in body.tx_num_range() {
// First delete the transaction and hash to id mapping // First delete the transaction and hash to id mapping
if let Some((_, transaction)) = transaction_cursor.seek_exact(tx_id)? { if let Some((_, transaction)) = transaction_cursor.seek_exact(tx_id)? {
if tx_hash_number_cursor.seek_exact(transaction.hash)?.is_some() { if tx_hash_number_cursor.seek_exact(transaction.hash)?.is_some() {
@ -162,7 +163,7 @@ mod tests {
// Insert blocks with a single transaction at block `stage_progress + 10` // Insert blocks with a single transaction at block `stage_progress + 10`
let non_empty_block_number = stage_progress + 10; let non_empty_block_number = stage_progress + 10;
let blocks = (stage_progress..input.previous_stage_progress() + 1) let blocks = (stage_progress..=input.previous_stage_progress())
.map(|number| { .map(|number| {
random_block(number, None, Some((number == non_empty_block_number) as u8), None) random_block(number, None, Some((number == non_empty_block_number) as u8), None)
}) })
@ -246,10 +247,10 @@ mod tests {
/// 2. If the is no requested block entry in the bodies table, /// 2. If the is no requested block entry in the bodies table,
/// but [tables::TxHashNumber] is not empty. /// but [tables::TxHashNumber] is not empty.
fn ensure_no_hash_by_block(&self, number: BlockNumber) -> Result<(), TestRunnerError> { fn ensure_no_hash_by_block(&self, number: BlockNumber) -> Result<(), TestRunnerError> {
let body_result = self.tx.inner().get_block_body(number); let body_result = self.tx.inner().get_block_meta(number);
match body_result { match body_result {
Ok(body) => self.tx.ensure_no_entry_above_by_value::<tables::TxHashNumber, _>( Ok(body) => self.tx.ensure_no_entry_above_by_value::<tables::TxHashNumber, _>(
body.last_tx_index(), body.last_tx_num(),
|key| key, |key| key,
)?, )?,
Err(_) => { Err(_) => {
@ -299,11 +300,11 @@ mod tests {
return Ok(()) return Ok(())
} }
let mut body_cursor = tx.cursor_read::<tables::BlockBodies>()?; let mut body_cursor = tx.cursor_read::<tables::BlockBodyIndices>()?;
body_cursor.seek_exact(start_block)?; body_cursor.seek_exact(start_block)?;
while let Some((_, body)) = body_cursor.next()? { while let Some((_, body)) = body_cursor.next()? {
for tx_id in body.tx_id_range() { for tx_id in body.tx_num_range() {
let transaction = tx let transaction = tx
.get::<tables::Transactions>(tx_id)? .get::<tables::Transactions>(tx_id)?
.expect("no transaction entry"); .expect("no transaction entry");

View File

@ -6,7 +6,7 @@ use reth_db::{
tx::Tx, tx::Tx,
Env, EnvKind, WriteMap, RW, Env, EnvKind, WriteMap, RW,
}, },
models::{AccountBeforeTx, StoredBlockBody}, models::{AccountBeforeTx, StoredBlockBodyIndices},
table::Table, table::Table,
tables, tables,
transaction::{DbTx, DbTxMut}, transaction::{DbTx, DbTxMut},
@ -230,25 +230,25 @@ impl TestTransaction {
I: Iterator<Item = &'a SealedBlock>, I: Iterator<Item = &'a SealedBlock>,
{ {
self.commit(|tx| { self.commit(|tx| {
let mut current_tx_id = tx_offset.unwrap_or_default(); let mut next_tx_num = tx_offset.unwrap_or_default();
blocks.into_iter().try_for_each(|block| { blocks.into_iter().try_for_each(|block| {
Self::insert_header(tx, &block.header)?; Self::insert_header(tx, &block.header)?;
// Insert into body tables. // Insert into body tables.
tx.put::<tables::BlockBodies>( tx.put::<tables::BlockBodyIndices>(
block.number, block.number,
StoredBlockBody { StoredBlockBodyIndices {
start_tx_id: current_tx_id, first_tx_num: next_tx_num,
first_transition_id: next_tx_num,
tx_count: block.body.len() as u64, tx_count: block.body.len() as u64,
has_block_change: false,
}, },
)?; )?;
block.body.iter().try_for_each(|body_tx| { block.body.iter().try_for_each(|body_tx| {
tx.put::<tables::TxTransitionIndex>(current_tx_id, current_tx_id)?; tx.put::<tables::Transactions>(next_tx_num, body_tx.clone())?;
tx.put::<tables::Transactions>(current_tx_id, body_tx.clone())?; next_tx_num += 1;
current_tx_id += 1;
Ok(()) Ok(())
})?; })
tx.put::<tables::BlockTransitionIndex>(block.number, current_tx_id)
}) })
}) })
} }

View File

@ -162,7 +162,7 @@ pub fn get_bit_size(ftype: &str) -> u8 {
match ftype { match ftype {
"bool" | "Option" => 1, "bool" | "Option" => 1,
"TxType" => 2, "TxType" => 2,
"u64" | "BlockNumber" | "TxNumber" | "ChainId" => 4, "u64" | "BlockNumber" | "TxNumber" | "ChainId" | "TransitionId" | "NumTransactions" => 4,
"u128" => 5, "u128" => 5,
"U256" | "TxHash" => 6, "U256" | "TxHash" => 6,
_ => 0, _ => 0,

View File

@ -26,11 +26,9 @@ pub fn db(c: &mut Criterion) {
measure_table_db::<HeaderTD>(&mut group); measure_table_db::<HeaderTD>(&mut group);
measure_table_db::<HeaderNumbers>(&mut group); measure_table_db::<HeaderNumbers>(&mut group);
measure_table_db::<Headers>(&mut group); measure_table_db::<Headers>(&mut group);
measure_table_db::<BlockBodies>(&mut group); measure_table_db::<BlockBodyIndices>(&mut group);
measure_table_db::<BlockOmmers>(&mut group); measure_table_db::<BlockOmmers>(&mut group);
measure_table_db::<TxHashNumber>(&mut group); measure_table_db::<TxHashNumber>(&mut group);
measure_table_db::<BlockTransitionIndex>(&mut group);
measure_table_db::<TxTransitionIndex>(&mut group);
measure_table_db::<Transactions>(&mut group); measure_table_db::<Transactions>(&mut group);
measure_dupsort_db::<PlainStorageState>(&mut group); measure_dupsort_db::<PlainStorageState>(&mut group);
measure_table_db::<PlainAccountState>(&mut group); measure_table_db::<PlainAccountState>(&mut group);
@ -45,11 +43,9 @@ pub fn serialization(c: &mut Criterion) {
measure_table_serialization::<HeaderTD>(&mut group); measure_table_serialization::<HeaderTD>(&mut group);
measure_table_serialization::<HeaderNumbers>(&mut group); measure_table_serialization::<HeaderNumbers>(&mut group);
measure_table_serialization::<Headers>(&mut group); measure_table_serialization::<Headers>(&mut group);
measure_table_serialization::<BlockBodies>(&mut group); measure_table_serialization::<BlockBodyIndices>(&mut group);
measure_table_serialization::<BlockOmmers>(&mut group); measure_table_serialization::<BlockOmmers>(&mut group);
measure_table_serialization::<TxHashNumber>(&mut group); measure_table_serialization::<TxHashNumber>(&mut group);
measure_table_serialization::<BlockTransitionIndex>(&mut group);
measure_table_serialization::<TxTransitionIndex>(&mut group);
measure_table_serialization::<Transactions>(&mut group); measure_table_serialization::<Transactions>(&mut group);
measure_table_serialization::<PlainStorageState>(&mut group); measure_table_serialization::<PlainStorageState>(&mut group);
measure_table_serialization::<PlainAccountState>(&mut group); measure_table_serialization::<PlainAccountState>(&mut group);

View File

@ -68,11 +68,9 @@ impl_iai!(
HeaderTD, HeaderTD,
HeaderNumbers, HeaderNumbers,
Headers, Headers,
BlockBodies, BlockBodyIndices,
BlockOmmers, BlockOmmers,
TxHashNumber, TxHashNumber,
BlockTransitionIndex,
TxTransitionIndex,
Transactions, Transactions,
PlainStorageState, PlainStorageState,
PlainAccountState PlainAccountState

View File

@ -41,7 +41,7 @@ impl_compression_for_compact!(
TxType, TxType,
StorageEntry, StorageEntry,
StorageTrieEntry, StorageTrieEntry,
StoredBlockBody, StoredBlockBodyIndices,
StoredBlockOmmers, StoredBlockOmmers,
StoredBlockWithdrawals, StoredBlockWithdrawals,
Bytecode, Bytecode,

View File

@ -25,7 +25,7 @@ use crate::{
accounts::{AccountBeforeTx, TransitionIdAddress}, accounts::{AccountBeforeTx, TransitionIdAddress},
blocks::{HeaderHash, StoredBlockOmmers}, blocks::{HeaderHash, StoredBlockOmmers},
storage_sharded_key::StorageShardedKey, storage_sharded_key::StorageShardedKey,
ShardedKey, StoredBlockBody, StoredBlockWithdrawals, ShardedKey, StoredBlockBodyIndices, StoredBlockWithdrawals,
}, },
}, },
}; };
@ -44,12 +44,12 @@ pub enum TableType {
} }
/// Default tables that should be present inside database. /// Default tables that should be present inside database.
pub const TABLES: [(TableType, &str); 27] = [ pub const TABLES: [(TableType, &str); 25] = [
(TableType::Table, CanonicalHeaders::const_name()), (TableType::Table, CanonicalHeaders::const_name()),
(TableType::Table, HeaderTD::const_name()), (TableType::Table, HeaderTD::const_name()),
(TableType::Table, HeaderNumbers::const_name()), (TableType::Table, HeaderNumbers::const_name()),
(TableType::Table, Headers::const_name()), (TableType::Table, Headers::const_name()),
(TableType::Table, BlockBodies::const_name()), (TableType::Table, BlockBodyIndices::const_name()),
(TableType::Table, BlockOmmers::const_name()), (TableType::Table, BlockOmmers::const_name()),
(TableType::Table, BlockWithdrawals::const_name()), (TableType::Table, BlockWithdrawals::const_name()),
(TableType::Table, TransactionBlock::const_name()), (TableType::Table, TransactionBlock::const_name()),
@ -59,8 +59,6 @@ pub const TABLES: [(TableType, &str); 27] = [
(TableType::Table, PlainAccountState::const_name()), (TableType::Table, PlainAccountState::const_name()),
(TableType::DupSort, PlainStorageState::const_name()), (TableType::DupSort, PlainStorageState::const_name()),
(TableType::Table, Bytecodes::const_name()), (TableType::Table, Bytecodes::const_name()),
(TableType::Table, BlockTransitionIndex::const_name()),
(TableType::Table, TxTransitionIndex::const_name()),
(TableType::Table, AccountHistory::const_name()), (TableType::Table, AccountHistory::const_name()),
(TableType::Table, StorageHistory::const_name()), (TableType::Table, StorageHistory::const_name()),
(TableType::DupSort, AccountChangeSet::const_name()), (TableType::DupSort, AccountChangeSet::const_name()),
@ -146,8 +144,11 @@ table!(
); );
table!( table!(
/// Stores block bodies. /// Stores block indices that contains indexes of transaction and transitions,
( BlockBodies ) BlockNumber | StoredBlockBody /// number of transactions and if block has a block change (block reward or withdrawals).
///
/// More information about stored indices can be found in the [`StoredBlockBodyIndices`] struct.
( BlockBodyIndices ) BlockNumber | StoredBlockBodyIndices
); );
table!( table!(
@ -190,20 +191,6 @@ table!(
( Bytecodes ) H256 | Bytecode ( Bytecodes ) H256 | Bytecode
); );
table!(
/// Stores the mapping of block number to state transition id.
/// The block transition marks the final state at the end of the block.
/// Increment the transition if the block contains an addition block reward.
/// If the block does not have a reward and transaction, the transition will be the same as the
/// transition at the last transaction of this block.
( BlockTransitionIndex ) BlockNumber | TransitionId
);
table!(
/// Stores the mapping of transaction number to state transition id.
( TxTransitionIndex ) TxNumber | TransitionId
);
table!( table!(
/// Stores the current state of an [`Account`]. /// Stores the current state of an [`Account`].
( PlainAccountState ) Address | Account ( PlainAccountState ) Address | Account

View File

@ -1,40 +1,89 @@
//! Block related models and types. //! Block related models and types.
use reth_codecs::{main_codec, Compact}; use reth_codecs::{main_codec, Compact};
use reth_primitives::{Header, TxNumber, Withdrawal, H256}; use reth_primitives::{Header, TransitionId, TxNumber, Withdrawal, H256};
use std::ops::Range; use std::ops::Range;
/// Total number of transactions. /// Total number of transactions.
pub type NumTransactions = u64; pub type NumTransactions = u64;
/// The storage representation of a block. /// The storage of the block body indices
/// ///
/// It has the pointer to the transaction Number of the first /// It has the pointer to the transaction Number of the first
/// transaction in the block and the total number of transactions /// transaction in the block and the total number of transactions
#[derive(Debug, Default, Eq, PartialEq, Clone)] #[derive(Debug, Default, Eq, PartialEq, Clone)]
#[main_codec] #[main_codec]
pub struct StoredBlockBody { pub struct StoredBlockBodyIndices {
/// The id of the first transaction in this block /// The number of the first transaction in this block
pub start_tx_id: TxNumber, ///
/// Note: If the block is empty, this is the number of the first transaction
/// in the next non-empty block.
pub first_tx_num: TxNumber,
/// The id of the first transition in this block.
///
/// NOTE: If the block is empty, this is the id of the first transition
/// in the next non-empty block.
pub first_transition_id: TransitionId,
/// The total number of transactions in the block /// The total number of transactions in the block
///
/// NOTE: Number of transitions is equal to number of transactions with
/// additional transition for block change if block has block reward or withdrawal.
pub tx_count: NumTransactions, pub tx_count: NumTransactions,
/// Flags if there is additional transition changeset of the withdrawal or block reward.
pub has_block_change: bool,
} }
impl StoredBlockBody { impl StoredBlockBodyIndices {
/// Return the range of transaction ids for this body /// Return the range of transaction ids for this block.
pub fn tx_id_range(&self) -> Range<u64> { pub fn tx_num_range(&self) -> Range<TxNumber> {
self.start_tx_id..self.start_tx_id + self.tx_count self.first_tx_num..self.first_tx_num + self.tx_count
}
/// Return the range of transition ids for this block.
pub fn transition_range(&self) -> Range<TransitionId> {
self.transition_at_block()..self.transition_after_block()
}
/// Return transition id of the state after block executed.
/// This transitions is used with the history index to represent the state after this
/// block execution.
///
/// Because we are storing old values of the changeset in the history index, we need
/// transition of one after, to fetch correct values of the past state
///
/// NOTE: This is the same as the first transition id of the next block.
pub fn transition_after_block(&self) -> TransitionId {
self.first_transition_id + self.tx_count + (self.has_block_change as u64)
}
/// Return transition id of the state at the block execution.
/// This transitions is used with the history index to represent the state
/// before the block execution.
///
/// Because we are storing old values of the changeset in the history index, we need
/// transition of one after, to fetch correct values of the past state
///
/// NOTE: If block does not have transitions (empty block) then this is the same
/// as the first transition id of the next block.
pub fn transition_at_block(&self) -> TransitionId {
self.first_transition_id
} }
/// Return the index of last transaction in this block unless the block /// Return the index of last transaction in this block unless the block
/// is empty in which case it refers to the last transaction in a previous /// is empty in which case it refers to the last transaction in a previous
/// non-empty block /// non-empty block
pub fn last_tx_index(&self) -> TxNumber { pub fn last_tx_num(&self) -> TxNumber {
self.start_tx_id.saturating_add(self.tx_count).saturating_sub(1) self.first_tx_num.saturating_add(self.tx_count).saturating_sub(1)
} }
/// First transaction index. /// First transaction index.
pub fn first_tx_index(&self) -> TxNumber { pub fn first_tx_num(&self) -> TxNumber {
self.start_tx_id self.first_tx_num
}
/// Return the index of the next transaction after this block.
pub fn next_tx_num(&self) -> TxNumber {
self.first_tx_num + self.tx_count
} }
/// Return a flag whether the block is empty /// Return a flag whether the block is empty
@ -43,9 +92,17 @@ impl StoredBlockBody {
} }
/// Return number of transaction inside block /// Return number of transaction inside block
///
/// NOTE: This is not the same as the number of transitions.
pub fn tx_count(&self) -> NumTransactions { pub fn tx_count(&self) -> NumTransactions {
self.tx_count self.tx_count
} }
/// Return flag signifying whether the block has additional
/// transition changeset (withdrawal or uncle/block rewards).
pub fn has_block_change(&self) -> bool {
self.has_block_change
}
} }
/// The storage representation of a block ommers. /// The storage representation of a block ommers.
@ -72,9 +129,8 @@ pub type HeaderHash = H256;
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::table::{Compress, Decompress};
use super::*; use super::*;
use crate::table::{Compress, Decompress};
#[test] #[test]
fn test_ommer() { fn test_ommer() {
@ -85,4 +141,33 @@ mod test {
ommer.clone() == StoredBlockOmmers::decompress::<Vec<_>>(ommer.compress()).unwrap() ommer.clone() == StoredBlockOmmers::decompress::<Vec<_>>(ommer.compress()).unwrap()
); );
} }
#[test]
fn block_meta_indices() {
let first_tx_num = 10;
let first_transition_id = 14;
let tx_count = 6;
let has_block_change = true;
let mut block_meta = StoredBlockBodyIndices {
first_tx_num,
first_transition_id,
tx_count,
has_block_change,
};
assert_eq!(block_meta.first_tx_num(), first_tx_num);
assert_eq!(block_meta.last_tx_num(), first_tx_num + tx_count - 1);
assert_eq!(block_meta.next_tx_num(), first_tx_num + tx_count);
assert_eq!(block_meta.tx_count(), tx_count);
assert!(block_meta.has_block_change());
assert_eq!(block_meta.transition_at_block(), first_transition_id);
assert_eq!(block_meta.transition_after_block(), first_transition_id + tx_count + 1);
assert_eq!(block_meta.tx_num_range(), first_tx_num..first_tx_num + tx_count);
assert_eq!(
block_meta.transition_range(),
first_transition_id..first_transition_id + tx_count + 1
);
block_meta.has_block_change = false;
assert_eq!(block_meta.transition_after_block(), first_transition_id + tx_count);
}
} }

View File

@ -233,12 +233,11 @@ fn test_concurrent_readers_single_writer() {
let txn = env.begin_rw_txn().unwrap(); let txn = env.begin_rw_txn().unwrap();
let db = txn.open_db(None).unwrap(); let db = txn.open_db(None).unwrap();
println!("wait2");
barrier.wait(); barrier.wait();
txn.put(&db, key, val, WriteFlags::empty()).unwrap(); txn.put(&db, key, val, WriteFlags::empty()).unwrap();
txn.commit().unwrap(); txn.commit().unwrap();
println!("wait1");
barrier.wait(); barrier.wait();
assert!(threads.into_iter().all(|b| b.join().unwrap())) assert!(threads.into_iter().all(|b| b.join().unwrap()))

View File

@ -132,8 +132,9 @@ impl<DB: Database> BlockProvider for ShareableDatabase<DB> {
if let Some(header) = self.header_by_number(number)? { if let Some(header) = self.header_by_number(number)? {
let id = BlockId::Number(number.into()); let id = BlockId::Number(number.into());
let tx = self.db.tx()?; let tx = self.db.tx()?;
let transactions = let transactions = self
self.transactions_by_block(id)?.ok_or(ProviderError::BlockBody { number })?; .transactions_by_block(id)?
.ok_or(ProviderError::BlockBodyIndices { number })?;
let ommers = tx.get::<tables::BlockOmmers>(header.number)?.map(|o| o.ommers); let ommers = tx.get::<tables::BlockOmmers>(header.number)?.map(|o| o.ommers);
let withdrawals = self.withdrawals_by_block(id, header.timestamp)?; let withdrawals = self.withdrawals_by_block(id, header.timestamp)?;
@ -200,13 +201,13 @@ impl<DB: Database> TransactionsProvider for ShareableDatabase<DB> {
tx.get::<tables::CanonicalHeaders>(block_number)? tx.get::<tables::CanonicalHeaders>(block_number)?
{ {
if let Some(block_body) = if let Some(block_body) =
tx.get::<tables::BlockBodies>(block_number)? tx.get::<tables::BlockBodyIndices>(block_number)?
{ {
// the index of the tx in the block is the offset: // the index of the tx in the block is the offset:
// len([start..tx_id]) // len([start..tx_id])
// SAFETY: `transaction_id` is always `>=` the block's first // SAFETY: `transaction_id` is always `>=` the block's first
// index // index
let index = transaction_id - block_body.first_tx_index(); let index = transaction_id - block_body.first_tx_num();
let meta = TransactionMeta { let meta = TransactionMeta {
tx_hash, tx_hash,
@ -239,8 +240,8 @@ impl<DB: Database> TransactionsProvider for ShareableDatabase<DB> {
fn transactions_by_block(&self, id: BlockId) -> Result<Option<Vec<TransactionSigned>>> { fn transactions_by_block(&self, id: BlockId) -> Result<Option<Vec<TransactionSigned>>> {
if let Some(number) = self.block_number_for_id(id)? { if let Some(number) = self.block_number_for_id(id)? {
let tx = self.db.tx()?; let tx = self.db.tx()?;
if let Some(body) = tx.get::<tables::BlockBodies>(number)? { if let Some(body) = tx.get::<tables::BlockBodyIndices>(number)? {
let tx_range = body.tx_id_range(); let tx_range = body.tx_num_range();
return if tx_range.is_empty() { return if tx_range.is_empty() {
Ok(Some(Vec::new())) Ok(Some(Vec::new()))
} else { } else {
@ -262,17 +263,17 @@ impl<DB: Database> TransactionsProvider for ShareableDatabase<DB> {
) -> Result<Vec<Vec<TransactionSigned>>> { ) -> Result<Vec<Vec<TransactionSigned>>> {
let tx = self.db.tx()?; let tx = self.db.tx()?;
let mut results = Vec::default(); let mut results = Vec::default();
let mut body_cursor = tx.cursor_read::<tables::BlockBodies>()?; let mut body_cursor = tx.cursor_read::<tables::BlockBodyIndices>()?;
let mut tx_cursor = tx.cursor_read::<tables::Transactions>()?; let mut tx_cursor = tx.cursor_read::<tables::Transactions>()?;
for entry in body_cursor.walk_range(range)? { for entry in body_cursor.walk_range(range)? {
let (_, body) = entry?; let (_, body) = entry?;
let tx_range = body.tx_id_range(); let tx_num_range = body.tx_num_range();
if body.tx_id_range().is_empty() { if tx_num_range.is_empty() {
results.push(Vec::default()); results.push(Vec::default());
} else { } else {
results.push( results.push(
tx_cursor tx_cursor
.walk_range(tx_range)? .walk_range(tx_num_range)?
.map(|result| result.map(|(_, tx)| tx)) .map(|result| result.map(|(_, tx)| tx))
.collect::<std::result::Result<Vec<_>, _>>()?, .collect::<std::result::Result<Vec<_>, _>>()?,
); );
@ -302,8 +303,8 @@ impl<DB: Database> ReceiptProvider for ShareableDatabase<DB> {
fn receipts_by_block(&self, block: BlockId) -> Result<Option<Vec<Receipt>>> { fn receipts_by_block(&self, block: BlockId) -> Result<Option<Vec<Receipt>>> {
if let Some(number) = self.block_number_for_id(block)? { if let Some(number) = self.block_number_for_id(block)? {
let tx = self.db.tx()?; let tx = self.db.tx()?;
if let Some(body) = tx.get::<tables::BlockBodies>(number)? { if let Some(body) = tx.get::<tables::BlockBodyIndices>(number)? {
let tx_range = body.tx_id_range(); let tx_range = body.tx_num_range();
return if tx_range.is_empty() { return if tx_range.is_empty() {
Ok(Some(Vec::new())) Ok(Some(Vec::new()))
} else { } else {
@ -419,8 +420,9 @@ impl<DB: Database> StateProviderFactory for ShareableDatabase<DB> {
// get transition id // get transition id
let transition = tx let transition = tx
.get::<tables::BlockTransitionIndex>(block_number)? .get::<tables::BlockBodyIndices>(block_number)?
.ok_or(ProviderError::BlockTransition { block_number })?; .ok_or(ProviderError::BlockTransition { block_number })?
.transition_after_block();
Ok(Box::new(HistoricalStateProvider::new(tx, transition))) Ok(Box::new(HistoricalStateProvider::new(tx, transition)))
} }
@ -434,8 +436,9 @@ impl<DB: Database> StateProviderFactory for ShareableDatabase<DB> {
// get transition id // get transition id
let transition = tx let transition = tx
.get::<tables::BlockTransitionIndex>(block_number)? .get::<tables::BlockBodyIndices>(block_number)?
.ok_or(ProviderError::BlockTransition { block_number })?; .ok_or(ProviderError::BlockTransition { block_number })?
.transition_after_block();
Ok(Box::new(HistoricalStateProvider::new(tx, transition))) Ok(Box::new(HistoricalStateProvider::new(tx, transition)))
} }

View File

@ -1,7 +1,7 @@
//! Dummy blocks and data for tests //! Dummy blocks and data for tests
use crate::{post_state::PostState, Transaction}; use crate::{post_state::PostState, Transaction};
use reth_db::{database::Database, models::StoredBlockBody, tables}; use reth_db::{database::Database, models::StoredBlockBodyIndices, tables};
use reth_primitives::{ use reth_primitives::{
hex_literal::hex, proofs::EMPTY_ROOT, Account, Header, SealedBlock, SealedBlockWithSenders, hex_literal::hex, proofs::EMPTY_ROOT, Account, Header, SealedBlock, SealedBlockWithSenders,
Withdrawal, H160, H256, U256, Withdrawal, H160, H256, U256,
@ -19,7 +19,10 @@ pub fn assert_genesis_block<DB: Database>(tx: &Transaction<'_, DB>, g: SealedBlo
assert_eq!(tx.table::<tables::HeaderNumbers>().unwrap(), vec![(h, n)]); assert_eq!(tx.table::<tables::HeaderNumbers>().unwrap(), vec![(h, n)]);
assert_eq!(tx.table::<tables::CanonicalHeaders>().unwrap(), vec![(n, h)]); assert_eq!(tx.table::<tables::CanonicalHeaders>().unwrap(), vec![(n, h)]);
assert_eq!(tx.table::<tables::HeaderTD>().unwrap(), vec![(n, g.difficulty.into())]); assert_eq!(tx.table::<tables::HeaderTD>().unwrap(), vec![(n, g.difficulty.into())]);
assert_eq!(tx.table::<tables::BlockBodies>().unwrap(), vec![(0, StoredBlockBody::default())]); assert_eq!(
tx.table::<tables::BlockBodyIndices>().unwrap(),
vec![(0, StoredBlockBodyIndices::default())]
);
assert_eq!(tx.table::<tables::BlockOmmers>().unwrap(), vec![]); assert_eq!(tx.table::<tables::BlockOmmers>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::BlockWithdrawals>().unwrap(), vec![]); assert_eq!(tx.table::<tables::BlockWithdrawals>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::Transactions>().unwrap(), vec![]); assert_eq!(tx.table::<tables::Transactions>().unwrap(), vec![]);
@ -32,8 +35,6 @@ pub fn assert_genesis_block<DB: Database>(tx: &Transaction<'_, DB>, g: SealedBlo
assert_eq!(tx.table::<tables::StorageHistory>().unwrap(), vec![]); assert_eq!(tx.table::<tables::StorageHistory>().unwrap(), vec![]);
// TODO check after this gets done: https://github.com/paradigmxyz/reth/issues/1588 // TODO check after this gets done: https://github.com/paradigmxyz/reth/issues/1588
// Bytecodes are not reverted assert_eq!(tx.table::<tables::Bytecodes>().unwrap(), vec![]); // Bytecodes are not reverted assert_eq!(tx.table::<tables::Bytecodes>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::BlockTransitionIndex>().unwrap(), vec![(n, 0)]);
assert_eq!(tx.table::<tables::TxTransitionIndex>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::AccountChangeSet>().unwrap(), vec![]); assert_eq!(tx.table::<tables::AccountChangeSet>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::StorageChangeSet>().unwrap(), vec![]); assert_eq!(tx.table::<tables::StorageChangeSet>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::HashedAccount>().unwrap(), vec![]); assert_eq!(tx.table::<tables::HashedAccount>().unwrap(), vec![]);

View File

@ -11,7 +11,7 @@ use reth_db::{
models::{ models::{
sharded_key, sharded_key,
storage_sharded_key::{self, StorageShardedKey}, storage_sharded_key::{self, StorageShardedKey},
AccountBeforeTx, ShardedKey, StoredBlockBody, TransitionIdAddress, AccountBeforeTx, ShardedKey, StoredBlockBodyIndices, TransitionIdAddress,
}, },
table::Table, table::Table,
tables, tables,
@ -140,17 +140,22 @@ where
} }
/// Query the block body by number. /// Query the block body by number.
pub fn get_block_body(&self, number: BlockNumber) -> Result<StoredBlockBody, TransactionError> { pub fn get_block_meta(
let body = &self,
self.get::<tables::BlockBodies>(number)?.ok_or(ProviderError::BlockBody { number })?; number: BlockNumber,
) -> Result<StoredBlockBodyIndices, TransactionError> {
let body = self
.get::<tables::BlockBodyIndices>(number)?
.ok_or(ProviderError::BlockBodyIndices { number })?;
Ok(body) Ok(body)
} }
/// Query the last transition of the block by [BlockNumber] key /// Query the last transition of the block by [BlockNumber] key
pub fn get_block_transition(&self, key: BlockNumber) -> Result<TransitionId, TransactionError> { pub fn get_block_transition(&self, key: BlockNumber) -> Result<TransitionId, TransactionError> {
let last_transition_id = self let last_transition_id = self
.get::<tables::BlockTransitionIndex>(key)? .get::<tables::BlockBodyIndices>(key)?
.ok_or(ProviderError::BlockTransition { block_number: key })?; .ok_or(ProviderError::BlockTransition { block_number: key })?
.transition_after_block();
Ok(last_transition_id) Ok(last_transition_id)
} }
@ -165,11 +170,8 @@ where
} }
let prev_number = block - 1; let prev_number = block - 1;
let prev_body = self.get_block_body(prev_number)?; let prev_body = self.get_block_meta(prev_number)?;
let last_transition = self Ok((prev_body.first_tx_num + prev_body.tx_count, prev_body.transition_after_block()))
.get::<tables::BlockTransitionIndex>(prev_number)?
.ok_or(ProviderError::BlockTransition { block_number: prev_number })?;
Ok((prev_body.start_tx_id + prev_body.tx_count, last_transition))
} }
/// Query the block header by number /// Query the block header by number
@ -567,9 +569,11 @@ where
// Header, Body, SenderRecovery, TD, TxLookup stages // Header, Body, SenderRecovery, TD, TxLookup stages
let (block, senders) = block.into_components(); let (block, senders) = block.into_components();
let (from, to) = let block_meta =
insert_canonical_block(self.deref_mut(), block, Some(senders), false).unwrap(); insert_canonical_block(self.deref_mut(), block, Some(senders), false).unwrap();
let from = block_meta.transition_at_block();
let to = block_meta.transition_after_block();
// account history stage // account history stage
{ {
let indices = self.get_account_transition_ids_from_changeset(from, to)?; let indices = self.get_account_transition_ids_from_changeset(from, to)?;
@ -664,16 +668,16 @@ where
&self, &self,
range: impl RangeBounds<BlockNumber> + Clone, range: impl RangeBounds<BlockNumber> + Clone,
) -> Result<Vec<(BlockNumber, Vec<TransactionSignedEcRecovered>)>, TransactionError> { ) -> Result<Vec<(BlockNumber, Vec<TransactionSignedEcRecovered>)>, TransactionError> {
// Get the transaction ID ranges for the blocks // Raad range of block bodies to get all transactions id's of this range.
let block_bodies = self.get_or_take::<tables::BlockBodies, false>(range)?; let block_bodies = self.get_or_take::<tables::BlockBodyIndices, false>(range)?;
if block_bodies.is_empty() { if block_bodies.is_empty() {
return Ok(Vec::new()) return Ok(Vec::new())
} }
// Compute the first and last tx ID in the range // Compute the first and last tx ID in the range
let first_transaction = let first_transaction = block_bodies.first().expect("If we have headers").1.first_tx_num();
block_bodies.first().expect("If we have headers").1.first_tx_index(); let last_transaction = block_bodies.last().expect("Not empty").1.last_tx_num();
let last_transaction = block_bodies.last().expect("Not empty").1.last_tx_index();
// If this is the case then all of the blocks in the range are empty // If this is the case then all of the blocks in the range are empty
if last_transaction < first_transaction { if last_transaction < first_transaction {
@ -694,10 +698,6 @@ where
tx_hash_cursor.delete_current()?; tx_hash_cursor.delete_current()?;
} }
} }
// Remove TxTransitionId
self.get_or_take::<tables::TxTransitionIndex, TAKE>(
first_transaction..=last_transaction,
)?;
// Remove TransactionBlock index if there are transaction present // Remove TransactionBlock index if there are transaction present
if !transactions.is_empty() { if !transactions.is_empty() {
@ -712,7 +712,7 @@ where
let mut transactions = transactions.into_iter(); let mut transactions = transactions.into_iter();
for (block_number, block_body) in block_bodies { for (block_number, block_body) in block_bodies {
let mut one_block_tx = Vec::with_capacity(block_body.tx_count as usize); let mut one_block_tx = Vec::with_capacity(block_body.tx_count as usize);
for _ in block_body.tx_id_range() { for _ in block_body.tx_num_range() {
let tx = transactions.next(); let tx = transactions.next();
let sender = senders.next(); let sender = senders.next();
@ -835,8 +835,8 @@ where
/// Traverse over changesets and plain state and recreate the [`PostState`]s for the given range /// Traverse over changesets and plain state and recreate the [`PostState`]s for the given range
/// of blocks. /// of blocks.
/// ///
/// 1. Iterate over the [BlockTransitionIndex][tables::BlockTransitionIndex] table to get all /// 1. Iterate over the [BlockBodyIndices][tables::BlockBodyIndices] table to get all
/// the transitions /// the transition indices.
/// 2. Iterate over the [StorageChangeSet][tables::StorageChangeSet] table /// 2. Iterate over the [StorageChangeSet][tables::StorageChangeSet] table
/// and the [AccountChangeSet][tables::AccountChangeSet] tables in reverse order to reconstruct /// and the [AccountChangeSet][tables::AccountChangeSet] tables in reverse order to reconstruct
/// the changesets. /// the changesets.
@ -859,29 +859,35 @@ where
&self, &self,
range: impl RangeBounds<BlockNumber> + Clone, range: impl RangeBounds<BlockNumber> + Clone,
) -> Result<Vec<PostState>, TransactionError> { ) -> Result<Vec<PostState>, TransactionError> {
// We are not removing block meta as it is used to get block transitions.
let block_transition = let block_transition =
self.get_or_take::<tables::BlockTransitionIndex, TAKE>(range.clone())?; self.get_or_take::<tables::BlockBodyIndices, false>(range.clone())?;
if block_transition.is_empty() { if block_transition.is_empty() {
return Ok(Vec::new()) return Ok(Vec::new())
} }
// get block transitions
let first_block_number =
block_transition.first().expect("Check for empty is already done").0;
// get block transition of parent block. // get block transition of parent block.
let from = self.get_block_transition(first_block_number.saturating_sub(1))?; let from = block_transition
let to = block_transition.last().expect("Check for empty is already done").1; .first()
.expect("Check for empty is already done")
.1
.transition_at_block();
let to = block_transition
.last()
.expect("Check for empty is already done")
.1
.transition_after_block();
// NOTE: Just get block bodies dont remove them // NOTE: Just get block bodies dont remove them
// it is connection point for bodies getter and execution result getter. // it is connection point for bodies getter and execution result getter.
let block_bodies = self.get_or_take::<tables::BlockBodies, false>(range)?; let block_bodies = self.get_or_take::<tables::BlockBodyIndices, false>(range)?;
// get transaction receipts // get transaction receipts
let from_transaction_num = let from_transaction_num =
block_bodies.first().expect("already checked if there are blocks").1.first_tx_index(); block_bodies.first().expect("already checked if there are blocks").1.first_tx_num();
let to_transaction_num = let to_transaction_num =
block_bodies.last().expect("already checked if there are blocks").1.last_tx_index(); block_bodies.last().expect("already checked if there are blocks").1.last_tx_num();
let receipts = let receipts =
self.get_or_take::<tables::Receipts, TAKE>(from_transaction_num..=to_transaction_num)?; self.get_or_take::<tables::Receipts, TAKE>(from_transaction_num..=to_transaction_num)?;
@ -1031,7 +1037,7 @@ where
// loop break if we are at the end of the blocks. // loop break if we are at the end of the blocks.
for (_, block_body) in block_bodies.into_iter() { for (_, block_body) in block_bodies.into_iter() {
let mut block_post_state = PostState::new(); let mut block_post_state = PostState::new();
for tx_num in block_body.tx_id_range() { for tx_num in block_body.tx_num_range() {
if let Some(changes) = all_changesets.remove(&next_transition_id) { if let Some(changes) = all_changesets.remove(&next_transition_id) {
for mut change in changes.into_iter() { for mut change in changes.into_iter() {
change change
@ -1048,10 +1054,10 @@ where
next_transition_id += 1; next_transition_id += 1;
} }
let Some((_,block_transition)) = block_transition_iter.next() else { break}; let Some((_,block_meta)) = block_transition_iter.next() else { break};
// if block transition points to 1+next transition id it means that there is block // if block transition points to 1+next transition id it means that there is block
// changeset. // changeset.
if block_transition == next_transition_id + 1 { if block_meta.has_block_change() {
if let Some(changes) = all_changesets.remove(&next_transition_id) { if let Some(changes) = all_changesets.remove(&next_transition_id) {
for mut change in changes.into_iter() { for mut change in changes.into_iter() {
change change
@ -1139,7 +1145,7 @@ where
// that is why it is deleted afterwards. // that is why it is deleted afterwards.
if TAKE { if TAKE {
// rm block bodies // rm block bodies
self.get_or_take::<tables::BlockBodies, TAKE>(range)?; self.get_or_take::<tables::BlockBodyIndices, TAKE>(range)?;
// Update pipeline progress // Update pipeline progress
if let Some(fork_number) = unwind_to { if let Some(fork_number) = unwind_to {

View File

@ -1,28 +1,29 @@
use reth_db::{ use reth_db::{
models::{StoredBlockBody, StoredBlockOmmers, StoredBlockWithdrawals}, models::{StoredBlockBodyIndices, StoredBlockOmmers, StoredBlockWithdrawals},
tables, tables,
transaction::{DbTx, DbTxMut}, transaction::{DbTx, DbTxMut},
}; };
use reth_interfaces::{provider::ProviderError, Result}; use reth_interfaces::{provider::ProviderError, Result};
use reth_primitives::{Address, SealedBlock, TransitionId}; use reth_primitives::{Address, SealedBlock};
/// Insert block data into corresponding tables. Used mainly for testing & internal tooling. /// Insert block data into corresponding tables. Used mainly for testing & internal tooling.
/// ///
/// ///
/// Check parent dependency in [tables::HeaderNumbers] and in [tables::BlockBodies] tables. /// Check parent dependency in [tables::HeaderNumbers] and in [tables::BlockBodyIndices] tables.
/// Inserts header data to [tables::CanonicalHeaders], [tables::Headers], [tables::HeaderNumbers]. /// Inserts header data to [tables::CanonicalHeaders], [tables::Headers], [tables::HeaderNumbers].
/// and transactions data to [tables::TxSenders], [tables::Transactions], [tables::TxHashNumber]. /// and transactions data to [tables::TxSenders], [tables::Transactions], [tables::TxHashNumber].
/// and transition indexes to [tables::BlockTransitionIndex] and [tables::TxTransitionIndex]. /// and transition/transaction meta data to [tables::BlockBodyIndices]
/// And block data [tables::BlockBodies], [tables::BlockBodies] and [tables::BlockWithdrawals]. /// and block data to [tables::BlockOmmers] and [tables::BlockWithdrawals].
/// ///
/// Return [TransitionId] `(from,to)` /// Return [StoredBlockBodyIndices] that contains indices of the first and last transactions and
/// transition in the block.
pub fn insert_block<'a, TX: DbTxMut<'a> + DbTx<'a>>( pub fn insert_block<'a, TX: DbTxMut<'a> + DbTx<'a>>(
tx: &TX, tx: &TX,
block: SealedBlock, block: SealedBlock,
senders: Option<Vec<Address>>, senders: Option<Vec<Address>>,
has_block_reward: bool, has_block_reward: bool,
parent_tx_num_transition_id: Option<(u64, u64)>, parent_tx_num_transition_id: Option<(u64, u64)>,
) -> Result<(TransitionId, TransitionId)> { ) -> Result<StoredBlockBodyIndices> {
let block_number = block.number; let block_number = block.number;
tx.put::<tables::CanonicalHeaders>(block.number, block.hash())?; tx.put::<tables::CanonicalHeaders>(block.number, block.hash())?;
// Put header with canonical hashes. // Put header with canonical hashes.
@ -48,35 +49,22 @@ pub fn insert_block<'a, TX: DbTxMut<'a> + DbTx<'a>>(
)?; )?;
} }
let (mut current_tx_id, mut transition_id) = let parent_block_meta = if let Some(parent_tx_num_transition_id) = parent_tx_num_transition_id {
if let Some(parent_tx_num_transition_id) = parent_tx_num_transition_id { StoredBlockBodyIndices {
parent_tx_num_transition_id first_transition_id: parent_tx_num_transition_id.1,
} else if block.number == 0 { first_tx_num: parent_tx_num_transition_id.0,
(0, 0) tx_count: 0,
} else { has_block_change: false,
let prev_block_num = block.number - 1; }
let prev_body = tx } else if block.number == 0 {
.get::<tables::BlockBodies>(prev_block_num)? StoredBlockBodyIndices::default()
.ok_or(ProviderError::BlockBody { number: prev_block_num })?; } else {
let last_transition_id = tx let prev_block_num = block.number - 1;
.get::<tables::BlockTransitionIndex>(prev_block_num)? tx.get::<tables::BlockBodyIndices>(prev_block_num)?
.ok_or(ProviderError::BlockTransition { block_number: prev_block_num })?; .ok_or(ProviderError::BlockBodyIndices { number: prev_block_num })?
(prev_body.start_tx_id + prev_body.tx_count, last_transition_id) };
}; let tx_count = block.body.len() as u64;
let from_transition = transition_id; let mut next_tx_num = parent_block_meta.next_tx_num();
// insert body data
tx.put::<tables::BlockBodies>(
block.number,
StoredBlockBody { start_tx_id: current_tx_id, tx_count: block.body.len() as u64 },
)?;
if !block.body.is_empty() {
// -1 is here as current_tx_id points to the next transaction.
tx.put::<tables::TransactionBlock>(
current_tx_id + block.body.len() as u64 - 1,
block.number,
)?;
}
let senders_len = senders.as_ref().map(|s| s.len()); let senders_len = senders.as_ref().map(|s| s.len());
let tx_iter = if Some(block.body.len()) == senders_len { let tx_iter = if Some(block.body.len()) == senders_len {
@ -94,12 +82,10 @@ pub fn insert_block<'a, TX: DbTxMut<'a> + DbTx<'a>>(
for (transaction, sender) in tx_iter { for (transaction, sender) in tx_iter {
let hash = transaction.hash(); let hash = transaction.hash();
tx.put::<tables::TxSenders>(current_tx_id, sender)?; tx.put::<tables::TxSenders>(next_tx_num, sender)?;
tx.put::<tables::Transactions>(current_tx_id, transaction)?; tx.put::<tables::Transactions>(next_tx_num, transaction)?;
tx.put::<tables::TxTransitionIndex>(current_tx_id, transition_id)?; tx.put::<tables::TxHashNumber>(hash, next_tx_num)?;
tx.put::<tables::TxHashNumber>(hash, current_tx_id)?; next_tx_num += 1;
transition_id += 1;
current_tx_id += 1;
} }
let mut has_withdrawals = false; let mut has_withdrawals = false;
@ -113,13 +99,21 @@ pub fn insert_block<'a, TX: DbTxMut<'a> + DbTx<'a>>(
} }
} }
if has_block_reward || has_withdrawals { let has_block_change = has_block_reward || has_withdrawals;
transition_id += 1;
}
tx.put::<tables::BlockTransitionIndex>(block_number, transition_id)?;
let to_transition = transition_id; let block_meta = StoredBlockBodyIndices {
Ok((from_transition, to_transition)) first_transition_id: parent_block_meta.transition_after_block(),
first_tx_num: parent_block_meta.next_tx_num(),
tx_count,
has_block_change,
};
tx.put::<tables::BlockBodyIndices>(block_number, block_meta.clone())?;
if !block_meta.is_empty() {
tx.put::<tables::TransactionBlock>(block_meta.last_tx_num(), block_number)?;
}
Ok(block_meta)
} }
/// Inserts canonical block in blockchain. Parent tx num and transition id is taken from /// Inserts canonical block in blockchain. Parent tx num and transition id is taken from
@ -129,6 +123,6 @@ pub fn insert_canonical_block<'a, TX: DbTxMut<'a> + DbTx<'a>>(
block: SealedBlock, block: SealedBlock,
senders: Option<Vec<Address>>, senders: Option<Vec<Address>>,
has_block_reward: bool, has_block_reward: bool,
) -> Result<(TransitionId, TransitionId)> { ) -> Result<StoredBlockBodyIndices> {
insert_block(tx, block, senders, has_block_reward, None) insert_block(tx, block, senders, has_block_reward, None)
} }