Hashed Storage Locations
requires "evm.md"
requires "buf.md"
module HASHED-LOCATIONS
imports EVM
imports BUF
Hashed Location for Storage
The storage accommodates permanent data such as the balances
map.
A map is laid out in the storage where the map entries are scattered over the entire storage space using the (256-bit) hash of each key to determine the location.
The detailed mechanism of calculating the location varies by compilers.
In Vyper, for example, map[key1][key2]
is stored at the location:
hash(hash(key1 ++ slot(map)) ++ key2)
where slot(map)
is the position index of map
in the program, and ++
is byte-array concatenation, while in Solidity, it is stored at:
hash(key2 ++ hash(key1 ++ slot(map)))
The eDSL provides #hashedLocation
that allows to uniformly specify the locations in a form parameterized by the underlying compilers.
For maps, the location of map[key1][key2]
can be specified as follows, where {COMPILER}
is a place-holder to be replaced by the name of the compiler.
Note that the keys are separated by the white spaces instead of commas.
#hashedLocation({COMPILER}, slot(map), key1 key2)
This notation makes the specification independent of the underlying compilers, enabling it to be reused for differently compiled programs.
For dynamically sized arrays in Solidity, and both statically and dynamically sized arrays in Vyper, the length of the array is stored at:
hash(slot(array))
and the element at index i
is stored at:
hash(slot(array)) + i
More information about how storage locations are defined in Solidity can be found here.
Specifically, #hashedLocation
is defined as follows, capturing the storage layout schemes of Solidity and Vyper.
syntax Int ::= #hashedLocation( String , Int , IntList ) [function, klabel(hashLoc), smtlib(hashLoc)]
// -----------------------------------------------------------------------------------------------------
rule #hashedLocation(_LANG, BASE, .IntList ) => BASE
rule #hashedLocation( LANG, BASE, OFFSET OFFSETS) => #hashedLocation(LANG, #hashedLocation(LANG, BASE, OFFSET .IntList), OFFSETS) requires OFFSETS =/=K .IntList
rule #hashedLocation("Vyper", BASE, OFFSET .IntList) => keccak(#bufStrict(32, BASE) ++ #bufStrict(32, OFFSET)) requires #rangeUInt(256, BASE) andBool #rangeUInt(256, OFFSET)
rule #hashedLocation("Solidity", BASE, OFFSET .IntList) => keccak(#bufStrict(32, OFFSET) ++ #bufStrict(32, BASE)) requires #rangeUInt(256, BASE) andBool #rangeUInt(256, OFFSET) [simplification]
rule #hashedLocation("Array", BASE, OFFSET .IntList) => keccak(#bufStrict(32, BASE)) +Word OFFSET requires #rangeUInt(256, BASE) andBool #rangeUInt(256, OFFSET)
syntax IntList ::= List{Int, ""} [klabel(intList), smtlib(intList)]
// -------------------------------------------------------------------
endmodule
module SOLIDITY-FIELDS
imports HASHED-LOCATIONS
syntax Contract
syntax Field
syntax ContractAccess ::= Contract
| ContractAccess "." Field [klabel(contract_access_field), symbol]
| ContractAccess "[" Int "]" [klabel(contract_access_index), symbol]
// --------------------------------------------------------------------------------------------
syntax Int ::= #loc ( ContractAccess ) [function, klabel(contract_access_loc), symbol]
// --------------------------------------------------------------------------------------
rule #loc(_:Contract) => 0
rule #loc(C [ I ]) => #hash(#loc(C), I)
syntax Int ::= #hash ( Int , Int ) [function, klabel(contract_access_hash), symbol]
// -----------------------------------------------------------------------------------
rule #hash(I1, I2) => keccak(#bufStrict(32, I2) ++ #bufStrict(32, I1))
endmodule