什么是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. MailBee.NET

    MailBee.NET Objects 是一款为创建.发送.接收以及处理电子邮件而设计的健壮.功能丰富的.NET控件.具备“必需”以及独特的功能,这些控件帮助开发人员简单快速地将复杂的电子邮件功能添加 ...

  2. VB.Net DataSet 填充資料庫內容

    '導入命名空間Imports System.Data.OleDb '定義變量 Dim ds As DataSet = New DataSet() Dim i, cn As Integer Dim Sq ...

  3. (3)Microsoft office Word 2013版本操作入门_段落设定

    1.查看文件: 打开word查看左下角 会显示 word一共有多少页,当前第几页,共多少字等,如下图所示 2.word快速翻页: Ctrl+PageDown  向下翻页, Ctrl+PageUp 向上 ...

  4. 通过jQuery制作电子时钟表的代码

    源码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <titl ...

  5. js之全选即点击全选标签可选择全部复选框

    目标效果:网页全选功能,即点击全选标签可选择全部复选框 实现代码如下 <!DOCTYPE html> <html lang="en"> <head&g ...

  6. Python 中 and 和 or 的短路原则

    对于 and 来说: 如果第一个条件的结论为假,那么 and 前后两个条件组成的表达式计算结果一定为假,后面的条件计算机不会进行计算 对于 or 来说: 如果第一个条件的结论为真,那么 or 前后两个 ...

  7. 使用脚手架快速搭建React项目

    create-react-app是Facebook官方推出的脚手架,基本可以零配置搭建基于webpack的React开发环境步骤: 打开控制台 进入你想要创建项目的目录文件下面 依次执行以下命令 np ...

  8. 部署Redis(脚本安装)

    部署Redis(脚本安装) #/bin/bash # DES:Redis Deploy # Author: will_xue # Email:linuxcto@aliyun.com # DATE : ...

  9. Network-Emulator Network-Emulator-Toolkit网络模拟器使用详细介绍

    Network-Emulator-Toolkit网络模拟器使用详细介绍 by:授客 QQ:1033553122 原理介绍 图1 如上图,一个ADSL用户通过modem连接到网络,通过网络应用如IE,M ...

  10. 怎样制作爽心的 dashboard ?

    在目前的大数据趋势中,数据的大屏可视化成为大家所推崇的一种互动展示模式.如果我们能够早一些了解和掌握这方面的技术,相信对我们的未来将会非常有帮助! 我们知道,通过报表工具实现大屏展示可以通过单张报表. ...