3分钟Solidity: 11.4 访问私人数据

28 阅读5分钟

欢迎订阅专栏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 尝试一下