Skip to content

Instantly share code, notes, and snippets.

@swkim109
Created March 13, 2023 01:03
Show Gist options
  • Save swkim109/a2ddcbc1ff1cb9d798b1fd0f9f12ad93 to your computer and use it in GitHub Desktop.
Save swkim109/a2ddcbc1ff1cb9d798b1fd0f9f12ad93 to your computer and use it in GitHub Desktop.
mapping storage
mapping 타입의 스토리지 해시
저장되는 슬롯 번호 = keccak256(key || slot No.)
(예)
mapping(address => uint256) public m;
m[0x5B38Da6a701c568545dCfcB03FcB875f56beddC4] = 100;
슬롯번호는
ethers.utils.keccak256("0x0000000000000000000000005B38Da6a701c568545dCfcB03FcB875f56beddC40000000000000000000000000000000000000000000000000000000000000000")
= 0x58f8e73c330daffe64653449eb9a999c1162911d5129dd8193c7233d46ade2d5
슬롯에 해당하는 스토리지 해시
= 0x2f4efd012f30b85c3b205250c3dad4cd9208919ba8889723a8325ec6826f69e1
구조체일때
struct UserInfo {
uint256 id;
uint256 age;
}
mapping(address => UserInfo) public m;
m[0x5B38Da6a701c568545dCfcB03FcB875f56beddC4] = UserInfo({id: 100, age: 50});
구조체의 각 필드의 위치는 처음 슬롯번호부터 차례로 +1, +2...
id의 슬롯번호
ethers.utils.keccak256("0x0000000000000000000000005B38Da6a701c568545dCfcB03FcB875f56beddC40000000000000000000000000000000000000000000000000000000000000000")
= 0x58f8e73c330daffe64653449eb9a999c1162911d5129dd8193c7233d46ade2d5
age의 슬롯번호
0x58f8e73c330daffe64653449eb9a999c1162911d5129dd8193c7233d46ade2d5 + 1
= 0x58f8e73c330daffe64653449eb9a999c1162911d5129dd8193c7233d46ade2d6
@swkim109
Copy link
Author

swkim109 commented Nov 2, 2023

//SPDX-License-Identifier:MIT
pragma solidity ^0.8.20;
 
contract MyContract {
 
   mapping (string => uint) public m;
 
   constructor() {
       m["foo"] = 100;
   }
 
}

key 가 string 타입일 경우

const s = ethers.utils.toUtf8Bytes("foo");
const v = ethers.utils.concat([s,"0x0000000000000000000000000000000000000000000000000000000000000000"]);

슬롯번호 s = ethers.utils.keccak256(v) = 0x58710dca1e2a45afe5496e8d02017d2788e01040086e5de4c6d09b2c1a60473d
슬롯해시 = ethers.utils.keccak256(s) = 0x8f098fdec8d49f366ac3ab21b3c2455b0a419f443611b0573a9a9744054b4412

@swkim109
Copy link
Author

swkim109 commented Nov 2, 2023

in Remix

{
	"0x8f098fdec8d49f366ac3ab21b3c2455b0a419f443611b0573a9a9744054b4412": {
		"key": "0x58710dca1e2a45afe5496e8d02017d2788e01040086e5de4c6d09b2c1a60473d",
		"value": "0x64"
	}
}

@swkim109
Copy link
Author

swkim109 commented Nov 3, 2023

{
  "id": 0,
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
      "0x054AfC9C126105526F5Dc27FFf27F9E00d5fCBD9",
      "0x58710dca1e2a45afe5496e8d02017d2788e01040086e5de4c6d09b2c1a60473d",
      "latest"
  ]
}

{
    "jsonrpc": "2.0",
    "id": 0,
    "result": "0x0000000000000000000000000000000000000000000000000000000000000064"
}

@swkim109
Copy link
Author

swkim109 commented Nov 3, 2023

MPT의 구조는?

{
  "id": 0,
  "jsonrpc": "2.0",
  "method": "eth_getProof",
  "params":  [
      "0x054AfC9C126105526F5Dc27FFf27F9E00d5fCBD9",
      ["0x58710dca1e2a45afe5496e8d02017d2788e01040086e5de4c6d09b2c1a60473d"],
      "latest"
  ]
}

스토리지 트리의 노드는 leaf 노드 하나 밖에 없다.

"storageHash": "0x9c3c7f5b29952928a22cb71982914b3a0677794eddfa4e00cc038ff1ca48e4b0",
        "storageProof": [
            {
                "key": "0x58710dca1e2a45afe5496e8d02017d2788e01040086e5de4c6d09b2c1a60473d",
                "value": "0x64",
                "proof": [
                    "0xe3a1208f098fdec8d49f366ac3ab21b3c2455b0a419f443611b0573a9a9744054b441264"
                ]
            }
        ]

proof는 leaf 노드의 RLP 인코딩이다. leaf 노드는 [key, value] 구조이고 key는 슬롯번호의 해시이다. leaf 노드만 있고 경로의 길이가 32바이트 짝수이므로 hex prefix 인코딩 규칙에 따라 key에 0x20이 추가된 것이다.

const node = ethers.utils.RLP.encode(["0x208f098fdec8d49f366ac3ab21b3c2455b0a419f443611b0573a9a9744054b4412", "0x64"]);
// 0xe3a1208f098fdec8d49f366ac3ab21b3c2455b0a419f443611b0573a9a9744054b441264

storageHash는 RLP 인코딩 결과의 해시가 된다.

ethers.utils.keccak256("0xe3a1208f098fdec8d49f366ac3ab21b3c2455b0a419f443611b0573a9a9744054b441264")
// 0x9c3c7f5b29952928a22cb71982914b3a0677794eddfa4e00cc038ff1ca48e4b0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment