查看原文
其他

科普 | 为什么使用提款(Withdrawal)模式?

Jim McDonald 以太坊爱好者 2019-01-23


《建立以太坊支付通道》这篇文章引发了这样一个问题:为什么关闭一个支付通道要将资金增加到一个账户,而不是简单地将资金返还给通道参与者。这种机制被称为提款模式,这篇文章将解释其必要性。


当一笔以太坊交易试图将资金释放到不受交易发起人控制的地址时,问题就出现了。一个非常简化的合约例子如下所示:


contract BadSplitter {    uint256 funds;    address sender;    address recipient;    // Deposit some funds in to the contract    function deposit(address other) payable {        funds = msg.value;        sender = msg.sender;        recipient = other;    }    // Split an amount of funds between the sender and the recipient    function split() {        // Can only be called by the recipient        require(msg.sender == recipient);        // Split the funds        sender.transfer(funds / 2);        recipient.transfer(funds / 2);    }

(请注意,本智能合约纯粹是为了说明目的,完全不具有任何实用价值。)


本合约的运作方式如下:


  • 发送方发起一个 deposit() 事务,并附上接收方的地址。

  • 接收方发起一个 split() 事务,该交易将一半的存款发送给接收方,另一半发送回发送方。


或如图所示:



没有代码循环或限制条件,所以控制流程很容易遵循。那会出现什么问题呢?


问题在于,账户和合约都是交易的有效参与者,而作为交易的一部分,当这些合约收到资金时,合约会按照代码来执行。


当合约收到资金时,它会在合约上调用一个特殊的函数,称为回退(Fallback)函数。这允许合约控制调用它的交易的控制流程。


一个纯粹的恶意合约如下所示:


contract BadSender {    // Forward funds to the splitter contract    function forward(address other) payable {        // This is the address of the splitter contract        address splitterContract = 0xAe3aE77F5ab2490C46958D0b05f766871c17cA5e;        BadSplitter splitter = BadSplitter(splitterContract);        splitter.deposit.gas(200000).value(msg.value)(other);    }    // Purely malevolent fallback    function () {        revert();    } }


本合约的运作方式如下:


  • 发送方发起一个 forward() 事务,并附上接收方的地址。这将调用前面描述的恶意拆分合约(BadSplitter Contract)。

  • 当接收方试图在恶意拆分合约中调用拆分 split() 时,他们发现他们的交易失败了。这是因为恶意拆分引起 sender.transfer() 调用恶意拆分中的回退函数,该函数立即失效,并导致整个交易失效。


或如图所示:



重点是要明白:回滚(Reverting)交易会取消原交易采取的所有行动,所以回滚后以太坊的状态就好像从未发生过交易一样。如若没有这个 reverse()  方程,在发送者之前写一个简单的合约,向接受者发送资金就可以避免提款失败。


正如上面所提到的,这是一种纯粹的恶意合约,因为它会无条件地禁止接收者地址获得其资金。然而,鉴于智能合约是一个程序,一个更高级的恶意发送者(BadSender)可以根据时间、某个帐户的余额、一个内部标志等来允许或拒绝交易。恶意的可能性是无止境的,并且发送者还可以要求接收者支付赎金。


在支付通道的情境中,这会有负面的影响,因为当接受者试图关闭通道时,一个恶意发送者可能会回滚交易,不让接受者获得应得的资金。一旦通道在到期时间关闭,发送方可能会终止该通道、接收所有已存的资金,且永久地剥夺接收方所应得的资金。


解决这个问题的办法是使用提款模式。通过跟踪合约内部的余额,并迫使每个用户撤回自己的资金,从而在交易中的另一方可能是恶意合约的事实变得无关紧要。关于提款模式的更多细节可以在 solidity 官方文件中找到。




原文链接: https://medium.com/@jgm.orinoco/why-use-the-withdrawal-pattern-d5255921ca2a
作者: Jim McDonald
翻译&校对: Waterzeong & Elisa


本文由作者授权 EthFans 翻译及再出版。


你可能还会喜欢:

干货 | 代币支付的以太坊智能服务
引介 | 域名销售:一个链上二级ENS市场
教程 | 在区块链上建立可更新的智慧合约(一)<https://ethfans.org/posts/how-to-build-updateable-smart-contract-part-1>


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存