mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
375 lines
12 KiB
Rust
375 lines
12 KiB
Rust
//! Implements data structures specific to the database
|
|
|
|
use crate::{
|
|
table::{Compress, Decode, Decompress, Encode},
|
|
DatabaseError,
|
|
};
|
|
use reth_codecs::{add_arbitrary_tests, Compact};
|
|
use reth_primitives::{Address, B256, *};
|
|
use reth_prune_types::{PruneCheckpoint, PruneSegment};
|
|
use reth_stages_types::StageCheckpoint;
|
|
use reth_trie_common::{StoredNibbles, StoredNibblesSubKey, *};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
pub mod accounts;
|
|
pub mod blocks;
|
|
pub mod integer_list;
|
|
pub mod sharded_key;
|
|
pub mod storage_sharded_key;
|
|
|
|
pub use accounts::*;
|
|
pub use blocks::*;
|
|
pub use reth_db_models::{
|
|
AccountBeforeTx, ClientVersion, StoredBlockBodyIndices, StoredBlockWithdrawals,
|
|
};
|
|
pub use sharded_key::ShardedKey;
|
|
|
|
/// Macro that implements [`Encode`] and [`Decode`] for uint types.
|
|
macro_rules! impl_uints {
|
|
($($name:tt),+) => {
|
|
$(
|
|
impl Encode for $name {
|
|
type Encoded = [u8; std::mem::size_of::<$name>()];
|
|
|
|
fn encode(self) -> Self::Encoded {
|
|
self.to_be_bytes()
|
|
}
|
|
}
|
|
|
|
impl Decode for $name {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, $crate::DatabaseError> {
|
|
Ok(
|
|
$name::from_be_bytes(
|
|
value.as_ref().try_into().map_err(|_| $crate::DatabaseError::Decode)?
|
|
)
|
|
)
|
|
}
|
|
}
|
|
)+
|
|
};
|
|
}
|
|
|
|
impl_uints!(u64, u32, u16, u8);
|
|
|
|
impl Encode for Vec<u8> {
|
|
type Encoded = Self;
|
|
|
|
fn encode(self) -> Self::Encoded {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Decode for Vec<u8> {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
|
Ok(value.as_ref().to_vec())
|
|
}
|
|
}
|
|
|
|
impl Encode for Address {
|
|
type Encoded = [u8; 20];
|
|
|
|
fn encode(self) -> Self::Encoded {
|
|
self.0 .0
|
|
}
|
|
}
|
|
|
|
impl Decode for Address {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
|
Ok(Self::from_slice(value.as_ref()))
|
|
}
|
|
}
|
|
|
|
impl Encode for B256 {
|
|
type Encoded = [u8; 32];
|
|
|
|
fn encode(self) -> Self::Encoded {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
impl Decode for B256 {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
|
Ok(Self::new(value.as_ref().try_into().map_err(|_| DatabaseError::Decode)?))
|
|
}
|
|
}
|
|
|
|
impl Encode for String {
|
|
type Encoded = Vec<u8>;
|
|
|
|
fn encode(self) -> Self::Encoded {
|
|
self.into_bytes()
|
|
}
|
|
}
|
|
|
|
impl Decode for String {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
|
Self::from_utf8(value.as_ref().to_vec()).map_err(|_| DatabaseError::Decode)
|
|
}
|
|
}
|
|
|
|
impl Encode for StoredNibbles {
|
|
type Encoded = Vec<u8>;
|
|
|
|
// Delegate to the Compact implementation
|
|
fn encode(self) -> Self::Encoded {
|
|
let mut buf = Vec::with_capacity(self.0.len());
|
|
self.to_compact(&mut buf);
|
|
buf
|
|
}
|
|
}
|
|
|
|
impl Decode for StoredNibbles {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
|
let buf = value.as_ref();
|
|
Ok(Self::from_compact(buf, buf.len()).0)
|
|
}
|
|
}
|
|
|
|
impl Encode for StoredNibblesSubKey {
|
|
type Encoded = Vec<u8>;
|
|
|
|
// Delegate to the Compact implementation
|
|
fn encode(self) -> Self::Encoded {
|
|
let mut buf = Vec::with_capacity(65);
|
|
self.to_compact(&mut buf);
|
|
buf
|
|
}
|
|
}
|
|
|
|
impl Decode for StoredNibblesSubKey {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
|
let buf = value.as_ref();
|
|
Ok(Self::from_compact(buf, buf.len()).0)
|
|
}
|
|
}
|
|
|
|
impl Encode for PruneSegment {
|
|
type Encoded = [u8; 1];
|
|
|
|
fn encode(self) -> Self::Encoded {
|
|
let mut buf = [0u8];
|
|
self.to_compact(&mut buf.as_mut());
|
|
buf
|
|
}
|
|
}
|
|
|
|
impl Decode for PruneSegment {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
|
let buf = value.as_ref();
|
|
Ok(Self::from_compact(buf, buf.len()).0)
|
|
}
|
|
}
|
|
|
|
impl Encode for ClientVersion {
|
|
type Encoded = Vec<u8>;
|
|
|
|
// Delegate to the Compact implementation
|
|
fn encode(self) -> Self::Encoded {
|
|
let mut buf = vec![];
|
|
self.to_compact(&mut buf);
|
|
buf
|
|
}
|
|
}
|
|
|
|
impl Decode for ClientVersion {
|
|
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, DatabaseError> {
|
|
let buf = value.as_ref();
|
|
Ok(Self::from_compact(buf, buf.len()).0)
|
|
}
|
|
}
|
|
|
|
/// Implements compression for Compact type.
|
|
macro_rules! impl_compression_for_compact {
|
|
($($name:tt),+) => {
|
|
$(
|
|
impl Compress for $name {
|
|
type Compressed = Vec<u8>;
|
|
|
|
fn compress_to_buf<B: bytes::BufMut + AsMut<[u8]>>(self, buf: &mut B) {
|
|
let _ = Compact::to_compact(&self, buf);
|
|
}
|
|
}
|
|
|
|
impl Decompress for $name {
|
|
fn decompress<B: AsRef<[u8]>>(value: B) -> Result<$name, $crate::DatabaseError> {
|
|
let value = value.as_ref();
|
|
let (obj, _) = Compact::from_compact(&value, value.len());
|
|
Ok(obj)
|
|
}
|
|
}
|
|
)+
|
|
};
|
|
}
|
|
|
|
impl_compression_for_compact!(
|
|
SealedHeader,
|
|
Header,
|
|
Account,
|
|
Log,
|
|
Receipt,
|
|
TxType,
|
|
StorageEntry,
|
|
BranchNodeCompact,
|
|
StoredNibbles,
|
|
StoredNibblesSubKey,
|
|
StorageTrieEntry,
|
|
StoredBlockBodyIndices,
|
|
StoredBlockOmmers,
|
|
StoredBlockWithdrawals,
|
|
Bytecode,
|
|
AccountBeforeTx,
|
|
TransactionSignedNoHash,
|
|
CompactU256,
|
|
StageCheckpoint,
|
|
PruneCheckpoint,
|
|
ClientVersion,
|
|
Requests,
|
|
// Non-DB
|
|
GenesisAccount
|
|
);
|
|
|
|
macro_rules! impl_compression_fixed_compact {
|
|
($($name:tt),+) => {
|
|
$(
|
|
impl Compress for $name
|
|
{
|
|
type Compressed = Vec<u8>;
|
|
|
|
fn compress_to_buf<B: bytes::BufMut + AsMut<[u8]>>(self, buf: &mut B) {
|
|
let _ = Compact::to_compact(&self, buf);
|
|
}
|
|
|
|
fn uncompressable_ref(&self) -> Option<&[u8]> {
|
|
Some(self.as_ref())
|
|
}
|
|
}
|
|
|
|
impl Decompress for $name
|
|
{
|
|
fn decompress<B: AsRef<[u8]>>(value: B) -> Result<$name, $crate::DatabaseError> {
|
|
let value = value.as_ref();
|
|
let (obj, _) = Compact::from_compact(&value, value.len());
|
|
Ok(obj)
|
|
}
|
|
}
|
|
|
|
)+
|
|
};
|
|
}
|
|
|
|
impl_compression_fixed_compact!(B256, Address);
|
|
|
|
/// Adds wrapper structs for some primitive types so they can use `StructFlags` from Compact, when
|
|
/// used as pure table values.
|
|
macro_rules! add_wrapper_struct {
|
|
($(($name:tt, $wrapper:tt)),+) => {
|
|
$(
|
|
/// Wrapper struct so it can use StructFlags from Compact, when used as pure table values.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, Compact)]
|
|
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
|
|
#[add_arbitrary_tests(compact)]
|
|
pub struct $wrapper(pub $name);
|
|
|
|
impl From<$name> for $wrapper {
|
|
fn from(value: $name) -> Self {
|
|
$wrapper(value)
|
|
}
|
|
}
|
|
|
|
impl From<$wrapper> for $name {
|
|
fn from(value: $wrapper) -> Self {
|
|
value.0
|
|
}
|
|
}
|
|
|
|
impl std::ops::Deref for $wrapper {
|
|
type Target = $name;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
)+
|
|
};
|
|
}
|
|
|
|
add_wrapper_struct!((U256, CompactU256));
|
|
add_wrapper_struct!((u64, CompactU64));
|
|
add_wrapper_struct!((ClientVersion, CompactClientVersion));
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use reth_primitives::{Account, Header, Receipt, ReceiptWithBloom, SealedHeader, Withdrawals};
|
|
use reth_prune_types::{PruneCheckpoint, PruneMode, PruneSegment};
|
|
use reth_stages_types::{
|
|
AccountHashingCheckpoint, CheckpointBlockRange, EntitiesCheckpoint, ExecutionCheckpoint,
|
|
HeadersCheckpoint, IndexHistoryCheckpoint, StageCheckpoint, StageUnitCheckpoint,
|
|
StorageHashingCheckpoint,
|
|
};
|
|
|
|
// each value in the database has an extra field named flags that encodes metadata about other
|
|
// fields in the value, e.g. offset and length.
|
|
//
|
|
// this check is to ensure we do not inadvertently add too many fields to a struct which would
|
|
// expand the flags field and break backwards compatibility
|
|
#[cfg(not(feature = "optimism"))]
|
|
#[test]
|
|
fn test_ensure_backwards_compatibility() {
|
|
assert_eq!(Account::bitflag_encoded_bytes(), 2);
|
|
assert_eq!(AccountHashingCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(CheckpointBlockRange::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(CompactClientVersion::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(CompactU256::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(CompactU64::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(EntitiesCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(ExecutionCheckpoint::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(Header::bitflag_encoded_bytes(), 4);
|
|
assert_eq!(HeadersCheckpoint::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(IndexHistoryCheckpoint::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(PruneCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(PruneMode::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(PruneSegment::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(Receipt::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(ReceiptWithBloom::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(SealedHeader::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(StageCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(StageUnitCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(StoredBlockBodyIndices::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(StoredBlockOmmers::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(StoredBlockWithdrawals::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(StorageHashingCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(Withdrawals::bitflag_encoded_bytes(), 0);
|
|
}
|
|
|
|
#[cfg(feature = "optimism")]
|
|
#[test]
|
|
fn test_ensure_backwards_compatibility() {
|
|
assert_eq!(Account::bitflag_encoded_bytes(), 2);
|
|
assert_eq!(AccountHashingCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(CheckpointBlockRange::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(CompactClientVersion::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(CompactU256::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(CompactU64::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(EntitiesCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(ExecutionCheckpoint::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(Header::bitflag_encoded_bytes(), 4);
|
|
assert_eq!(HeadersCheckpoint::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(IndexHistoryCheckpoint::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(PruneCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(PruneMode::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(PruneSegment::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(Receipt::bitflag_encoded_bytes(), 2);
|
|
assert_eq!(ReceiptWithBloom::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(SealedHeader::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(StageCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(StageUnitCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(StoredBlockBodyIndices::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(StoredBlockOmmers::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(StoredBlockWithdrawals::bitflag_encoded_bytes(), 0);
|
|
assert_eq!(StorageHashingCheckpoint::bitflag_encoded_bytes(), 1);
|
|
assert_eq!(Withdrawals::bitflag_encoded_bytes(), 0);
|
|
}
|
|
}
|