为什么有限制?
2016年 11月22日,Spurious Dragon硬叉推出了 EIP-170,该协议增加了24.576 kb的智能合约大小限制。对于您作为Solidity开发人员而言,这意味着当您向合同中添加越来越多的功能时,在某些时候您将达到极限,并且在部署错误时会看到以下错误:
Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.
引入此限制是为了防止拒绝服务(DOS)攻击。任何签订合同的呼吁都是相对便宜的。但是,合约调用对以太坊节点的影响取决于被调用合约代码的大小(从磁盘读取代码,预处理代码,将数据添加到Merkle证明中)不成比例地增加。每当您遇到这种情况时,攻击者只需很少的资源即可为他人带来很多工作,那么您就有可能遭受DOS攻击。
最初,这没有什么问题,因为一个自然合同大小限制是块气限制。显然,需要在包含合同所有字节码的事务中部署合同。如果然后仅将一个事务包含在一个块中,则可以用尽所有气体,但是它不是无限的。但是,在这种情况下,问题在于阻气限值会随时间变化,并且在理论上是不受限制的。在EIP-170时,块气限制仅为470万。现在,区块气体限制上个月才再次增加到1190万。
进行优化
对各个合约进行优化之前,我们要先知道各个合约各自的体积,Truffle -contract-size插件是帮助您的一个好工具,如果您使用的是Truffle
安装插件
npm install truffle-contract-size
配置插件
在truffle-config.js文件中加入
plugins: ["truffle-contract-size"]
运行插件
Run truffle run contract-size
此时就可以看到各个合约的各自的体积了
针对合约进行压缩
分拆合约部分逻辑到子合约
这应该始终是您的第一种方法。您如何将合同分成多个较小的合同?通常,它会迫使您为合同设计出良好的体系结构。从代码可读性的角度来看,始终首选较小的合同。对于拆分合同,请问自己:
- 哪些功能属于同一类?每套功能在其自己的合同中可能是最好的。
- 哪些功能不需要读取合同状态或仅读取状态的特定子集?
- 您可以拆分存储和功能吗?
减少中间memory变量的创建
比如
function get(uint id) returns (address,address) {
MyStruct memory myStruct = myStructs[id];
return (myStruct.addr1, myStruct.addr2);
}
修改为
function get(uint id) returns (address,address) {
return (myStructs[id].addr1, myStructs[id].addr2);
}
相差0.28kb。您很可能会在合同中找到许多类似的情况,而这些情况实际上加起来可观。
缩短错误提示
比如
require(msg.sender == owner, "Only the owner of this contract can call this function");
修改为
require(msg.sender == owner, "OW1");
每次修改完,重新编译合约,然后查看优化大小,是否已到达临界内