Skip to main content

neo_devpack_solidity/
storage_key.rs

1//! Storage Key Computation Module
2//!
3//! Computes deterministic storage keys for Neo N3 smart contract state
4//! variables, matching the layout actually emitted by the compiler's
5//! bytecode (see `src/cli/bytecode/bytecode_helpers/storage/mapping.rs`).
6//!
7//! # Key Derivation (production scheme)
8//!
9//! - Base slot of a state variable: `SHA256(variable_name)` —
10//!   [`compute_state_slot`]. Struct fields use the same hash over the
11//!   qualified `"Struct::field"` name (see `src/ir/build/inference.rs`).
12//! - Mapping entry / dynamic-array element: derived ITERATIVELY per nesting
13//!   level on-chain as `slot = keccak256(StdLib.serialize(key) || slot)`,
14//!   where `serialize` is Neo's StdLib binary serializer applied to the
15//!   canonicalized key stack item and the current slot is appended LAST.
16//!   Dynamic arrays store their length at the base slot and element `i` at
17//!   `keccak256(serialize(i) || slot)`.
18//! - Struct field inside a mapping value: `keccak256(field_key || slot)`
19//!   where `field_key = SHA256("Struct::field")`.
20//!
21//! The mapping-entry derivation depends on Neo's StdLib `serialize` native
22//! call (the serialized form embeds the stack-item type byte), so it cannot
23//! be reproduced faithfully off-chain without replicating Neo's
24//! BinarySerializer byte-for-byte. A previous `derive_mapping_slot` /
25//! `KeyFragment` API in this module implemented a DIFFERENT, untyped
26//! SHA-256-based scheme that matched nothing the compiler emits; it has been
27//! removed so off-chain consumers cannot silently compute wrong keys.
28
29use sha2::{Digest, Sha256};
30
31/// Compute the canonical storage slot hash for a state variable name.
32///
33/// The returned value is the 32-byte SHA-256 digest of the UTF-8 name.
34pub fn compute_state_slot(name: &str) -> [u8; 32] {
35    let mut hasher = Sha256::new();
36    hasher.update(name.as_bytes());
37    let digest = hasher.finalize();
38    let mut out = [0u8; 32];
39    out.copy_from_slice(&digest);
40    out
41}
42
43#[cfg(test)]
44mod tests;