您正在查看: Ethereum-开源推荐 分类下的文章

缩小以太坊合同规模,以应对合同体积限制

为什么有限制?

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");

每次修改完,重新编译合约,然后查看优化大小,是否已到达临界内

参考

https://soliditydeveloper.com/max-contract-size

solidity中的Mapping遍历

开源地址:https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol

库代码

/// @dev Models a uint -> uint mapping where it is possible to iterate over all keys.
library IterableMapping
{
  struct itmap
  {
    mapping(uint => IndexValue) data;
    KeyFlag[] keys;
    uint size;
  }
  struct IndexValue { uint keyIndex; uint value; }
  struct KeyFlag { uint key; bool deleted; }
  function insert(itmap storage self, uint key, uint value) returns (bool replaced)
  {
    uint keyIndex = self.data[key].keyIndex;
    self.data[key].value = value;
    if (keyIndex > 0)
      return true;
    else
    {
      keyIndex = self.keys.length++;
      self.data[key].keyIndex = keyIndex + 1;
      self.keys[keyIndex].key = key;
      self.size++;
      return false;
    }
  }
  function remove(itmap storage self, uint key) returns (bool success)
  {
    uint keyIndex = self.data[key].keyIndex;
    if (keyIndex == 0)
      return false;
    delete self.data[key];
    self.keys[keyIndex - 1].deleted = true;
    self.size --;
  }
  function contains(itmap storage self, uint key) returns (bool)
  {
    return self.data[key].keyIndex > 0;
  }
  function iterate_start(itmap storage self) returns (uint keyIndex)
  {
    return iterate_next(self, uint(-1));
  }
  function iterate_valid(itmap storage self, uint keyIndex) returns (bool)
  {
    return keyIndex < self.keys.length;
  }
  function iterate_next(itmap storage self, uint keyIndex) returns (uint r_keyIndex)
  {
    keyIndex++;
    while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
      keyIndex++;
    return keyIndex;
  }
  function iterate_get(itmap storage self, uint keyIndex) returns (uint key, uint value)
  {
    key = self.keys[keyIndex].key;
    value = self.data[key].value;
  }
}

案例

// How to use it:
contract User
{
  // Just a struct holding our data.
  IterableMapping.itmap data;
  // Insert something
  function insert(uint k, uint v) returns (uint size)
  {
    // Actually calls itmap_impl.insert, auto-supplying the first parameter for us.
    IterableMapping.insert(data, k, v);
    // We can still access members of the struct - but we should take care not to mess with them.
    return data.size;
  }
  // Computes the sum of all stored data.
  function sum() returns (uint s)
  {
    for (var i = IterableMapping.iterate_start(data); IterableMapping.iterate_valid(data, i); i = IterableMapping.iterate_next(data, i))
    {
        var (key, value) = IterableMapping.iterate_get(data, i);
        s += value;
    }
  }
}

python Ethereum Event Explorer

开源地址:https://github.com/apolonsoft/ethereum-event-explorer

How to run

python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
python3 event_listener.py

ETH 批量发送交易

关于
令牌Multisender Dapp智能合约。空投令牌。批量发送ERC20,ETH,以太坊令牌。在几笔交易中发送数千笔转帐。与一一发送相比,它可以帮助用户节省更多的发送费用和时间

演示:https://multisender.app/
开源:https://github.com/rstormsf/multisender