From 0a17a981d6285e63f82c493e5d7cb9fe5f254a80 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 18 Nov 2023 17:30:23 -0500 Subject: [PATCH] Start receipts reader --- crates/db-utils/src/leveldb/key.rs | 14 ++++++ crates/db-utils/src/leveldb/mod.rs | 68 +++++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/crates/db-utils/src/leveldb/key.rs b/crates/db-utils/src/leveldb/key.rs index 6d4392e..191f393 100644 --- a/crates/db-utils/src/leveldb/key.rs +++ b/crates/db-utils/src/leveldb/key.rs @@ -7,6 +7,7 @@ use leveldb::database::key::Key; pub const HEADER_PREFIX: u8 = b"h"[0]; pub const HEADER_HASH_SUFFIX: u8 = b"n"[0]; pub const BLOCK_BODY_PREFIX: u8 = b"b"[0]; +pub const BLOCK_RECEIPTS_PREFIX: u8 = b"r"[0]; /// Wrapper around a [Vec] to implement the [Key] trait. pub struct DBKey(Vec); @@ -50,6 +51,19 @@ impl DBKey { key.extend_from_slice(&hash); Self(key) } + + /// Get a key for the `block receipts by number + hash` table in the Geth leveldb. + /// + /// Format: `block_receipts_prefix + number + hash -> block receipts` + pub fn receipts_by_hash(hash: [u8; 32], number: u64) -> Self { + const KEY_SIZE: usize = 1 + 8 + 32; + + let mut key = Vec::with_capacity(KEY_SIZE); + key.push(BLOCK_RECEIPTS_PREFIX); + key.extend_from_slice(&number.to_be_bytes()); + key.extend_from_slice(&hash); + Self(key) + } } impl From> for DBKey { diff --git a/crates/db-utils/src/leveldb/mod.rs b/crates/db-utils/src/leveldb/mod.rs index 08bcfe3..9b63232 100644 --- a/crates/db-utils/src/leveldb/mod.rs +++ b/crates/db-utils/src/leveldb/mod.rs @@ -1,7 +1,7 @@ use alloy_rlp::Decodable; use anyhow::{anyhow, Result}; use leveldb::{database::Database, kv::KV, options::ReadOptions}; -use reth_primitives::{Header, SealedBlock, SealedHeader, TransactionSigned}; +use reth_primitives::{Header, ReceiptWithBloom, SealedBlock, SealedHeader, TransactionSigned}; mod key; pub use self::key::DBKey; @@ -21,10 +21,29 @@ impl GethDBReader { Self { db: database } } + /// Retrieve a header hash by its number from a Geth LevelDB. + /// + /// ### Takes + /// - `number`: The block number of the header hash to retrieve + /// + /// ### Returns + /// - Success: A [u8; 32] containing the header hash + /// - Failure: An [anyhow::Error] if the header hash could not be found + pub fn hash_by_number(&self, number: u64) -> Result<[u8; 32]> { + let key = DBKey::hash_by_number(number); + let hash = self + .db + .get(ReadOptions::new(), key)? + .ok_or(anyhow!("Header hash not found"))? + .try_into() + .map_err(|_| anyhow!("Header hash received from DB is not 32 bytes in size"))?; + + Ok(hash) + } + /// Retrieve a [SealedHeader] by its block number from a Geth LevelDB /// /// ### Takes - /// - `db`: A reference to a [Database] instance /// - `number`: The block number of the [SealedHeader] to retrieve /// /// ### Returns @@ -32,13 +51,7 @@ impl GethDBReader { /// - Failure: An [anyhow::Error] if the header could not be found pub fn header_by_number(&self, number: u64) -> Result { // Fetch the header hash - let header_hash_key = DBKey::hash_by_number(number); - let header_hash: [u8; 32] = self - .db - .get(ReadOptions::new(), header_hash_key)? - .ok_or(anyhow::anyhow!("Header hash not found"))? - .try_into() - .map_err(|_| anyhow!("Header hash received from DB is not 32 bytes in size"))?; + let header_hash = self.hash_by_number(number)?; // Fetch the header RLP let header_key = DBKey::header_lookup(header_hash, number); @@ -58,7 +71,6 @@ impl GethDBReader { /// Retrieve a [SealedBlock] by its block number from a Geth LevelDB. /// /// ### Takes - /// - `db`: A reference to a [Database] instance /// - `number`: The block number of the [SealedBlock] to Retrieve /// /// ### Returns @@ -84,6 +96,29 @@ impl GethDBReader { withdrawals: None, }) } + + /// Reads the receipts for a [SealedBlock] by its number from a Geth LevelDB. + /// + /// ### Takes + /// - `db`: A reference to a [Database] instance + /// + /// ### Returns + /// - Success: A [Vec] of [ReceiptWithBloom] instances + /// - Failure: An [anyhow::Error] if the receipts could not be found + pub fn receipts_by_number(&self, number: u64) -> Result> { + let header_hash = self.hash_by_number(number)?; + + let receipts_key = DBKey::receipts_by_hash(header_hash, number); + let receipts_rlp = self + .db + .get(ReadOptions::new(), receipts_key)? + .ok_or(anyhow::anyhow!("Receipts RLP not found"))?; + + let receipts = >::decode(&mut receipts_rlp.as_slice())?; + dbg!(&receipts); + + Ok(receipts) + } } #[cfg(test)] @@ -120,4 +155,17 @@ mod db_test { dbg!(reader.block_by_number(TEST_BLOCK_NO).unwrap()); } + + #[test] + #[ignore] + fn sanity_read_receipts() { + let mut db_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + db_path.push("testdata/geth/chaindata"); + + let options = Options::new(); + let database: Database = Database::open(db_path.as_path(), options).unwrap(); + let reader = GethDBReader::new(database); + + dbg!(reader.receipts_by_number(TEST_BLOCK_NO).unwrap()); + } }