Solidity 映射 Mapping

发布于 2024-09-26 22:51:05 字数 2292 浏览 5 评论 0

映射类型提供了键到值的数据存取能力。一个映射中,通过提供一个特定的键,可以存取与这个键相对应的值。

Solidity 这中,映射以 mapping(键的类型 => 值的类型) 的形式声明。比如, mapping(address => uint) 声明了一个地址uint 的映射。映射中,键的类型不可以是动态数组合约枚举结构体映射本身,其他类型则没有问题;值的类型没有以上限制,可以是包括映射在内的任何类型。此外,与其他语言的类似容器不同,Solidity 的映射不支持遍历,如果需要,可以寻找第三方的库,或手动维护一个数组来实现遍历的可能。

映射只允许作为状态变量,不过它也可以作为 storage 上的引用类型出现在合约的内部函数里。

与其他类型一样,通过用 public 可见性修饰符修饰,Solidity 会自动生成该映射的 getter 函数。最简单的情况下,没有数组和映射的嵌套, getter 函数需要一个参数作为键,返回对应的值。否则, getter 所需的参数会递归地为每一个键和索引而增加。

映射在 storage 中的存储形式

动态数组类似,映射storage 中的存储方式也很特殊。映射 在初始化时也会在 storage 的某位置 $p$ 占有一个槽位。但与动态数组不同的是,此槽位仅仅用来区分不同的映射,操作映射类型并不会用到此处的值。对于映射 $m$ 中每一个键 $k_x$,都可以通过函数 $f(p, k_x) = keccak256(k . p)$ 得到一个 32 字节长的哈希值。此哈希值便是映射 $m$ 中,键 $k_x$ 所对应的值在 storage 中的地址。同数组一样,由于碰撞的可能性实在太小, 因此不会检测此地址是否发生碰撞。

由于这种设计,Solidity 的映射并不能自行实现遍历。 Solidity 的映射没有键是否被被使用的概念,也没有存储"设置"过的键,直接将键所对应的值零散地存放在整个 Storage 中,即使可以遍历其中存放的所有的值,也无法判断那些值属于哪里。不过这个问题技术上并不难解决,声明一个用于记录目标映射的数组,在向映射插入值的同时,把对应的键同时记入该数组中。需要遍历映射的时候,用该数组中所存的键即可。

如何在 geth 中找到 mapping 的存储位置

注意:

  • 在数组中,key 就是 key
  • mapping 中,pos 是合约存储时数据定义的位置(pos 是存储槽位置)。

因此, sha3(key+pos) 是 mapping 真正的 key。geth 中查找还需要转换为 bigNumber 然后 hex 格式化

> key
"00000000000000000000000046fb9a22689c4a4bfb494baeafbb8b2993725305"

> pos
"0000000000000000000000000000000000000000000000000000000000000001"


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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

绝不服输

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

新人笑

文章 0 评论 0

mb_vYjKhcd3

文章 0 评论 0

小高

文章 0 评论 0

来日方长

文章 0 评论 0

哄哄

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文