Skip to main content

reth_db_api/models/
storage_sharded_key.rs

1//! Storage sharded key
2use crate::{
3    table::{Decode, Encode},
4    DatabaseError,
5};
6use alloy_primitives::{Address, BlockNumber, B256};
7use derive_more::AsRef;
8use serde::{Deserialize, Serialize};
9
10use super::ShardedKey;
11
12/// Number of indices in one shard.
13pub const NUM_OF_INDICES_IN_SHARD: usize = 2_000;
14
15/// The size of [`StorageShardedKey`] encode bytes.
16/// The fields are: 20-byte address, 32-byte key, and 8-byte block number
17const STORAGE_SHARD_KEY_BYTES_SIZE: usize = 20 + 32 + 8;
18
19/// Stack-allocated encoded key for `StorageShardedKey`.
20///
21/// This avoids heap allocation in hot database paths. The key layout is:
22/// - 20 bytes: `Address`
23/// - 32 bytes: `B256` storage key
24/// - 8 bytes: `BlockNumber` (big-endian)
25pub type StorageShardedKeyEncoded = [u8; STORAGE_SHARD_KEY_BYTES_SIZE];
26
27/// Sometimes data can be too big to be saved for a single key. This helps out by dividing the data
28/// into different shards. Example:
29///
30/// `Address | StorageKey | 200` -> data is from block 0 to 200.
31///
32/// `Address | StorageKey | 300` -> data is from block 201 to 300.
33#[derive(
34    Debug, Default, Clone, Eq, Ord, PartialOrd, PartialEq, AsRef, Serialize, Deserialize, Hash,
35)]
36pub struct StorageShardedKey {
37    /// Storage account address.
38    pub address: Address,
39    /// Storage slot with highest block number.
40    #[as_ref]
41    pub sharded_key: ShardedKey<B256>,
42}
43
44impl StorageShardedKey {
45    /// Creates a new `StorageShardedKey`.
46    pub const fn new(
47        address: Address,
48        storage_key: B256,
49        highest_block_number: BlockNumber,
50    ) -> Self {
51        Self { address, sharded_key: ShardedKey { key: storage_key, highest_block_number } }
52    }
53
54    /// Creates a new key with the highest block number set to maximum.
55    /// This is useful when we want to search the last value for a given key.
56    pub const fn last(address: Address, storage_key: B256) -> Self {
57        Self {
58            address,
59            sharded_key: ShardedKey { key: storage_key, highest_block_number: u64::MAX },
60        }
61    }
62}
63
64impl Encode for StorageShardedKey {
65    type Encoded = StorageShardedKeyEncoded;
66
67    #[inline]
68    fn encode(self) -> Self::Encoded {
69        let mut buf = [0u8; STORAGE_SHARD_KEY_BYTES_SIZE];
70        buf[..20].copy_from_slice(self.address.as_slice());
71        buf[20..52].copy_from_slice(self.sharded_key.key.as_slice());
72        buf[52..].copy_from_slice(&self.sharded_key.highest_block_number.to_be_bytes());
73        buf
74    }
75}
76
77impl Decode for StorageShardedKey {
78    fn decode(value: &[u8]) -> Result<Self, DatabaseError> {
79        if value.len() != STORAGE_SHARD_KEY_BYTES_SIZE {
80            return Err(DatabaseError::Decode)
81        }
82        let block_num_index = value.len() - 8;
83
84        let highest_block_number = u64::from_be_bytes(
85            value[block_num_index..].try_into().map_err(|_| DatabaseError::Decode)?,
86        );
87        let address = Address::decode(&value[..20])?;
88        let storage_key = B256::decode(&value[20..52])?;
89
90        Ok(Self { address, sharded_key: ShardedKey::new(storage_key, highest_block_number) })
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use alloy_primitives::{address, b256};
98
99    #[test]
100    fn storage_sharded_key_encode_decode_roundtrip() {
101        let addr = address!("0102030405060708091011121314151617181920");
102        let storage_key = b256!("0001020304050607080910111213141516171819202122232425262728293031");
103        let block_num = 0x123456789ABCDEFu64;
104        let key = StorageShardedKey::new(addr, storage_key, block_num);
105
106        let encoded = key.encode();
107
108        // Verify it's stack-allocated (60 bytes)
109        assert_eq!(encoded.len(), 60);
110        assert_eq!(std::mem::size_of_val(&encoded), 60);
111
112        // Verify roundtrip (check against expected values since key was consumed)
113        let decoded = StorageShardedKey::decode(&encoded).unwrap();
114        assert_eq!(decoded.address, address!("0102030405060708091011121314151617181920"));
115        assert_eq!(
116            decoded.sharded_key.key,
117            b256!("0001020304050607080910111213141516171819202122232425262728293031")
118        );
119        assert_eq!(decoded.sharded_key.highest_block_number, 0x123456789ABCDEFu64);
120    }
121
122    #[test]
123    fn storage_sharded_key_last_works() {
124        let addr = address!("0102030405060708091011121314151617181920");
125        let storage_key = b256!("0001020304050607080910111213141516171819202122232425262728293031");
126        let key = StorageShardedKey::last(addr, storage_key);
127        assert_eq!(key.sharded_key.highest_block_number, u64::MAX);
128
129        let encoded = key.encode();
130        let decoded = StorageShardedKey::decode(&encoded).unwrap();
131        assert_eq!(decoded.sharded_key.highest_block_number, u64::MAX);
132    }
133}