什么是fallback函数:

出处:http://me.tryblockchain.org/blockchain-solidity-fallback.html

回退函数是合约里的特殊函数,没有名字,不能有参数,没有返回值。当调用的函数找不到时,就会调用默认的fallback函数

⚠️Even though the fallback function cannot have arguments, one can still use msg.data to retrieve any payload supplied with the call.

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

pragma solidity ^0.4.24;

contract ExecuteFallback{

  //回退事件,会把调用的数据打印出来
event FallbackCalled(bytes data);
//fallback函数,注意是没有名字的,没有参数,没有返回值的
function() public{
emit FallbackCalled(msg.data);//在这里返回的是functionNotExist()函数签名0x69774a91
} //调用已存在函数的事件,会把调用的原始数据,请求参数打印出来
event ExistFuncCalled(bytes data, uint256 para);
//一个存在的函数
function existFunc(uint256 para) public {
emit ExistFuncCalled(msg.data, para);
} // 模拟从外部对一个存在的函数发起一个调用,将直接调用函数
function callExistFunc() public returns(bool){
bytes4 funcIdentifier = bytes4(keccak256("existFunc(uint256)"));
return address(this).call(funcIdentifier, uint256(1));
} //模拟从外部对一个不存在的函数发起一个调用,由于匹配不到函数,将调用回退函数
function callNonExistFunc() public returns(bool){
bytes4 funcIdentifier = bytes4(keccak256("functionNotExist()"));
return address(this).call(funcIdentifier);
}
}

调用callExistFunc,返回:

调用callNonExistFunc,有调用fallback函数返回,而且要注意,这里call的返回值也为true:

⚠️一个没有定义一个回退函数的合约。如果接收ether,会触发异常,并返还ether(solidity v0.4.0开始)。所以合约要接收ether,必须实现回退函数。

  • balance 和 transfer

可以通过地址的balance属性来查看一个地址的余额,发送以太币(单位为:wei)到一个地址可以使用 transfer方法

 

address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);//将合约this中的10wei转到账户地址x

  注意:如果x是一个合约地址,它的代码(如果存在的话,更明确是为 fallback 函数)将会和 transfer 调用一起被执行,send也一样(这是EVM的限制,是不可阻止的)。如果执行过程中gas不够或是失败,当前合约会终止抛出异常

1)send(),有返回值bool

举例说明:

pragma solidity ^0.4.24;

contract SendFallback{

  //fallback函数及其事件
event FallbackTrigged(bytes data);
function() public payable{//一定要声明为payable,否则send()执行结果将会始终为false
emit FallbackTrigged(msg.data); }
//存入一些ether用于后面的测试
function deposit() public payable{
} //查询当前的余额
function getBalance() public view returns(uint){
return address(this).balance;
} event SendEvent(address to, uint value, bool result);
//使用send()发送ether,观察会触发fallback函数
function sendEther() public{
bool result = address(this).send(1);//从合约地址的余额中发送1wei给它自己,所以其balance不会变,只是会消耗msg.sender账户gas
emit SendEvent(this, 1, result);
}
}

一开始,调用getBalance函数得到合约地址中余额为0,这时候如果调用sendEther函数,result将为false,也没有调用fallback函数:

然后通过调用deposit函数传入value = 5,使得合约地址账户余额为5,这时候再调用sendEther函数,result将为true,并且调用了fallback函数,附带的数据是0x(bytes类型的默认空值),空数据:

因为是address(this).send(1),所以调用getBalance函数发现并不会有改变,还是5
如果改为msg.sender.send(1),就不是自己给自己了,而是从合约账户中传1wei给msg.sender账户,调用getBalance函数值变为了4 2)transfer(),没有返回值
但是如果改成使用transfer的话,发现并没有调用fallback函数,是不是后面设置为transfer不调用fallback函数了???????:
pragma solidity ^0.4.24;

contract SendFallback{
...event SendEvent(address to, uint value);
//使用send()发送ether,观察会触发fallback函数
function sendEther() public{
msg.sender.transfer(1);
emit SendEvent(msg.sender, 1);
}
}

