您当前的位置:主页 > 区块链 >

关于波场智能合约fallback 函数的特性

2019-05-20 11:28 来源:互联网 编辑:运营003
摘要: 
函数的特性以及如何有效避免重入攻击。同时,欢迎大家关注 TRON官方 twitter,踊跃投稿合约代码。

本期小课堂结合 TRON-Rich 团队的 RichBank 合约,谈一谈波场智能合约fallback 函数的特性以及如何有效避免重入攻击。同时,欢迎大家关注 TRON官方 twitter,踊跃投稿合约代码。

老规矩,开讲之前,先通过https://troneye.com (以下简称 TRON-Eye)验证合约源码的真实性。

TRON-Eye 是来自社区的波场合约验证平台,上一期小课堂对 TRON-Eye 验证平台进行了详细的介绍。下图所示的即为TRON-Rich 团队的TEoSMNi7WvVHXe2NxxDw2AeLhiwLJXxQYL RichBank合约的源码。

关于波场智能合约fallback 函数的特性

RichBank 合约是个资金盘合约,大部分逻辑和上一期介绍的 UsdtBank 类似。这里着重关注的有2个细节。

1. Fallback 函数

关于波场智能合约fallback 函数的特性

图2所示即为RichBank 的 fallback 函数,在 fallback 函数中它记录了来自某个地址的这笔转账,或者完成该地址的取款请求。当通过 grpc 接口 调用合约一个不存在的函数;或在 solidity 中直接调用不存在的函数时,亦或直接在 Solidity 中给该地址 transfer() 或者 send() TRX 时,都会执行目标合约的fallback 函数。这种特性被很多开发者用来记录用户转账。

但是,这种方式有两个需要关注的地方。

第一个,fallback 函数的energy问题。由于 transfer 和 send 函数,只能附带2300的 Energy,而对一个32 byte数据的读取(SSLOAD指令)、修改(SSTORE指令)和创建(SSTORE 指令)分别用到了400,5000和20000 Energy。所以,2300的 Energy 不足以创建和修改数据。也就是说,如果一个能量消耗超过2300的 fallback 函数,会导致在 solidity 中对该地址调用 transfer和 send 必然失败。

第二个,波场相对以太坊,还提供了“免费转账”的普通转账交易,GRPC 中的 transfercontract 类型的交易。该交易只消耗带宽,不消耗 Energy。由于该交易不消耗Energy,所以不会触发合约的 fallback 函数。(也就是说,直接通过 tronscan 对该合约地址的普通 trx 转账,并不会触发 fallback 函数。)

上述是所有依赖 fallback 函数记录用户所有转账的合约,必须知晓的2个问题。他们决定了,所有需要记录的转账交易,都必须采用triggersmartcontract 方式调用合约来给合约发送 trx。其中第1个是 Solidity 解决重入攻击的基础,在可见的将来,都不会改变;第2个是波场“免费转账”特性,我们也正在TIP37 (链接:https://github.com/tronprotocol/TIPs/issues/37)讨论是否要停止支持对合约地址进行 transfercontract类型的交易,欢迎踊跃参与讨论。

2. 如何有效避免重入攻击

在 Solidity 中,调用其他地址的函数或者给合约地址转账,都会把自己的地址作为被调合约的 msg.sender 传递过去。此时,如果传递的energy 足够的话,就可能会被对方就行重入攻击。

防止重入攻击,最简单粗暴的方式就是,在 solidity 中禁止对合约地址进行操作。这个可以通过上一期讲到的 require(msg.sender == tx.origin) 和 extcodesize() > 0,来基本上实现。RichBank 采用了 msg.sender == tx.origin ,但是在两个位置,留下了空隙。

分别如下:

function grant(address addr, uint256 _planId) public whenNotPaused isHuman payable {

uint256 grantorUid = address2UID[msg.sender];

bool isAutoAddReferrer = true;

uint256 referrerCode = 0;

if (grantorUid != 0 && isAutoAddReferrer) {

referrerCode = grantorUid;

}

if (_invest(addr,_planId,referrerCode,msg.value)) {

emit onGrant(msg.sender, addr, msg.value);

}

}

这段代码,值判断了调用者是否为合约,并没有判断传入的 addr 参数是否为合约。同时,对于 fallback 函数,这个问题更加严重,它甚至没有判断调用者是否为合约。这两处都是该合约后续可以完善的地方。

function() external payable {

if (msg.value == 0) {

withdraw();

} else {

invest(0, 0); //default to buy plan 0, no referrer

}

}

但是,允许其他合约参与投注,并不一定会构成重入攻击。前面说过,在 Solidity 中对合约地址调用transfer/sender,只会传递2300的 Energy,这不足以发起一次重入攻击。所以要一定避免通过 callvalue 的方式,调用未知的合约地址。因为 callvalue 可以传入远大于2300的 Energy。

[ 编辑: 运营BX01 ]

中国金融领域第一媒体

更多服务
友情链接

京ICP备11011451号-1

举报热线:(010)12377

举报邮箱:xinhua_ljzjr#ljzjr.cn(#替换@)

合作QQ:1447260813

中国金融时报网 版权所有 Copyright © 2010 - 2018 ljzjr.cn All Rights Reserved.