蜜罐合约-老版本solidity引用类型的一个坑

都是引用类型不赋初值惹的祸

一个例子:

注意看变量a的初始值为0:

当调用f()函数后:

a的值居然变成了1,而且多次调用f函数会发现每次a的值都会增加1。

这也太坑了!

原因在于:创建数组x的操作uint[] x没有添加初始的值,所以x保存的地址指向是默认指向整个合约的开始位置,也就是变量a的位置/地址。而Solidity中数组的机制是会使用初始的位置保存整个数组的长度值,而数组的值则是用这个长度和下标哈希映射到某个地方存储。**这个记录数组长度的位置就是a变量的位置,a等同于在记录局部数组x的长度!**所以当使用push操作的时候,数组的长度变为1,那么a的值也就变成了1。

解决方法:

真实案例 – 蜜罐合约

猜数游戏:猜中了就双倍返回金额,GuessHistory用来记录每次猜的信息

合约内容:

pragma solidity >=0.4.0;

contract A{
    uint public luckNum = 52;
    struct Guess{
        address player;
        uint num;
    }
    Guess[] public guessHistory;
    
    function guess(uint _num) public payable{
        Guess newGuess;
        newGuess.player = msg.sender;
        newGuess.num = _num;
        guessHistory.push(newGuess);
        if(_num == luckNum){
            msg.sender.transfer(msg.value * 2);
        }
    }
    
}

因为以太坊上的合约的内容都是可见的,所以luckNum别人是可以直接看到的,并且在这例子中还设置了public更加方便知道数字了。(这不傻子吗?我还能猜不中?)

猜52,下注10ether

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PI0GvEUf-1596979218147)(http://xwjpics.gumptlu.work/qiniu_picGo/20200809204955.png)]

记录上也啥都没有

明明猜对了啊?为什么?

问题所在:Guess newGuess这一步

struct和上面的uint[]一样都是引用类型,这里没有给其赋初始值或者说指定到某个空间,所以这里的newGuess指针就会指向合约的开头,也就是uint public luckNum = 52;!!

再次查看luckNum的值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rriNDYwC-1596979218153)(http://xwjpics.gumptlu.work/qiniu_picGo/20200809205315.png)]

居然变成了这样,这个数就是msg.sender地址形成的数!与num无关是因为luckNum只够存struct中的一个参数-地址

如果把结构体中的num换到第一个吗,那么就会变成怎么猜都是对的了,因为_num会修改luckNum的值,每次都猜对!把自己坑了。

把这个数复制,再猜这个数:

为啥这次luckNum不改变了呢,因为msg.sender没有改变。

但是,真实的情况下,luckNum是不会public的,所以钱没法拿回来,自以为是反而会被坑!

总结

在老版本的solidity中,局部变量引用类型要赋初值,指向某个区域!

本文地址:https://blog.csdn.net/weixin_43988498/article/details/107900500

(0)
上一篇 2022年3月22日
下一篇 2022年3月22日

相关推荐