返回:

后面发现了原因,这里有一个概念没有搞明白,就是调用的fallback函数与你所指定的地址有关。比如上面的例子中,使用的是msg.sender.transfer(1),那么将意味着如果msg.sender为一个合约地址,就调用它里面写的fallback函数,如果不是合约地址,那么自然就没有fallback函数调用,所以这里的结果才会没有调用fallback函数,所以如果我们将其改成address(this).transfer(1),就会发现果然调用了:

3)call.value(),有返回值bool

改成msg.sender.call.value(1)():

pragma solidity ^0.4.24;

contract SendFallback{
...
event SendEvent(address to, uint value,bool result);
//使用send()发送ether,观察会触发fallback函数
function sendEther() public{
bool result = msg.sender.call.value(1)();
emit SendEvent(msg.sender, 1,result);
}
}

返回也没有触发fallback函数,这是因为这里调用的是msg.sender.call.value(1)(),而不是address(this).call.value(1)():

将msg.sender.call.value(1)改为address(this).call.value(1)()后,就会发现还是会触发fallback函数:

从上面我们可以看出这三个调用都会调用访问其的地址的fallback函数,这会有危险。

fallback中的限制

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

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

  • 向区块链中写数据( x =1;)
  • 创建一个合约
  • 调用一个external的函数
  • 发送ether

所以一般,我们只能在fallback函数中进行一些日志操作

举例说明:

pragma solidity ^0.4.24;

contract Test {
event FallbackTrigged1(bytes data);
function() external { emit FallbackTrigged1(msg.data); }
function getBalance() public view returns(uint){
return address(this).balance;
}
} contract Sink {
event FallbackTrigged2(bytes data);
function() external payable {emit FallbackTrigged2(msg.data); }
function getBalance() public view returns(uint){
return address(this).balance;
}
} contract Caller {
function deposit() payable public{}
function callTest(Test test) public returns (bool) {
require(address(test).call(abi.encodeWithSignature("nonExistingFunction()")));//一般call进行调用都返回true,不管里面的函数是否存在
return address(test).send(2 ether);
}
}

在这个例子中callTest(Sink合约地址)会成功:

callTest(Test合约地址)失败,因为没有payable:

如果改为:

pragma solidity ^0.4.24;

contract Test {
function() external { x = 1;}
uint x;
function getBalance() public view returns(uint){
return address(this).balance;
}
} contract Sink {
function() external payable { x = 1;}
uint x;
function getBalance() public view returns(uint){
return address(this).balance;
}
}

则两个都会失败,返回形如下面的结果:

说明gas的限制果然是起作用的

但是好像要是想要写复杂的操作也是可以的,但是没有查到呢,查到再补充?????????

solidity fallback函数的更多相关文章

  1. solidity的函数修改器(modifier)

    内容:modifier的定义.modifier对函数参数的操作.modifier执行的顺序 modifier的定义 官方文档:modifier可以改变函数的行为.可以被继承和重写. 其实modifie ...

  2. solidity 学习笔记(6)call 函数

    call() 方法 call()是一个底层的接口,用来向一个合约发送消息,也就是说如果你想实现自己的消息传递,可以使用这个函数.函数支持传入任意类型的任意参数,并将参数打包成32字节,相互拼接后向合约 ...

  3. 智能合约语言 Solidity 教程系列10 - 完全理解函数修改器

    这是Solidity教程系列文章第10篇,带大家完全理解Solidity的函数修改器. Solidity系列完整的文章列表请查看分类-Solidity. 写在前面 Solidity 是以太坊智能合约编 ...

  4. 智能合约语言Solidity教程系列2 - 地址类型介绍

    智能合约语言Solidity教程系列第二篇 - Solidity地址类型介绍. 写在前面 Solidity是以太坊智能合约编程语言,阅读本文前,你应该对以太坊.智能合约有所了解,如果你还不了解,建议你 ...

  5. 智能合约语言 Solidity 教程系列2 - 地址类型介绍

    Solidity教程系列第二篇 - Solidity地址类型介绍. 写在前面 Solidity是以太坊智能合约编程语言,阅读本文前,你应该对以太坊.智能合约有所了解,如果你还不了解,建议你先看以太坊是 ...

  6. solidity learning (1)

    学习文档笔记:http://solidity-cn.readthedocs.io/zh/develop/layout-of-source-files.html 1.pragma solidity ^0 ...

  7. solidity学习-cryptoPunks为实例

    在这里使用cryptoPunks为实例来进行solidity的介绍,一般这些内容理解了就能够进行相对简单的智能合约的编写了,同时会添加一些我认为也十分重要的内容学习文档为http://solidity ...

  8. 杂乱的Solidity - 2019-7-13

    要清楚在区块链上开发DApp的架构[x][][][][][]   DApp是去中心化的应用   基于智能合约 去中心化的游戏规则 代币激励  

  9. 如何通过以太坊智能合约来进行众筹(ICO)

    前面我们有两遍文章写了如何发行代币,今天我们讲一下如何使用代币来公开募资,即编写一个募资合约. 写在前面 本文所讲的代币是使用以太坊智能合约创建,阅读本文前,你应该对以太坊.智能合约有所了解,如果你还 ...

