Solidity学习::(17)fallback函数

fallback函数


在合约调用没有匹配到函数签名,或者调用没有带任何数据时被自动调用。


 声明方式:

没有名字,不能有参数,没有返回值

pragma solidity ^0.4.0;

contract SimpleFallback{
  function(){
    //fallback function
  }
}

简单例子:

由于Solidity编辑器remix中,提供了编译期检查,所以我们不能直接通过Solidity调用一个不存在的函数。但我们可以使用Solidity的提供的底层函数address.call来模拟这一行为

pragma solidity ^0.4.0;

contract ExecuteFallback{

  //回退事件,会把调用的数据打印出来
  event FallbackCalled(bytes data);
  //fallback函数,注意是没有名字的,没有参数,没有返回值的
  function(){
    FallbackCalled(msg.data);
  }

  //调用已存在函数的事件,会把调用的原始数据,请求参数打印出来
  event ExistFuncCalled(bytes data, uint256 para);
  //一个存在的函数
  function existFunc(uint256 para){
    ExistFuncCalled(msg.data, para);
  }

  // 模拟从外部对一个存在的函数发起一个调用,将直接调用函数
  function callExistFunc(){
    bytes4 funcIdentifier = bytes4(keccak256("existFunc(uint256)"));
    this.call(funcIdentifier, uint256(1));
  }

  //模拟从外部对一个不存在的函数发起一个调用,由于匹配不到函数,将调用回退函数
  function callNonExistFunc(){
    bytes4 funcIdentifier = bytes4(keccak256("functionNotExist()"));
    this.call(funcIdentifier);
  }
}

(1)我们调用callExistFunc(),这个方法后,返回日志,可以看到返回的值跟传入的参数一致,是正常的调用

(2)调用callNonExistFunc(),这个方法,返回的日志是如下图,可以发现当没有找到对应函数可调用时,会默认调用fallback函数


 ether发送send()

address.send(ether to send)向某个合约直接转帐时,address指向的合约必须有fallback函数,且为payable的,才可以接收到发送来的ether,因为:address.send(ether to send)这个行为没有发送任何数据,所以接收合约总是会调用fallback函数

合约实例测试:

  • 两个合约,两个合约均有发送ether的函数,一个合约有fallback函数且为payable,另一个合约没有fallback函数
  • 部署时,两个合约同时存入100
  • 两个合约相互发送ether,看结果如何
pragma solidity ^0.4.0;

contract SendWithFallback{
  function SendWithFallback() payable { //构造函数,部署时用来存入
  }

  //fallback函数及其事件
  event fallbackTrigged(bytes data);
  function() payable{fallbackTrigged(msg.data);}

  //查询当前的余额
  function getBalance() constant returns(uint){
      return this.balance;
  }

  event SendEvent(address to, uint value, bool result);
  //使用send()发送ether
  function sendEther(address _addto){
      bool result = _addto.send(3);
      SendEvent(_addto, 1, result);
  }
}

contract SendWithoutFallback{
  function SendWithoutFallback() payable { //构造函数,部署时用来存入
  }

  //查询当前的余额
  function getBalance() constant returns(uint){
      return this.balance;
  }

  event SendEvent(address to, uint value, bool result);
  //使用send()发送ether
  function sendEther(address _addto){
      bool result = _addto.send(3);
      SendEvent(_addto, 1, result);
  }
}

测试步骤:

(1)部署,两个合约都存入100

Solidity学习::(17)fallback函数_第1张图片

(2)先用不带Fallback的合约向Fallback的合约发送ether

发送会成功,触发事件

Solidity学习::(17)fallback函数_第2张图片

再看两合约的余额

Solidity学习::(17)fallback函数_第3张图片

(3)再用带Fallback的合约向不带Fallback的合约发送ether 

返回结果result为false,表明发送ether是失败的,

Solidity学习::(17)fallback函数_第4张图片

再看看两合约的余额,并没有变化

Solidity学习::(17)fallback函数_第5张图片

(4)因此,要接收ether的合约,要有fallback函数,且为payable属性的 


fallback的限制:

send()函数总是会调用fallback,这个行为非常危险,著名的DAO被黑也与这有关。如果我们在分红时,对一系列帐户进行send()操作,其中某个做恶意帐户中的fallback函数实现了一个无限循环,将因为gas耗尽,导致所有send()失败。为解决这个问题,send()函数当前即便gas充足,也只会附带限定的2300gas,故而fallback函数内除了可以进行日志操作外,你几乎不能做任何操作。

下述行为消耗的gas都将超过fallback函数限定的gas值:

 

注意:上述仅对使用send()方式的有2300gas的限制,对使用call()方式没有这样的限制。 

  • 向区块链中写数据
  • 创建一个合约
  • 调用一个external的函数
  • 发送ether


参考文章:http://me.tryblockchain.org/blockchain-solidity-fallback.html

你可能感兴趣的:(区块链学习)