# 合约编程误区

## 继承覆盖关键变量

```javascript
pragma solidity >=0.4.22 <0.6.0;

contract owned {
    address public owner;

    constructor() public {
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner, "owner calls only!");
        _;
    }
}

contract XXX_POS is owned {
    address private owner;

    function distributeDividends(uint256 amount)
        public
        onlyOwner
        returns (bool)
    {
        require(address(this).balance >= amount, "!invalid withdraw amount");
        owner.transfer(amount);
        return true;
    }
}
```

如上合约代码，关键功能性函数已忽略。

看似 `onlyOwner` modifier 工作正常，且整个合约没有修改 `owner` 的入口，那么按照逻辑，只有合约作者可以通过调用 `distributeDividends` 获得收益。

然而实际执行中，合约的余额并没有发送给 `owner`, 而是被发到了 `T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb` 这样一个地址。通过调用 `owner()` 检查，并没有发现 `owner` 被修改。

问题出在 `address private owner;` 一句，该私有变量申明通过合约继承链，屏蔽掉了父合约 `owned` 中的真正的 `owner` 变量。所以在 `owner.transfer()` 中使用的地址是一个未初始化的 `address` 类型，值为 `"410000000000000000000000000000000000000000"`, 用 base58check 编码后正好是 `T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb`.

{% hint style="warning" %}
检查合约继承链中的所有变量定义，避免覆盖关键变量。
{% endhint %}

## 使用可预测变量获取随机数

```javascript
pragma solidity >=0.4.22 <0.6.0;

contract RandGuess {
    function genRandom() internal view returns (uint256) {
        return uint256(keccak256(abi.encodePacked(block.difficulty, now)));
    }

    function guess(uint8 value) public payable {
        require(msg.value == 1 trx);
        if (value == uint8(genRandom())) {
            msg.sender.transfer(2 trx);
        }
    }
}
```

如上合约代码，是一个简单的猜数字游戏。

`uint256(keccak256(abi.encodePacked(block.difficulty, now)))` 多见于以太坊合约的随机数生成，然而， 对于 TRON 来说，`block.difficulty` 恒为 0, 且 `now`(即 block.timestamp) 可准确预测。

最终结果是，攻击者可以提前计算随机数值，选择在合适的时间点发送交易，达到控制随机数的目的。

{% hint style="info" %}
检查所有随机数生成源头，避免可预测的随机数。
{% endhint %}

相关细节参考: [波场Solidity智能合约安全实践：合约内随机数的实现](https://zhuanlan.zhihu.com/p/116738596).
