use alloy_primitives::{Address, B256, U256};
use sha2::{Digest, Sha256};
use crate::{Stem, SubIndex, TreeKey, STEM_LEN};
pub const BASIC_DATA_LEAF_KEY: SubIndex = 0;
pub const CODE_HASH_LEAF_KEY: SubIndex = 1;
pub const BASIC_DATA_CODE_SIZE_OFFSET: usize = 5;
pub const BASIC_DATA_NONCE_OFFSET: usize = 8;
pub const BASIC_DATA_BALANCE_OFFSET: usize = 16;
pub const HEADER_STORAGE_OFFSET: SubIndex = 64;
pub const CODE_OFFSET: SubIndex = 128;
pub const STEM_SUBTREE_WIDTH: u64 = 256;
const ZERO_PREFIX: [u8; 12] = [0u8; 12];
pub fn get_binary_tree_key(address: &Address, input_key: &[u8; 32]) -> TreeKey {
get_binary_tree_key_inner(address, input_key, false)
}
fn get_binary_tree_key_inner(address: &Address, input_key: &[u8; 32], overflow: bool) -> TreeKey {
let mut hasher = Sha256::new();
hasher.update(ZERO_PREFIX);
hasher.update(address.as_slice());
let mut buf = [0u8; 32];
buf[..31].copy_from_slice(&input_key[..31]);
buf[..31].reverse();
if overflow {
buf[31] = 1;
}
hasher.update(buf);
let hash = hasher.finalize();
let mut stem_bytes = [0u8; STEM_LEN];
stem_bytes.copy_from_slice(&hash[..STEM_LEN]);
TreeKey::new(Stem::new(stem_bytes), input_key[31])
}
pub fn get_basic_data_key(address: &Address) -> TreeKey {
let mut k = [0u8; 32];
k[31] = BASIC_DATA_LEAF_KEY;
get_binary_tree_key(address, &k)
}
pub fn get_code_hash_key(address: &Address) -> TreeKey {
let mut k = [0u8; 32];
k[31] = CODE_HASH_LEAF_KEY;
get_binary_tree_key(address, &k)
}
pub fn get_storage_slot_key(address: &Address, slot: &[u8; 32]) -> TreeKey {
let mut k = [0u8; 32];
let is_header_slot = slot[..31].iter().all(|&b| b == 0) && slot[31] < 64;
let overflow = if is_header_slot {
k[31] = HEADER_STORAGE_OFFSET + slot[31];
false
} else {
for i in (1..31).rev() {
k[i] = slot[i];
}
k[0] = slot[0].wrapping_add(1);
k[31] = slot[31];
slot[0] == 0xff
};
get_binary_tree_key_inner(address, &k, overflow)
}
pub fn get_storage_slot_key_u256(address: &Address, slot: U256) -> TreeKey {
get_storage_slot_key(address, &slot.to_be_bytes::<32>())
}
pub fn get_code_chunk_key(address: &Address, chunk_number: u64) -> TreeKey {
let pos = u64::from(CODE_OFFSET) + chunk_number;
if pos < STEM_SUBTREE_WIDTH {
let mut k = [0u8; 32];
k[31] = u8::try_from(pos).expect("pos < 256 by construction"); return get_binary_tree_key(address, &k);
}
let stem_index = pos / STEM_SUBTREE_WIDTH;
let subindex = u8::try_from(pos % STEM_SUBTREE_WIDTH).expect("subindex < 256 by construction");
let mut k = [0u8; 32];
let stem_bytes = stem_index.to_be_bytes();
k[23..31].copy_from_slice(&stem_bytes); k[31] = subindex;
get_binary_tree_key(address, &k)
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BasicDataLeaf {
pub version: u8,
pub code_size: u32,
pub nonce: u64,
pub balance: u128,
}
impl BasicDataLeaf {
pub const fn new(nonce: u64, balance: u128, code_size: u32) -> Self {
Self {
version: 0,
code_size,
nonce,
balance,
}
}
pub fn encode(&self) -> B256 {
let mut bytes = [0u8; 32];
bytes[0] = self.version;
bytes[5..8].copy_from_slice(&self.code_size.to_be_bytes()[1..4]); bytes[8..16].copy_from_slice(&self.nonce.to_be_bytes());
bytes[16..32].copy_from_slice(&self.balance.to_be_bytes());
B256::from(bytes)
}
pub fn decode(value: B256) -> Self {
let bytes = value.as_slice();
let mut code_size_bytes = [0u8; 4];
code_size_bytes[1..4].copy_from_slice(&bytes[5..8]);
let mut nonce_bytes = [0u8; 8];
nonce_bytes.copy_from_slice(&bytes[8..16]);
let mut balance_bytes = [0u8; 16];
balance_bytes.copy_from_slice(&bytes[16..32]);
Self {
version: bytes[0],
code_size: u32::from_be_bytes(code_size_bytes),
nonce: u64::from_be_bytes(nonce_bytes),
balance: u128::from_be_bytes(balance_bytes),
}
}
}
pub struct AccountStem {
pub address: Address,
}
impl AccountStem {
pub fn new(address: Address) -> Self {
Self { address }
}
pub fn basic_data_key(&self) -> TreeKey {
get_basic_data_key(&self.address)
}
pub fn code_hash_key(&self) -> TreeKey {
get_code_hash_key(&self.address)
}
pub fn storage_key(&self, slot: U256) -> TreeKey {
get_storage_slot_key_u256(&self.address, slot)
}
pub fn code_chunk_key(&self, chunk_index: u64) -> TreeKey {
get_code_chunk_key(&self.address, chunk_index)
}
}
#[deprecated(
since = "0.2.0",
note = "Use `get_basic_data_key` or `AccountStem::basic_data_key` instead"
)]
#[allow(unused_imports)]
pub use get_basic_data_key as account_stem_basic_data;
#[deprecated(
since = "0.2.0",
note = "Use `get_code_chunk_key` or `AccountStem::code_chunk_key` instead"
)]
#[allow(unused_imports)]
pub use get_code_chunk_key as code_chunk_key;
#[deprecated(
since = "0.2.0",
note = "Use `get_storage_slot_key_u256` or `AccountStem::storage_key` instead"
)]
#[allow(unused_imports)]
pub use get_storage_slot_key_u256 as storage_key;
#[cfg(test)]
mod tests {
use super::*;
fn hex_to_b256(s: &str) -> B256 {
let bytes = hex::decode(s).expect("valid hex");
B256::from_slice(&bytes)
}
#[test]
fn test_key_derivation_geth_vectors() {
let address = Address::repeat_byte(0x42);
let key = get_basic_data_key(&address);
let expected =
hex_to_b256("5851f2118c28d2a77e2535442cd4bcf21c7ad2de368b7d0609833d9d2eea1500");
assert_eq!(key.to_bytes(), expected, "basic_data_key must match Geth");
let mut slot_bytes = [0u8; 32];
slot_bytes[31] = 100;
let key = get_storage_slot_key(&address, &slot_bytes);
let expected =
hex_to_b256("7254eaede3e09db532fff12affd6e83b02354608264472882f9c64e9b18abc64");
assert_eq!(
key.to_bytes(),
expected,
"storage_slot_key(100) must match Geth"
);
let mut slot_bytes = [0u8; 32];
slot_bytes[0] = 0xff;
slot_bytes[31] = 0x20;
let key = get_storage_slot_key(&address, &slot_bytes);
let expected =
hex_to_b256("f7dc034274011114ac420c79e0b8450a85750ee9ccd93cff48bbc61323e00220");
assert_eq!(
key.to_bytes(),
expected,
"storage_slot_key(0xff..0020) with overflow must match Geth"
);
}
#[test]
fn test_basic_data_roundtrip() {
let original = BasicDataLeaf::new(42, 1000000, 1024);
let encoded = original.encode();
let decoded = BasicDataLeaf::decode(encoded);
assert_eq!(original, decoded);
}
#[test]
fn test_storage_key_header_slots() {
let address = Address::repeat_byte(0x42);
let base_key = get_basic_data_key(&address);
for slot in 0..64u8 {
let mut slot_bytes = [0u8; 32];
slot_bytes[31] = slot;
let key = get_storage_slot_key(&address, &slot_bytes);
assert_eq!(key.stem, base_key.stem);
assert_eq!(key.subindex, HEADER_STORAGE_OFFSET + slot);
}
}
#[test]
fn test_storage_key_main_storage() {
let address = Address::repeat_byte(0x42);
let base_key = get_basic_data_key(&address);
let mut slot_bytes = [0u8; 32];
slot_bytes[31] = 100;
let key = get_storage_slot_key(&address, &slot_bytes);
assert_ne!(key.stem, base_key.stem);
}
#[test]
fn test_code_chunk_key() {
let address = Address::repeat_byte(0x42);
let key0a = get_code_chunk_key(&address, 0);
let key0b = get_code_chunk_key(&address, 0);
assert_eq!(key0a, key0b);
let key1 = get_code_chunk_key(&address, 1);
assert_ne!(key0a.to_bytes(), key1.to_bytes());
let addr2 = Address::repeat_byte(0x43);
let key0_addr2 = get_code_chunk_key(&addr2, 0);
assert_ne!(key0a.stem, key0_addr2.stem);
}
#[test]
fn test_key_derivation_deterministic() {
let address = Address::repeat_byte(0x42);
let key1 = get_basic_data_key(&address);
let key2 = get_basic_data_key(&address);
assert_eq!(key1, key2);
}
#[test]
fn test_different_addresses_different_stems() {
let addr1 = Address::repeat_byte(0x01);
let addr2 = Address::repeat_byte(0x02);
let key1 = get_basic_data_key(&addr1);
let key2 = get_basic_data_key(&addr2);
assert_ne!(key1.stem, key2.stem);
}
#[test]
fn test_code_chunks_share_account_stem() {
let address = Address::repeat_byte(0x42);
let basic_key = get_basic_data_key(&address);
for chunk_idx in 0..128u64 {
let chunk_key = get_code_chunk_key(&address, chunk_idx);
assert_eq!(
chunk_key.stem, basic_key.stem,
"Chunk {} should share account stem",
chunk_idx
);
assert_eq!(chunk_key.subindex, (128 + chunk_idx) as u8);
}
let chunk_128_key = get_code_chunk_key(&address, 128);
assert_ne!(chunk_128_key.stem, basic_key.stem);
}
#[test]
fn test_main_storage_slot_key_correctness() {
let address = Address::repeat_byte(0x42);
let slot_64 = U256::from(64);
let key_64 = get_storage_slot_key_u256(&address, slot_64);
assert_eq!(key_64.subindex, 64);
let slot_100 = U256::from(100);
let key_100 = get_storage_slot_key_u256(&address, slot_100);
assert_eq!(key_100.subindex, 100);
assert_eq!(key_64.stem, key_100.stem);
let slot_256 = U256::from(256);
let key_256 = get_storage_slot_key_u256(&address, slot_256);
assert_eq!(key_256.subindex, 0);
assert_ne!(
key_256.stem, key_64.stem,
"slot 256 should be in different stem than slot 64"
);
let slot_257 = U256::from(257);
let key_257 = get_storage_slot_key_u256(&address, slot_257);
assert_eq!(key_257.subindex, 1);
assert_eq!(
key_257.stem, key_256.stem,
"slot 257 should share stem with slot 256"
);
let slot_511 = U256::from(511);
let key_511 = get_storage_slot_key_u256(&address, slot_511);
assert_eq!(key_511.subindex, 255);
assert_eq!(key_511.stem, key_256.stem);
let slot_512 = U256::from(512);
let key_512 = get_storage_slot_key_u256(&address, slot_512);
assert_eq!(key_512.subindex, 0);
assert_ne!(key_512.stem, key_256.stem);
let mut large_slot_bytes = [0u8; 32];
large_slot_bytes[0] = 0x01;
large_slot_bytes[1] = 0x02;
large_slot_bytes[30] = 0x1F;
large_slot_bytes[31] = 0x20;
let _large_slot = U256::from_be_bytes(large_slot_bytes);
let key_large = get_storage_slot_key(&address, &large_slot_bytes);
assert_eq!(key_large.subindex, 0x20);
let mut other_slot_bytes = large_slot_bytes;
other_slot_bytes[1] = 0x03; let key_other = get_storage_slot_key(&address, &other_slot_bytes);
assert_ne!(
key_large.stem, key_other.stem,
"different tree_index should produce different stem"
);
}
#[test]
fn test_storage_slot_high_byte_0xff() {
let address = Address::repeat_byte(0x42);
let mut slot_bytes = [0u8; 32];
slot_bytes[0] = 0xff;
slot_bytes[31] = 0x20;
let key1 = get_storage_slot_key(&address, &slot_bytes);
let key2 = get_storage_slot_key(&address, &slot_bytes);
assert_eq!(key1, key2, "storage key must be deterministic");
assert_eq!(key1.subindex, 0x20);
let mut slot_fe = slot_bytes;
slot_fe[0] = 0xfe;
let key_fe = get_storage_slot_key(&address, &slot_fe);
assert_ne!(
key1.stem, key_fe.stem,
"0xff and 0xfe high bytes should produce different stems"
);
}
#[test]
fn test_storage_slot_all_high_bytes() {
let address = Address::repeat_byte(0x42);
let slot_max = [0xffu8; 32];
let key_max = get_storage_slot_key(&address, &slot_max);
assert_eq!(key_max.subindex, 0xff);
let mut slot_ff00 = [0u8; 32];
slot_ff00[0] = 0xff;
let key_ff00 = get_storage_slot_key(&address, &slot_ff00);
assert_eq!(key_ff00.subindex, 0x00);
assert_ne!(key_max.stem, key_ff00.stem);
}
#[test]
fn test_storage_slot_boundary_values() {
let address = Address::repeat_byte(0x42);
for high_byte in [0x00, 0x01, 0x7f, 0x80, 0xfe, 0xff] {
let mut slot = [0u8; 32];
slot[0] = high_byte;
slot[31] = 0x42;
let key = get_storage_slot_key(&address, &slot);
assert_eq!(key.subindex, 0x42);
}
}
}