Ethernaut-Vault — how to read the secret?

Eszymi
Coinmonks
3 min readAug 24, 2022

--

If we want to save something inside our contract, but we don’t want others could read it, we will use just private state (more about state here), won’t we? It’s not so simple. Every information saved in blockchain is public, even if it is private variable. So why do we use private variable, and what is different between them? Public variable every one could easily read, because after compile we could call her like function and get value. With private, it doesn’t work that and if we want to read its value we have to use a special function. But let’s begin from the start.

We know that Solidity saves information in binary form, which could be represented in hexadecimals (more about binary and hexadecimals here), but where exactly this information is contained?

Solidity uses storage with 2²⁵⁶ slots. Each slot has 32 bytes. Data is stored sequentially in the order of declaration. This storage is optimized to save space. If neighboring variables fit in a single 32 bytes, then they are packed into the same slot, starting from the right
Solidyty by Example

Optimized of space made by Solidity. The picture comes from 0xSage

So every contract has own storage which contains value of all variables. Access to it is free, but you will need a special function: web3.eth.getStorageAt. It needs address of contract and number of slot you are interesting of. And it all.

Unless you will use mapping or dynamic array. If you use one of them, reading from storage will be a bit harder. Why that and why only dynamic array? Let’s begin from compare dynamic and static array.

A static array is an array which has determinate size from the start. We write this in Solidity like bytes32[3] public data. And this informs us that data is a three elements array where every element of the array is bytes32. Therefore, if variable before array will be safe in slot number n, then elements of this array will be in slots number: n+1, n+2 and n+3. And it all.

When we create a dynamic array, created in Solidity, e.g. bytes32[] public data, compiler doesn’t know how much space it should book for this array. It is a problem, because if it would work similar to static array, and compiler would assume for example that this array need k slots. So it will write next variable out of booked space. But when our array would need more than k slots, it will start to overwrite other variable. We definitely don’t want it. The problem with mapping is similar.

So how does our compiler work with these two? Let’s assume that the next free slot is called p. If the next variable to write to storage is dynamic array, the compiler will put p into the keccak256 function. The result of this function will be place in storage where the compiler start saving array. The next elements from the array will be in the slot number
keccak256(p) + (index * elementSize).

Now let’s focus on the mapping. Again, presume that the first unoccupied slot is number p, and we want to save there a mapping. In this case, our compiler will save the value assigned to the key in the slot of number
keccak256(key, slot).

I hope you find this post useful. If you have any idea, how could I make my posts better, let my know. I am always ready to learn. You can connect with me on LinkedIn and Telegram.

If you would like to talk with me about this or any other topic I wrote, feel free. I’m open to conversation.

Happy learning!

New to trading? Try crypto trading bots or copy trading

--

--