在这篇文章中,我将实现一个简单但完整的以太坊支付通道。支付通道使用密码签名,以安全、即时、无交易费用重复地传送Ether。
什么是支付通道?
以太坊交易提供了一种安全的方式来转账,但每个交易需要被包括在一个区块中和并被挖掘。这意味着交易需要一些时间,并要求支付一些费用来补偿矿工的工作。特别是,这个交易费用使得其产生的这种小额支付,成为了以太坊和其他类似于它的区块链的使用,变得有点儿费劲一个原因。
支付通道允许参与者在不使用交易的情况下重复发送Ether。这意味着可以避免与交易相关的延迟和因此产生费用。在这篇文章中,我们将探讨一个简单的单向支付通道。这包括三个步骤:
- 1.发送者用Ether支付一个智能合约。这会打开支付通道。
- 2.发送者签署消息,指明该ether中应向接收者支付多少。对于每个支付,都重复这一步骤。
- 3.接收者关闭支付通道,收取他们的那部分ether,并将其余部分返回发送者。
重要的是,只有步骤1和步骤3需要空缺交易。步骤2通过密码签名和两方之间的通信(如电子邮件)完成。这意味着只需要两个交易来支持任何数量的发送。
收件人保证收到他们的资金,因为智能合约托管了ether并认可有效签署的消息。智能合约还强制执行直到截止时间,而且发送方有权收回资金,即使接收方拒绝关闭支付通道。
这取决于支付通道的参与者决定多长时间保持开放。对于短时间的交互,例如对于提供网络服务按每分钟支付的网吧,使用只持续一个小时左右的支付通道就足够了。对于一个较长期的支付关系,比如给员工支付按小时计的工资,支付通道可以持续数月或数年。
打开支付通道
为了打开支付通道,发送方部署智能合约,ether也将被托管,并指定接收方和通道存在的最晚截止时间。
contract SimplePaymentChannel {
address public sender; // The account sending payments.
address public recipient; // The account receiving the payments.
uint256 public expiration; // Timeout in case the recipient never closes.
function SimplePaymentChannel(address _recipient, uint256 duration)
public
payable
{
sender = msg.sender;
recipient = _recipient;
expiration = now + duration;
}
支付款项
发送者通过向接收者发送消息来进行支付。该步骤完全在以太坊网络之外执行。消息由发送方进行加密签名,然后直接发送给接收方。
每个消息包括以下信息:
- 智能合约的地址,用来防止跨合约replay攻击。
- 迄今为止,接受者所消耗的ether总量。
在一系列转账结束时,支付通道只关闭一次。正因为如此,只有一个发送的消息将被赎回。这就是为什么每个消息都指定了累积的Ether消耗总量,而不是单个微支付的量。接收者自然会选择赎回最近的消息,因为这是一个总拥有最高ether的消息。
请注意,因为智能合约仅对单个消息进行维护,所以不需要每个临时消息。智能合约的地址仍然用于防止用于一个支付通道的消息被用于不同的通道。
可以用支持加密的hash和签名操作的任何语言构建和签名支付相应的消息。下面的代码是用JavaScript编写的,并且使用ethereumjs-abi:
function constructPaymentMessage(contractAddress, amount) {
return ethereumjs.ABI.soliditySHA3(
["address", "uint256"],
[contractAddress, amount],
);
}
function signMessage(message, callback) {
web3.personal.sign("0x" + message.toString("hex"), web3.eth.defaultAccount,
callback);
}