reth_primitives_traits/storage.rs
1use alloy_primitives::{keccak256, B256, U256};
2
3/// Trait for `DupSort` table values that contain a subkey.
4///
5/// This trait allows extracting the subkey from a value during database iteration,
6/// enabling proper range queries and filtering on `DupSort` tables.
7pub trait ValueWithSubKey {
8 /// The type of the subkey.
9 type SubKey;
10
11 /// Extract the subkey from the value.
12 fn get_subkey(&self) -> Self::SubKey;
13}
14
15/// A storage slot key that tracks whether it holds a plain (unhashed) EVM slot
16/// or a keccak256-hashed slot.
17///
18/// This enum replaces the `use_hashed_state: bool` parameter pattern by carrying
19/// provenance with the key itself. Once tagged at a read/write boundary, downstream
20/// code can call [`Self::to_hashed`] without risk of double-hashing — hashing a
21/// [`StorageSlotKey::Hashed`] is a no-op.
22///
23/// The on-disk encoding is unchanged (raw 32-byte [`B256`]). The variant is set
24/// by the code that knows the context (which table, which storage mode).
25#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub enum StorageSlotKey {
27 /// An unhashed EVM storage slot, as produced by REVM execution.
28 Plain(B256),
29 /// A keccak256-hashed storage slot, as stored in `HashedStorages` and
30 /// in v2-mode `StorageChangeSets`.
31 Hashed(B256),
32}
33
34impl Default for StorageSlotKey {
35 fn default() -> Self {
36 Self::Plain(B256::ZERO)
37 }
38}
39
40impl StorageSlotKey {
41 /// Create a plain slot key from a REVM [`U256`] storage index.
42 pub const fn from_u256(slot: U256) -> Self {
43 Self::Plain(B256::new(slot.to_be_bytes()))
44 }
45
46 /// Create a plain slot key from a raw [`B256`].
47 pub const fn plain(key: B256) -> Self {
48 Self::Plain(key)
49 }
50
51 /// Create a hashed slot key from a raw [`B256`].
52 pub const fn hashed(key: B256) -> Self {
53 Self::Hashed(key)
54 }
55
56 /// Tag a raw [`B256`] based on the storage mode.
57 ///
58 /// When `use_hashed_state` is true the key is assumed already hashed.
59 /// When false it is assumed to be a plain slot.
60 pub const fn from_raw(key: B256, use_hashed_state: bool) -> Self {
61 if use_hashed_state {
62 Self::Hashed(key)
63 } else {
64 Self::Plain(key)
65 }
66 }
67
68 /// Returns the raw [`B256`] regardless of variant.
69 pub const fn as_b256(&self) -> B256 {
70 match *self {
71 Self::Plain(b) | Self::Hashed(b) => b,
72 }
73 }
74
75 /// Returns `true` if this key is already hashed.
76 pub const fn is_hashed(&self) -> bool {
77 matches!(self, Self::Hashed(_))
78 }
79
80 /// Returns `true` if this key is plain (unhashed).
81 pub const fn is_plain(&self) -> bool {
82 matches!(self, Self::Plain(_))
83 }
84
85 /// Produce the keccak256-hashed form of this slot key.
86 ///
87 /// - If already [`Hashed`](Self::Hashed), returns the inner value as-is (no double-hash).
88 /// - If [`Plain`](Self::Plain), applies keccak256 and returns the result.
89 pub fn to_hashed(&self) -> B256 {
90 match *self {
91 Self::Hashed(b) => b,
92 Self::Plain(b) => keccak256(b),
93 }
94 }
95
96 /// Convert a plain slot to its changeset representation.
97 ///
98 /// In v2 mode (`use_hashed_state = true`), the changeset stores hashed keys,
99 /// so the plain key is hashed. In v1 mode, the plain key is stored as-is.
100 ///
101 /// Panics (debug) if called on an already-hashed key.
102 pub fn to_changeset_key(self, use_hashed_state: bool) -> B256 {
103 debug_assert!(self.is_plain(), "to_changeset_key called on already-hashed key");
104 if use_hashed_state {
105 self.to_hashed()
106 } else {
107 self.as_b256()
108 }
109 }
110
111 /// Like [`to_changeset_key`](Self::to_changeset_key) but returns a tagged
112 /// [`StorageSlotKey`] instead of a raw [`B256`].
113 ///
114 /// Panics (debug) if called on an already-hashed key.
115 pub fn to_changeset(self, use_hashed_state: bool) -> Self {
116 Self::from_raw(self.to_changeset_key(use_hashed_state), use_hashed_state)
117 }
118}
119
120impl From<StorageSlotKey> for B256 {
121 fn from(key: StorageSlotKey) -> Self {
122 key.as_b256()
123 }
124}
125
126/// Account storage entry.
127///
128/// `key` is the subkey when used as a value in the `StorageChangeSets` table.
129#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
130#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
131#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
132#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
133pub struct StorageEntry {
134 /// Storage key.
135 pub key: B256,
136 /// Value on storage key.
137 pub value: U256,
138}
139
140impl StorageEntry {
141 /// Create a new `StorageEntry` with given key and value.
142 pub const fn new(key: B256, value: U256) -> Self {
143 Self { key, value }
144 }
145
146 /// Tag this entry's key as a [`StorageSlotKey`] based on the storage mode.
147 ///
148 /// When `use_hashed_state` is true, the key is tagged as already-hashed.
149 /// When false, it is tagged as plain.
150 pub const fn slot_key(&self, use_hashed_state: bool) -> StorageSlotKey {
151 StorageSlotKey::from_raw(self.key, use_hashed_state)
152 }
153}
154
155impl ValueWithSubKey for StorageEntry {
156 type SubKey = B256;
157
158 fn get_subkey(&self) -> Self::SubKey {
159 self.key
160 }
161}
162
163impl From<(B256, U256)> for StorageEntry {
164 fn from((key, value): (B256, U256)) -> Self {
165 Self { key, value }
166 }
167}
168
169// NOTE: Removing reth_codec and manually encode subkey
170// and compress second part of the value. If we have compression
171// over whole value (Even SubKey) that would mess up fetching of values with seek_by_key_subkey
172#[cfg(any(test, feature = "reth-codec"))]
173impl reth_codecs::Compact for StorageEntry {
174 fn to_compact<B>(&self, buf: &mut B) -> usize
175 where
176 B: bytes::BufMut + AsMut<[u8]>,
177 {
178 // for now put full bytes and later compress it.
179 buf.put_slice(&self.key[..]);
180 self.value.to_compact(buf) + 32
181 }
182
183 fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
184 let key = B256::from_slice(&buf[..32]);
185 let (value, out) = U256::from_compact(&buf[32..], len - 32);
186 (Self { key, value }, out)
187 }
188}