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
(2)先用不带Fallback的合约向带Fallback的合约发送ether
发送会成功,触发事件
再看两合约的余额
(3)再用带Fallback的合约向不带Fallback的合约发送ether
返回结果result为false,表明发送ether是失败的,
再看看两合约的余额,并没有变化
(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