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

Ajian   |     |   504 次阅读

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

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

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() 事务,该交易将一半的存款发送给接收方,另一半发送回发送方。

或如图所示:

1

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

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

当合约收到资金时,它会在合约上调用一个特殊的函数,称为回退(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市场
教程 | 在区块链上建立可更新的智慧合约(一)

 
0 人喜欢