区块链审计 如何测试solidity的bool值占用几个字节

文章目录


艾里卡的bool类型有多大?

木森和艾丽卡坐在他们的实验室里,面前摆着一本魔法书和一些奇怪的魔法工具。他们正在进行一项重要的研究------探索Solidity智能合约中不同数据类型占用的存储空间。

木森(兴奋地):"艾丽卡,看!我们已经设置好了实验环境。现在,我们要测试一下bool类型在智能合约中占用多少字节。"

艾丽卡(好奇地):"木森,什么是字节?能吃吗?"

木森(耐心地):"不,艾丽卡,字节不是食物。在魔法编程的世界里,字节是我们存储信息的基本单位。就像我们的魔法书,每一页都能记录很多魔法咒语。"

艾丽卡(恍然大悟):"哦,我明白了!那我们怎么开始测试呢?"

木森(指着电脑屏幕):"首先,我们要在Solidity中创建一个简单的合约,里面包含一个bool类型,还有一些其他类型的变量。"

艾丽卡(兴奋地):"然后呢?"

木森(认真地):"然后我们用一个神奇的命令,叫做cast storage,它会告诉我们每个变量在存储中的地址。"

艾丽卡(跳起来):"哇!那我们快试试吧!"

木森(输入命令):"好的,我已经输入了命令。现在,我们看看结果。"

他们等待了一会儿,电脑屏幕上显示出了结果。

木森(分析结果):"看,艾丽卡!bool类型和uint8类型占用的存储空间是一样的,都是一个字节。"

艾丽卡(惊讶地):"真的吗?那其他的数字类型呢?"

木森(继续分析):"我们可以用同样的方法测试uint16、uint32、uint64、uint128和uint256。每种类型占用的存储空间都是不同的。"

代码环节

示例:以下的一切测试是基于foundry环境搭建的

solidity 复制代码
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

contract Booltest{
   bool private x;
    uint8 private y =1;
    uint32 private z =0x22;
}

我们先编写一个最简单的合约,在这里只有一个bool类型以及后面的uint8和uint32类型,我们首先测试它占用多少字节。现在让我们运行,

bash 复制代码
cast storage 0x5FbDB2315678afecb367f032d93F642f64180aa3  0 --rpc-url http://127.0.0.1:8545   
0x0000000000000000000000000000000000000000000000000000000000220100

可以发现bool类型占用的是0x00,紧随其后的是unit8,它正好是0x01,0001他们都是两位,可以看出它们的大小是相同的。

艾丽卡更精确的测试bool

艾丽卡(兴奋地):"哇,那我们快试试吧!"

木森在魔法终端上输入了咒语,然后他们看到了结果:

bash 复制代码
0x0000000000000000000000000000000000000000000000000000000000220100

艾丽卡(疑惑):"木森,这个结果是什么意思?我只看到了一串数字。"

木森(耐心地):"这里的00代表bool类型的初始化值,01代表uint8类型的值是1。你看他们都有两位,这说明bool类型只占用了一个字节。"

艾丽卡(不满足):"但是,木森,这个结果不够精确。我们怎么知道它不是占用了更多的空间,只是我们看到的是00呢?"

木森(思考):"你说得对,艾丽卡。我们需要一个更精确的方法来验证。我们可以用hash运算来测试。如果两个值的hash值相同,那么它们的值就相同,特别是这个0,00,000这里面的虽然都是0,但是他们的hash肯定是不同的。"

艾丽卡(眼睛一亮):"好主意!那我们快用这个新咒语来测试吧!"

木森开始编写一个新的智能合约,用来计算每种类型的存储槽键的hash值。他使用了keccak256这个强大的hash函数,并且用console2.log来输出结果。

代码环节

不同uint类型的数据的0,在被放入储存槽之后,占用的是不同的空间,我们可以通过hash运算来验证,如果两个数据完全一致,那么他们的hash也必将相同,可是如果是0和00这样的虽然在值上面相同,但是hash值不相同

solidity 复制代码
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import  {Test} from "lib/forge-std/src/Test.sol";
import "lib/forge-std/src/console2.sol";

contract BooltestTest is Test {
    bool public boolVar;
    uint8 public uint8Var;
    uint16 public uint16Var;
    uint32 public uint32Var ;
    uint64 public uint64Var ;
    uint128 public uint128Var;
    uint256 public uint256Var;
    function setUp() public {
      
    }

    function testBoolAndUint8TypeSizes() public {
        
            // 计算存储槽的键
        console2.log("uint8 slot key:", uint256(keccak256(abi.encodePacked(uint8Var))));
        console2.log("uint16 slot key:", uint256(keccak256(abi.encodePacked(uint16Var))));
        console2.log("uint32 slot key:", uint256(keccak256(abi.encodePacked(uint32Var))));
        console2.log("uint64 slot key:", uint256(keccak256(abi.encodePacked(uint64Var))));
        console2.log("uint128 slot key:", uint256(keccak256(abi.encodePacked(uint128Var))));
        console2.log("uint256 slot key:", uint256(keccak256(abi.encodePacked(uint256Var))));
        console2.log("Storage slot for bool:", uint256(keccak256(abi.encodePacked(boolVar))));
    
  }
}

我们可以看到通过这样的比较,

