以太坊看上去EIP 1884正在伊斯坦布尔软叉行进。这一变化减少了sload操作者的gas成本,因此超越了一些现有的智能合约。
这些合约将裂痕,因为它们的fallback函数过去消耗的gas多于2300,现在它们将消耗更加多。为什么2300gas是最重要的? 如果通过Solidity的transfer()或send()方法调用合约的fallback函数,则它是合约的fallback函数。
自从transfer()引进以来,它一般来说被安全性社区引荐,因为它有助避免轻进反击。在gas成本恒定的假设下,这一指导意见是有意义的,但事实证明这一假设是错误的。
我们现在建议防止transfer()和send()。气体成本可以也将转变evm反对的每个操作码都有一个涉及的gas成本。例如,sload从存储器中加载一个单词,目前(但不是很长时间)必须200气体。
gas的价格不是随便的。它们目的体现包含以太坊的节点上每个操作者所消耗的底层资源。EIP的动机部分:操作者价格与资源消耗(CPU时间、内存等)之间的不均衡有几个缺点:· 它可以用作反击,通过填满区块与定价过较低的操作者,造成区块处置时间过长。· 定价过较低的操作码不会造成区块gas限值弯曲,有时区块气体较慢已完成,但其他类似于gas用于的块体较慢已完成。
· 如果操作者均衡,我们可以最大限度地提升区块气限,并有一个更加平稳的处置时间。sload历年来定价过较低,而eip 1884缺失了这一点。
智能合约无法倚赖气体成本如果gas成本不会发生变化,那么智能合约就无法依赖任何特定的gas成本。任何用于transfer()或send()的智能合约都会通过发送相同gas数量2300来严苛倚赖gas成本。我们的建议是暂停在代码中用于transfer()和send(),改回用于call():contract Vulnerable {function withdraw(uint256 amount) external {// This forwards 2300 gas, which may not be enough if the recipient// is a contract and gas costs change.msg.sender.transfer(amount);}}contract Fixed {function withdraw(uint256 amount) external {// This forwards all available gas. Be sure to check the return value!(bool success, ) = msg.sender.call.value(amount)("");require(success, "Transfer failed.");}}这两份智能合约除了gas的传输数量有所不同,其余都是大于的。可交接性?(Reentrancy )期望您在看见上述代码时首先考虑过这个问题。
引进transfer()和send()的全部原因是为了解决问题DAO臭名昭着的黑客攻击的原因。其思想是2300 gas不足以收到一个日志条目,但足以收到一个可交接调用,然后改动存储。不过,请求忘记,gas成本可能会发生变化,这意味著无论如何,这是解决问题轻进问题的差劲方法。
今年早些时候,君士坦丁堡叉子被延期了,因为减少gas成本造成先前安全性的代码无法再行转入。如果我们仍然用于transfer()和send(),我们将被迫以更加强壮的方式避免轻进。幸运地的是,这个问题有很好的解决方案。检查 - 效果 - 交互模式避免轻进错误的最简单方法是用于检查-效果-交互模式。
这是一个可交接错误的典型例子:1contract Vulnerable {2...34function withdraw() external {5uint256 amount = balanceOf[msg.sender];6(bool success, ) = msg.sender.call.value(amount)("");7require(success, "Transfer failed.");8balanceOf[msg.sender] = 0;9}10}如果msg.sender是智能合约,它在第6行有机会在第7行再次发生之前再度调用withdraw()。在第二次调用中,balanceOf [msg.sender]依然是完整金额,因此将再度传输。
这可以根据必须反复多次以消耗智能合约。检查 - 效果 - 交互模式的点子是保证所有交互(外部调用)最后再次发生。上述代码的典型修缮方法如下:1contract Fixed {2...34function withdraw() external {5uint256 amount = balanceOf[msg.sender];6balanceOf[msg.sender] = 0;7(bool success, ) = msg.sender.call.value(amount)("");8require(success, "Transfer failed.");9}10}请注意,在此代码中,余额在传输之前被清零,因此尝试对withdraw()展开可交接调用将会使攻击者获益。
用于Reentrancy 维护避免交接的另一种方法是具体检查和拒绝接受此类调用。这是一个非常简单版本的reentrancy 维护,你可以看见这个点子:1contract Guarded {2...34bool locked = false;56function withdraw() external {7require(!locked, "Reentrant call detected!");8locked = true;9...10locked = false;11}12}用于此代码,网卓新闻网,如果尝试轻进调用,第7行上的require将拒绝接受它,因为lock仍设置为true。在OpenZeppelin的ReentrancyGuard合约中可以寻找一个更加简单、更加省gas的版本。
如果从ReentrancyGuard承继,则只需用于nonReentrant标记函数以避免轻进。请注意,此方法仅有在显式将其应用于所有准确的函数时才维护您。由于必须在储存中维持一定的价值,这也减少了gas成本。
Vyper如何?Vyper的send()函数用于与Solidity的transfer()完全相同的硬编码gas,因此也不应防止用于。你可以转用raw_call。Vyper内置了一个@nonreentrant(unique_key)装饰器,其工作方式与OpenZeppelin的ReentrancyGuard类似于。总结1. 在假设gas成本恒定的情况下,引荐transfer()是有意义的。
2. gas成本并不恒定。智能合约对这个事实应当是完善的。Solidity的transfer()和send()用于软编码的gas量。
3. 应当防止这些方法。请求转用.call.value(...)(“”)。4. 这就带给了新的转入的风险。请求保证用于可用作避免轻进漏洞的强壮方法之一。
5. vyper的send()也有某种程度的问题。
本文关键词:开元官网平台,停止,使用,Solidity,的,transfer,函数,以太,坊
本文来源:开元官网平台-www.softwareforbad.com