Solidity – How are Mappings Found in Storage in Geth?

go-ethereumsolidity

Code –

pragma solidity ^0.4.18;

contract CA4 {


mapping(address=>uint16) public balances;

function getBalance() view returns (uint16) {
uint16 mybalance = balances[msg.sender];
return mybalance;
}

function setBalance(uint16 newbalance) public {
balances[msg.sender]=newbalance;
}


}

Geth –

> eth.getStorageAt(con.address,0)
"0x0000000000000000000000000000000000000000000000000000000000000000"
>  var key = web3.sha3("0x0000000000000000000000000000000000000000000000000000000000000000", {encoding:"hex"});
undefined
>  var bn = web3.toBigNumber(key);
undefined
> eth.getStorageAt(con.address, bn)
"0x0000000000000000000000000000000000000000000000000000000000000000"
> eth.getStorageAt(con.address,1)
"0x0000000000000000000000000000000000000000000000000000000000000000"
> con.getBalance()
1000

The array balances is stored somewhere. I am unsure how to find it. I tried a method which worked when we had an array of structs. But that did not work for the mapping of addresses to uint16.

How can we find the actual storage of balances using for example getStorageAt(con.address, slot)?

More Details –

> eth.defaultAccount
"0x46fb9a22689c4a4bfb494baeafbb8b2993725305"
> key=web3.sha3("0x46fb9a22689c4a4bfb494baeafbb8b2993725305",  {encoding:"hex"});
"0x0679d661b585d1b4f0c3fd18f943f19e7fa9777fe23cb96b94f469c20d6f0bd5"
> var bn = web3.toBigNumber(key);
undefined
> eth.getStorageAt(con.address, bn)
"0x0000000000000000000000000000000000000000000000000000000000000000"
> 

Best Answer

I finally solved it.

  1. The key is the actual key in the array.
  2. The pos is the displacement of that data definition from the start of the contract storage.

Then to solve this you just put the key (ie the address) into the a key+pos sha3 and convert to a bigNumber. The vital part also is to remove 0x and to format as 64 chars.

Here is the final output -

> key
"00000000000000000000000046fb9a22689c4a4bfb494baeafbb8b2993725305"
> key1
"000000000000000000000000f07044ba718e8eb25850be0e534f9522018784be"
> pos
"0000000000000000000000000000000000000000000000000000000000000001"
> 

Pos is 1 because I added a uint to the contract before the mapping to test this. The two keys are then the accounts with 0x removed and reformatted to 64 chars. ie.

> eth.coinbase
"0x46fb9a22689c4a4bfb494baeafbb8b2993725305"
> eth.accounts[1]
"0xf07044ba718e8eb25850be0e534f9522018784be"

> bn=web3.sha3(key + pos, {"encoding":"hex"})
"0x4a6915a70ddb253ab9075c26d94720491095a5a0a6d31c6720a4db10b12f661e"

> bn1=web3.sha3(key1 + pos, {"encoding":"hex"})
"0x4ecac4dae9466f9f535b4d7b3d80aedd57bdd0e6e6e74425ed865c2fa5e20707"

> sn1=web3.toBigNumber(bn1)
3.5638663940699095975789856456193070520684880277631386156092330925546436429575e+76

> sn=web3.toBigNumber(bn)
3.3656819177407030101749625369691302081266253965792325840314023187955028289054e+76

> eth.getStorageAt(con.address,sn1)
"0x0000000000000000000000000000000000000000000000000000000000001770"

> eth.getStorageAt(con.address,sn)
"0x0000000000000000000000000000000000000000000000000000000000001edc"

> con.getAddressBalance(eth.coinbase)
7900
> con.getAddressBalance(eth.accounts[1])
6000

Here is the amended contract -

pragma solidity ^0.4.18;

contract CA4 {

uint public pos;
mapping(address=>uint16) public balances;

function getBalance() view returns (uint16) {
uint16 mybalance = balances[msg.sender];
return mybalance;
}

function getAddressBalance (address myaddress) view returns (uint16) {
return balances[myaddress];
}

function setAddressBalance(address myaddress, uint16 newbalance) public {
pos=9;
balances[myaddress]=newbalance;
}

function setBalance(uint16 newbalance) public {
pos=9;
balances[msg.sender]=newbalance;
}


}
Related Topic