function deposit() public payable { balanceOf[msg.sender] += msg.value; totalSupply += msg.value; assert(this.balance >= totalSupply); } }
注意断言保护 不是 严格意义的余额检测, 因为智能合约可以不通过deposit() 函数被** 强制发送Ether**!
正确使用****assert()和require()
在Solidity 0.4.10 中assert()和require()被加入。require(condition)被用来验证用户的输入,如果条件不满足便会抛出异常,应当使用它验证所有用户的输入。
assert(condition) 在条件不满足也会抛出异常,但是***只用于固定变量:内部错误或你的智能合约陷入无效的状态。遵循这些范例,使用分析工具来验证永远不会执行这些无效操作码:意味着代码中不存在任何不变量,并且代码已经正式验证。
小心整数除法的四舍五入
所有整数除数都会四舍五入到最接近的整数。 如果您需要更高精度,请考虑使用乘数,或存储分子和分母。
(将来Solidity会有一个fixed-point类型来让这一切变得容易。)
// bad uint x = 5 / 2; // Result is 2, all integer
divison rounds DOWN to the nearest integer
// good uint multiplier = 10;
uint x = (5 * multiplier) / 2;
uint numerator = 5;
uint denominator = 2;
记住Ether可以被强制发送到账户
谨慎编写用来检查账户余额的不变量。
攻击者可以强制发送wei到任何账户,而且这是不能被阻止的(即使让fallback函数throw也不行)
攻击者可以仅仅使用1 wei来创建一个合约,然后调用selfdestruct(victimAddress)。在victimAddress中没有代码被执行,所以这是不能被阻止的。
不要假设合约创建时余额为零
攻击者可以在合约创建之前向合约的地址发送wei。合约不能假设它的初始状态包含的余额为零。浏览issue 61 获取更多信息。
(地址:https://github.com/ConsenSys/smart-contract-best-practices/issues/61)
记住链上的数据是公开的
许多应用需要提交的数据是私有的,直到某个时间点才能工作。游戏(比如,链上游戏rock-paper-scissors(石头剪刀布))和拍卖机(比如,sealed-bid second-price auctions)是两个典型的例子。如果你的应用存在隐私保护问题,一定要避免过早发布用户信息。
例如:
在游戏石头剪刀布中,需要参与游戏的双方提交他们“行动计划”的hash值,然后需要双方随后提交他们的行动计划;如果双方的“行动计划”和先前提交的hash值对不上则抛出异常。
在拍卖中,要求玩家在初始阶段提交其所出价格的hash值(以及超过其出价的保证金),然后在第二阶段提交他们所出价格的资金。
当开发一个依赖随机数生成器的应用时,正确的顺序应当是(1)玩家提交行动计划,(2)生成随机数,(3)玩家支付。产生随机数是一个值得研究的领域;当前最优的解决方案包括比特币区块头(通过验证),hash-commit-reveal方案(比如,一方产生number后,将其散列值提交作为对这个number的“提交”,然后在随后再暴露这个number本身)和 RANDAO。
如果你正在实现频繁的批量拍卖,那么hash-commit机制也是个不错的选择。
权衡Abstract合约和Interfaces
Interfaces和Abstract合约都是用来使智能合约能更好的被定制和重用。Interfaces是在Solidity 0.4.11中被引入的,和Abstract合约很像但是不能定义方法只能申明。
Interfaces存在一些限制比如不能够访问storage或者从其他Interfaces那继承,通常这些使Abstract合约更实用。尽管如此,Interfaces在实现智能合约之前的设计智能合约阶段仍然有很大用处。另外,需要注意的是如果一个智能合约从另一个Abstract合约继承而来那么它必须实现所有Abstract合约内的申明并未实现的函数,否则它也会成为一个Abstract合约。
在双方或多方参与的智能合约中,参与者可能会“脱机离线”后不再返回
不要让退款和索赔流程依赖于参与方执行的某个特定动作而没有其他途径来获取资金。比如,在石头剪刀布游戏中,一个常见的错误是在两个玩家提交他们的行动计划之前不要付钱。然而一个恶意玩家可以通过一直不提交它的行动计划来使对方蒙受损失 -- 事实上,如果玩家看到其他玩家泄露的行动计划然后决定他是否会损失(译者注:发现自己输了),那么他完全有理由不再提交他自己的行动计划。这些问题也同样会出现在通道结算。当这些情形出现导致问题后:(1)提供一种规避非参与者和参与者的方式,可能通过设置时间限制,和(2)考虑为参与者提供额外的经济激励,以便在他们应该这样做的所有情况下仍然提交信息。
使Fallback函数尽量简单