Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rule for string->uint mapping and the storage position #1550

Closed
hievan2016 opened this issue Jan 7, 2017 · 19 comments
Closed

Rule for string->uint mapping and the storage position #1550

hievan2016 opened this issue Jan 7, 2017 · 19 comments
Labels
waiting for more input Issues waiting for more input by the reporter

Comments

@hievan2016
Copy link

hievan2016 commented Jan 7, 2017

Env

solc --version
solc, the solidity compiler commandline interface
Version: 0.4.7+commit.822622cf.Linux.g++

./geth version
Geth
Version: 1.4.7-stable-667a386d
Protocol Versions: [63 62 61]
Network Id: 1
Go Version: go1.5.4
OS: linux

Problem

We are trying to figure out the mapping rule and storage position of saved data
base on this formula

keccack(LeftPad32(key, 0), LeftPad32(map position, 0))

which is mentioned at

https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat

however, seems this works for uint->string mapping, but not for string->uint
mapping, could anyone pls help on this?

Below is the test contract

contract testMapping{
mapping(string=>uint) s2i; //position is 0
mapping(uint=>string) i2s; //position is 1

function setStr(string key) {
    s2i[key] = 100;            
}

function setInt(uint key) {
    i2s[key] = "zzz";
}

}

then we use the mapping to save the data by calling

setStr("aaa") // key is aaa (ascii : 616161)
setInt(200) // key is 200 (hex : c8)

the contract storage is

storage: {
345f7c6c888721344af4147de0834159e0b302300ba13c4e7b6c0b60d8f2314e: "a07a7a7a0000000000000000000000000000000000000000000000000000000006",
69e08a2904d77becc5ebdff9265102784e25436cf0dfbaeb5c932ed98ffc834d: "64"
}

the first entry shows "zzz" (ascii : 7a7a7a) is saved at position "345f7c6c888721344af4147de0834159e0b302300ba13c4e7b6c0b60d8f2314e",
this position could be got from the formula.

but when using "aaa" as the key to do string->uint mapping to save value 100 (0x64),
we cannot get the correct storage position "69e08a2904d77becc5ebdff9265102784e25436cf0dfbaeb5c932ed98ffc834d"
based on the formula, you can see, the sha3 value is incorrect.

var key = "0000000000000000000000000000000000000000000000000000000000616161" + "0000000000000000000000000000000000000000000000000000000000000000"
web3.sha3(key, {"encoding":"hex"})

"0x15b5515e45c94bd2fbc0aa103673f2c4899d2996fe1407ba1e6e5ef9ac66c2f5"

@chriseth
Copy link
Contributor

Since strings are arbitrary length, you have to hash them first.

@chriseth chriseth added the waiting for more input Issues waiting for more input by the reporter label Jan 10, 2017
@hievan2016
Copy link
Author

@chriseth

I still cannot get the correct hash value "69e08a2904d77becc5ebdff9265102784e25436cf0dfbaeb5c932ed98ffc834d", could you help check whether I miss anything?

var key0 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616161"
var key1 = web3.sha3(key0, {"encoding":"hex"})
var key2 = key1 + "0000000000000000000000000000000000000000000000000000000000000000"
web3.sha3(key2, {"encoding":"hex"})

sha3 : "0x8f34d90832af21f9fc31ef2888a966ad8090cdd9bc1a16ecf819059d118ae6db"

var key0 = "616161"
var key1 = web3.sha3(key0, {"encoding":"hex"})
var key2 = key1 + "0000000000000000000000000000000000000000000000000000000000000000"
web3.sha3(key2, {"encoding":"hex"})

sha3 : "0xe4998bf274557cf66b8123e4803a12c6dca7f047052b01d6b4b51776b0eb04d7"

var key0 = "0000000000000000000000000000000000000000000000000000000000616161"
var key1 = web3.sha3(key0, {"encoding":"hex"})
var key2 = key1 + "0000000000000000000000000000000000000000000000000000000000000000"
web3.sha3(key2, {"encoding":"hex"})

sha3 : "0x4b9ff950998e1bfecbf6ed1b5e7531fee7fac216130cafe3d3c03d0b1ba697b8"

@pirapira
Copy link
Member

I would try the array access in the browser-solidity, and chase the stack & memory contents.

@hievan2016
Copy link
Author

We are going to use getStorageAt() API, which requires the storage slot as parameter, so have to do sha3 hash to get the exact storage slot, so could you kindly help on this?

@hievan2016
Copy link
Author

And, in fact, we need to use mapping in this way "mapping(string=>MyStruct)", and found the correct storage slot cannot be got, then simply the mapping to "mapping(string=>uint)" in this posting for easy reading/debugging.