随机推荐

  1. win10 关闭自动更新

    方法一 : 利用组策略关闭win10自动更新的步骤如下:1.按win+R打开“运行”,输入“gpedit.msc”,按下回车. 2.找到“计算机配置”→““管理模板”→“Windows 组件”→“Wi ...

  2. Jquery Ajax 调用后台并返回数据

    一.前台调用ajax并解析json对象. $.ajax({ url : '', type : 'POST', //GET data : '’, beforeSend : function(reques ...

  3. 【Spring】10、Spring中用@Component、@Repository、@Service和 @Controller等标注的默认Bean名称会是小写开头的非限定类名

    @Service用于标注业务层组件(我们通常定义的service层就用这个) @Controller用于标注控制层组件(如struts中的action) @Repository用于标注数据访问组件,即 ...

  4. C# Why does '+' + a short convert to 44

    I have a line of code that looks like this: MyObject.PhoneNumber = '+' + ThePhonePrefix + TheBizNumb ...

  5. 网页布局设计css中单位px和em,rem的区别

    国内的设计师大都喜欢用px,而国外的网站大都喜欢用em和rem,那么三者有什么区别,又各自有什么优劣呢? PX特点 1. IE无法调整那些使用px作为单位的字体大小: 2. 国外的大部分网站能够调整的 ...

  6. 前端开发周报: CSS 布局方式方式与JavaScript数据结构和算法

    前端开发周报:CSS 布局方式与JavaScript动画库 1.常见 CSS 布局方式详见: 一些常见的 CSS 布局方式梳理,涉及 Flex 布局.Grid 布局.圣杯布局.双飞翼布局等.http: ...

  7. element-ui select组件中复选时以字符串形式显示

    我使用的element-ui的版本是1.4.13. 如上图所示,使用el-select组件,要实现可搜索.可复选.可创建条目时,展示样式是如上图所示,输入框的高度会撑开,影响页面布局,按照产品的需求, ...

  8. 从输入url到页面展示到底发生了什么

    阅读目录 1.输入地址 2.浏览器查找域名的 IP 地址 3.浏览器向 web 服务器发送一个 HTTP 请求 4.服务器的永久重定向响应 5.浏览器跟踪重定向地址 6.服务器处理请求 7.服务器返回 ...

  9. RxJava2.0的使用详解

    RxJava2.0的使用详解 1,初识RxJava RxJava就是一种用Java语言实现的响应式编程,来创建基于事件的异步程序 RxJava是一个基于事件订阅的异步执行的一个类库,目前比较火的一些技 ...

  10. vue 环境的搭建及初始化项目

    其实超级简单,虽然网上很多,但是我顺便记录下相当于做笔记吧 1nodejs 的安装, 在node官网下载,点击安装,安装的时候最好选择路径在d盘 2设置环境变量 我的电脑-->属性-->系 ...