feat(db): models crate (#10314)

Co-authored-by: Miguel Oliveira <migueloliveiradev@gmail.com>
This commit is contained in:
Alexey Shekhirin
2024-08-17 10:25:34 -07:00
committed by GitHub
parent b6edbe36dc
commit 6bf3ca320a
14 changed files with 220 additions and 133 deletions

View File

@ -0,0 +1,46 @@
[package]
name = "reth-db-models"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
description = "Database models used in storage module."
[lints]
workspace = true
[dependencies]
# reth
reth-codecs.workspace = true
reth-primitives = { workspace = true, features = ["reth-codec"] }
# codecs
modular-bitfield.workspace = true
serde = { workspace = true, default-features = false }
# misc
bytes.workspace = true
# arbitrary utils
arbitrary = { workspace = true, features = ["derive"], optional = true }
proptest = { workspace = true, optional = true }
[dev-dependencies]
# reth
reth-primitives = { workspace = true, features = ["arbitrary"] }
reth-codecs.workspace = true
arbitrary = { workspace = true, features = ["derive"] }
proptest-arbitrary-interop.workspace = true
proptest.workspace = true
test-fuzz.workspace = true
[features]
test-utils = ["arbitrary"]
arbitrary = [
"reth-primitives/arbitrary",
"dep:arbitrary",
"dep:proptest",
]

View File

@ -0,0 +1,51 @@
use reth_codecs::{add_arbitrary_tests, Compact};
use serde::Serialize;
use reth_primitives::{Account, Address, Buf};
/// Account as it is saved in the database.
///
/// [`Address`] is the subkey.
#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize)]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[add_arbitrary_tests(compact)]
pub struct AccountBeforeTx {
/// Address for the account. Acts as `DupSort::SubKey`.
pub address: Address,
/// Account state before the transaction.
pub info: Option<Account>,
}
// NOTE: Removing reth_codec and manually encode subkey
// and compress second part of the value. If we have compression
// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
impl Compact for AccountBeforeTx {
fn to_compact<B>(&self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
// for now put full bytes and later compress it.
buf.put_slice(self.address.as_slice());
let mut acc_len = 0;
if let Some(account) = self.info {
acc_len = account.to_compact(buf);
}
acc_len + 20
}
fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
let address = Address::from_slice(&buf[..20]);
buf.advance(20);
let info = if len - 20 > 0 {
let (acc, advanced_buf) = Account::from_compact(buf, len - 20);
buf = advanced_buf;
Some(acc)
} else {
None
};
(Self { address, info }, buf)
}
}

View File

@ -0,0 +1,85 @@
use std::ops::Range;
use reth_codecs::{add_arbitrary_tests, Compact};
use reth_primitives::TxNumber;
use serde::{Deserialize, Serialize};
/// Total number of transactions.
pub type NumTransactions = u64;
/// The storage of the block body indices.
///
/// It has the pointer to the transaction Number of the first
/// transaction in the block and the total number of transactions.
#[derive(Debug, Default, Eq, PartialEq, Clone, Serialize, Deserialize, Compact)]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[add_arbitrary_tests(compact)]
pub struct StoredBlockBodyIndices {
/// The number of the first transaction in this block
///
/// 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 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,
}
impl StoredBlockBodyIndices {
/// Return the range of transaction ids for this block.
pub const fn tx_num_range(&self) -> Range<TxNumber> {
self.first_tx_num..self.first_tx_num + self.tx_count
}
/// 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
/// non-empty block
pub const fn last_tx_num(&self) -> TxNumber {
self.first_tx_num.saturating_add(self.tx_count).saturating_sub(1)
}
/// First transaction index.
///
/// Caution: If the block is empty, this is the number of the first transaction
/// in the next non-empty block.
pub const fn first_tx_num(&self) -> TxNumber {
self.first_tx_num
}
/// Return the index of the next transaction after this block.
pub const fn next_tx_num(&self) -> TxNumber {
self.first_tx_num + self.tx_count
}
/// Return a flag whether the block is empty
pub const fn is_empty(&self) -> bool {
self.tx_count == 0
}
/// Return number of transaction inside block
///
/// NOTE: This is not the same as the number of transitions.
pub const fn tx_count(&self) -> NumTransactions {
self.tx_count
}
}
#[cfg(test)]
mod tests {
use crate::StoredBlockBodyIndices;
#[test]
fn block_indices() {
let first_tx_num = 10;
let tx_count = 6;
let block_indices = StoredBlockBodyIndices { first_tx_num, tx_count };
assert_eq!(block_indices.first_tx_num(), first_tx_num);
assert_eq!(block_indices.last_tx_num(), first_tx_num + tx_count - 1);
assert_eq!(block_indices.next_tx_num(), first_tx_num + tx_count);
assert_eq!(block_indices.tx_count(), tx_count);
assert_eq!(block_indices.tx_num_range(), first_tx_num..first_tx_num + tx_count);
}
}

View File

@ -0,0 +1,9 @@
//! Models used in storage module
/// Accounts
pub mod accounts;
pub use accounts::AccountBeforeTx;
/// Blocks
pub mod blocks;
pub use blocks::StoredBlockBodyIndices;