bash 复制代码
  [13858] BooltestTest::testBoolAndUint8TypeSizes()
    ├─ [0] console::log("uint8 slot key:", 85131057757245807317576516368191972321038229705283732634690444270750521936266 [8.513e76]) [staticcall]
    │   └─ ← [Stop] 
    ├─ [0] console::log("uint16 slot key:", 38292439343987868037672198288784556730787963328266909981497454499372014593944 [3.829e76]) [staticcall]
    │   └─ ← [Stop] 
    ├─ [0] console::log("uint32 slot key:", 105345537983141833900523177836038591426164988092870088428104961074093597336652 [1.053e77]) [staticcall]
    │   └─ ← [Stop] 
    ├─ [0] console::log("uint64 slot key:", 500549258012437878224561338362079327067368301550791134293299473726337612750 [5.005e74]) [staticcall]
    │   └─ ← [Stop] 
    ├─ [0] console::log("uint128 slot key:", 110620294328144418057589324861608220015688365608948720310623173341503153578932 [1.106e77]) [staticcall]
    │   └─ ← [Stop] 
    ├─ [0] console::log("uint256 slot key:", 18569430475105882587588266137607568536673111973893317399460219858819262702947 [1.856e76]) [staticcall]
    │   └─ ← [Stop] 
    ├─ [0] console::log("Storage slot for bool:", 85131057757245807317576516368191972321038229705283732634690444270750521936266 [8.513e76]) [staticcall]
    │   └─ ← [Stop] 
    └─ ← [Stop] 

所有的uint类型都有不同的hash值,但是bool和uint8的hash值是一样的!

艾丽卡(兴奋地):"快看,木森!所有的uint类型都有不同的hash值,但是bool和uint8的hash值是一样的!"

木森(满意地):"是的,艾丽卡。这说明在Solidity中,bool类型确实只占用了一个字节的空间。"

bool的gas疑惑?

小镇的法师塔里,木森和艾丽卡正围坐在一张堆满了魔法书和卷轴的桌子旁。木森的眼睛在一本古老的魔法书上快速扫过,而艾丽卡则好奇地凑过来,她的大眼睛里充满了疑惑。

艾丽卡(疑惑地):"木森,这本书上说用uint256代替bool类型可以节省gas,这是怎么做到的呀?"

木森(思考片刻):"这个嘛,艾丽卡,这其实是一个关于魔法世界中资源管理的小技巧。在Solidity语言中,bool类型只占用1个字节,而uint256占用32个字节。但是,当涉及到存储和检索数据时,以太坊网络是以32字节的小块进行操作的。"

艾丽卡(更加困惑):"等等,如果uint256占用更多空间,为什么会更节省gas呢?"

木森(微笑):"这里的关键不在于占用的空间大小,而在于网络如何处理这些数据。想象一下,我们的魔法书不是一页一页的,而是每32页作为一个单元来保存。即使你只写了一页,我们也需要支付存储整个32页单元的费用。"

艾丽卡(恍然大悟):"哦,我明白了!所以如果我们用uint256,即使它占用了32页,也只算一个单元的费用,而不是bool那样,虽然只写了一页,但还是要为整个32页单元付费!"

木森(点头):"正是这样!而且,当我们需要修改bool值时,如果它单独占用一个存储单元,我们可能需要支付额外的gas来更改它,因为即使是微小的改变,网络也会将其视为对整个32字节单元的修改。"

艾丽卡(眼睛闪闪发光):"哇,这真是太聪明了!那我们是不是应该在所有的地方都使用uint256来代替bool呢?"

木森(摇摇手指):"也不尽然,艾丽卡。虽然在某些情况下使用uint256可以节省gas,但我们也要考虑代码的可读性和逻辑的清晰度。有时候,直接使用bool类型会让我们的魔法咒语更直观、更容易理解。"

艾丽卡(认真地):"而且,部署的时候似乎更长的会消耗更多的储存槽,消耗更多的gas?"

木森(疑惑):"啊?这个我也不太懂了"

艾丽卡(兴奋地):"那我们快去实验一下吧!我想看看这个技巧在实际中是怎么工作的。"

木森(无奈):"不行,不行。今天太饿了,我需要吃东西"

相关推荐
nina_LeXin39 分钟前
Mina protocol - 体验教程
web3·区块链·密码学·零知识证明
BeepCrypto7 小时前
NFT Insider #147:Sandbox 人物化身九月奖励上线;Catizen 付费用户突破百万
区块链
Mindfulness code11 小时前
区块链当前发展和未来展望
区块链
山师第一深情11 小时前
solidity-21-call_contract
区块链
易保全区块链存证13 小时前
科技赋能司法:易保全如何重塑法律文书签署与庭审流程
科技·区块链·易保全·互联网司法服务
qiquandongfc13 小时前
50ETF期权可以当天买卖吗?
区块链·程序员创富
YSGZJJ13 小时前
股指期货的详细玩法功能与应用解析
区块链
ClonBrowser20 小时前
从用户数据到区块链:Facebook如何利用去中心化技术
去中心化·区块链·facebook
Miyazaki_Hayao1 天前
初探全同态加密1 —— FHE的定义与历史回顾
区块链·同态加密
科技与数码2 天前
华南医电科技集团受邀出席中马建交50周年高级别经贸合作交流活动
人工智能·科技·区块链