@pirapira
Copy link
Member

For your purpose, you can define

function getStr(string key) returns (uint) {
    return s2i[key];            
}

and call the function from web3.

@hievan2016
Copy link
Author

@pirapira thanks for your reply, however, adding many get...() APIs is not what we want, we prefer using the calculated storage index and then call getStorageAt(), so let's continue figuring out how to calculate the storage slot.

@chriseth looking forwarding to your further input.

@chriseth
Copy link
Contributor

chriseth commented Jan 11, 2017

Ah, I think your original version was fine except for padding the key. So I think the following should work:

var key = "616161"
var pos = "0000000000000000000000000000000000000000000000000000000000000000"
web3.sha3(key + pos, {"encoding":"hex"})

@hievan2016
Copy link
Author

Awesome, it works, thanks a lot!

@ianmonkuk
Copy link

@hievan2016 @chriseth is there anyway to return an element of a mapping without knowing the key but using an index (i.e. pos 1, 2, etc)? Carrying on your example from above, if
i2s["aaa"] = 1
i2s["bbb"] = 2

Assuming we did not know the keys but we knew that i2s was in position 2?

I have worked out how to do this for arrays but not for maps.

Thanks Ian

@VoR0220
Copy link
Member

VoR0220 commented Mar 1, 2017

@ianmonkuk you will have to create an iterated mapping. Search for it in the readthedocs page. You'll get the idea.

@chriseth
Copy link
Contributor

chriseth commented Mar 1, 2017

@ianmonkuk
Copy link

@VoR0220 thanks for responding. I should have clarified that I need to do this in the same way as discussed in this thread - using getStorageAt function which @hievan2016 was using. I am aware that this can be done relatively easily within the contract itself.

@chriseth there's been a lot of questions relate to this on stackexchange and they all point to the page below. This seems to suggest access to map elements (via getStorageAt) can only be accessed if the keys are known.

http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage

Thanks, Ian

@chriseth
Copy link
Contributor

chriseth commented Mar 1, 2017

Yes, you have to know the hash of the key. Some clients can also list storage contents and you might be able to derive the keys from there.

@ianmonkuk
Copy link

ianmonkuk commented Mar 1, 2017 via email

@chriseth
Copy link
Contributor

chriseth commented Mar 1, 2017

It is a bit tricky, because there is another hash function evaluation envolved. If you did a full sync, yes. If you fast-synced, you might not have all values, but I think the geth team is currently building a remote server where you can get the missing values.

@ianmonkuk
Copy link

ok thanks

@zxhubo
Copy link

zxhubo commented Jul 30, 2018

I have a question:
my contract code is :
contract Test1 {

mapping(uint256 => uint256) z;

function test1(){
     z[233]=234;
     z[234]=1;
 }     

}

and I have been deploy this contract used by Remix,then invoke the test1() method. I debug this function In Debugger ,so Can see
the Storage completely loaded:

0x27fdb72814febb9410af57bca307f47028ec5b79245df092b7f15c66aea257e5: Object
    key: 0x28a06524829b6fed74df277d29b0e9d5843614df83ac4257ee812125675fdd54
    value: 0x00000000000000000000000000000000000000000000000000000000000000ea
0x8c96c4d8a0aa2c328c7c6862e662be3530e8741fec6dd703dac17a5374e7e828: Object
    key: 0x4a9ab6c152794dd891130d65b49535f6222d24e4c9a3f06f616c3634b24c9fac
    value: 0x0000000000000000000000000000000000000000000000000000000000000001

I know the hash 0x28a06524829b6fed74df277d29b0e9d5843614df83ac4257ee812125675fdd54 and 0x4a9ab6c152794dd891130d65b49535f6222d24e4c9a3f06f616c3634b24c9fac,
but the hash 0x27fdb72814febb9410af57bca307f47028ec5b79245df092b7f15c66aea257e5 and 0x8c96c4d8a0aa2c328c7c6862e662be3530e8741fec6dd703dac17a5374e7e828 is how to get it,
sorry ,my English is poor ,who can help me to get the value of the hash 0x27fdb72814febb9410af57bca307f47028ec5b79245df092b7f15c66aea257e5 and 0x8c96c4d8a0aa2c328c7c6862e662be3530e8741fec6dd703dac17a5374e7e828

@zxhubo
Copy link

zxhubo commented Jul 30, 2018

@hievan2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting for more input Issues waiting for more input by the reporter
Projects
None yet
Development

No branches or pull requests

6 participants