欢迎订阅专栏:3分钟Solidity--智能合约--Web3区块链技术必学
如需获取本内容的最新版本,请参见Cyfrin.io上“访问私人数据(代码示例)”
漏洞
智能合约上的所有数据都可以被读取。
让我们看看如何读取 private数据。在这个过程中,你将了解 Solidity 如何存储状态变量。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
/*
注意:无法在JVM上使用web3,因此请使用部署在Goerli上的合约
注意:浏览器中的Web3版本较旧,请使用truffle控制台中的Web3
合约已部署在Goerli上
0x534E4Ce0ffF779513793cfd70308AF195827BD31
*/
/*
# 存储
- 2 ** 256 个存储槽
- 每个存储槽 32 字节
- 数据按声明顺序依次存储
- 存储空间经过优化以节省空间。如果相邻变量可以放入单个 32 字节的存储槽中,则它们会从右开始打包到同一个存储槽中
*/
contract Vault {
// slot 0
uint256 public count = 123;
// slot 1
address public owner = msg.sender;
bool public isTrue = true;
uint16 public u16 = 31;
// slot 2
bytes32 private password;
// 常量不使用存储
uint256 public constant someConst = 123;
// 插槽 3、4、5(每个数组元素一个)
bytes32[3] public data;
struct User {
uint256 id;
bytes32 password;
}
// 槽位6 - 数组长度
// 从槽位hash(6)开始 - 数组元素
// 数组元素存储的槽位 = keccak256(槽位) + (索引 * 元素大小)
// 其中槽位 = 6,元素大小 = 2(1(uint)+ 1(bytes32))
User[] private users;
// 插槽7 - 空
// 条目存储在哈希(键,插槽)处
// 其中插槽=7,键=映射键
mapping(uint256 => User) private idToUser;
constructor(bytes32 _password) {
password = _password;
}
function addUser(bytes32 _password) public {
User memory user = User({id: users.length, password: _password});
users.push(user);
idToUser[user.id] = user;
}
function getArrayLocation(uint256 slot, uint256 index, uint256 elementSize)
public
pure
returns (uint256)
{
return
uint256(keccak256(abi.encodePacked(slot))) + (index * elementSize);
}
function getMapLocation(uint256 slot, uint256 key)
public
pure
returns (uint256)
{
return uint256(keccak256(abi.encodePacked(key, slot)));
}
}
/*
槽位0 - 计数器
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", 0, console.log)
槽位1 - u16类型, 布尔值, 所有者
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", 1, console.log)
槽位2 - 密码
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", 2, console.log)
槽位6 - 数组长度
getArrayLocation(6, 0, 2)
web3.utils.numberToHex("111414077815863400510004064629973595961579173665589224203503662149373724986687")
注:我们也可以使用web3获取数据位置
web3.utils.soliditySha3({ type: "uint", value: 6 })
第一位用户
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f", console.log)
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40", console.log)
注:使用web3.toAscii将bytes32转换为字母
第二位用户
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d41", console.log)
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", "0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d42", console.log)
插槽7 - 空
获取地图位置(7, 1)
web3.utils.numberToHex("81222191986226809103279119994707868322855741819905904417953092666699096963112")
注意:我们也可以使用web3来获取数据位置
web3.utils.soliditySha3({ type: "uint", value: 1 }, {type: "uint", value: 7})
用户1
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", "0xb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b828", console.log)
web3.eth.getStorageAt("0x534E4Ce0ffF779513793cfd70308AF195827BD31", "0xb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b829", console.log)
*/
Remix Lite 尝试一下