如何实现以太坊支付

在这篇文章中,我将实现一个简单但完整的以太坊支付通道。支付通道使用密码签名,以安全、即时、无交易费用重复地传送Ether。php

什么是支付通道?

以太坊交易提供了一种安全的方式来转帐,但每一个交易须要被包括在一个区块中和并被挖掘。这意味着交易须要一些时间,并要求支付一些费用来补偿矿工的工做。特别是,这个交易费用使得其产生的这种小额支付,成为了以太坊和其余相似于它的区块链的使用,变得有点儿费劲一个缘由。java

支付通道容许参与者在不使用交易的状况下重复发送Ether。这意味着能够避免与交易相关的延迟和所以产生费用。在这篇文章中,咱们将探讨一个简单的单向支付通道。这包括三个步骤:node

  • 1.发送者用Ether支付一个智能合约。这会打开支付通道。
  • 2.发送者签署消息,指明该ether中应向接收者支付多少。对于每一个支付,都重复这一步骤。
  • 3.接收者关闭支付通道,收取他们的那部分ether,并将其他部分返回发送者。

重要的是,只有步骤1和步骤3须要空缺交易。步骤2经过密码签名和两方之间的通讯(如电子邮件)完成。这意味着只须要两个交易来支持任何数量的发送。python

收件人保证收到他们的资金,由于智能合约托管了ether并承认有效签署的消息。智能合约还强制执行直到截止时间,并且发送方有权收回资金,即便接收方拒绝关闭支付通道。android

这取决于支付通道的参与者决定多长时间保持开放。对于短期的交互,例如对于提供网络服务按每分钟支付的网吧,使用只持续一个小时左右的支付通道就足够了。对于一个较长期的支付关系,好比给员工支付按小时计的工资,支付通道能够持续数月或数年。git

打开支付通道

为了打开支付通道,发送方部署智能合约,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;
    }

支付款项

发送者经过向接收者发送消息来进行支付。该步骤彻底在以太坊网络以外执行。消息由发送方进行加密签名,而后直接发送给接收方。github

每一个消息包括如下信息:web

  • 智能合约的地址,用来防止跨合约replay攻击。
  • 迄今为止,接受者所消耗的ether总量。

在一系列转帐结束时,支付通道只关闭一次。正由于如此,只有一个发送的消息将被赎回。这就是为何每一个消息都指定了累积的Ether消耗总量,而不是单个微支付的量。接收者天然会选择赎回最近的消息,由于这是一个总拥有最高ether的消息。mongodb

请注意,由于智能合约仅对单个消息进行维护,因此不须要每一个临时消息。智能合约的地址仍然用于防止用于一个支付通道的消息被用于不一样的通道。

能够用支持加密的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);
}

// contractAddress is used to prevent cross-contract replay attacks.
// amount, in wei, specifies how much ether should be sent.
function signPayment(contractAddress, amount, callback) {
    var message = constructPaymentMessage(contractAddress, amount);
    signMessage(message, callback);
}

核实付款

与签名不一样,支付通道中的消息不会当即被赎回。接收方跟踪最新消息并在关闭支付通道时赎回。这意味着接收方对每一个消息进行本身的验证是相当重要的。不然,不能保证收件人最终能获得报酬。

接收方应使用如下过程验证每一个消息:

  • 1.验证消息中的合约地址与支付通道相匹配。
  • 2.验证新合计是否为预期金额。
  • 3.验证新的总量不超过ether的量。
  • 4.验证签名是否有效,并来自支付通道发送者。

前三个步骤很简单。最后一步能够经过多种方式执行,可是若是它在JavaScript中完成,我推荐ethereumjs-util库。下面的代码从上面的签名代码中借用constructMessage函数:

// This mimics the prefixing behavior of the eth_sign JSON-RPC method.
function prefixed(hash) {
  return ethereumjs.ABI.soliditySHA3(
    ["string", "bytes32"],
    ["\x19Ethereum Signed Message:\n32", hash]
  );
}

function recoverSigner(message, signature) {
  var split = ethereumjs.Util.fromRpcSig(signature);
  var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s);
  var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex");
  return signer;
}

function isValidSignature(contractAddress, amount, signature, expectedSigner) {
  var message = prefixed(constructPaymentMessage(contractAddress, amount));
  var signer = recoverSigner(message, signature);
  return signer.toLowerCase() ==
    ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase();
}

关闭支付通道

当接受者准备好接收他们的资金时,是时候经过在智能合约上调用close功能来关闭支付通道。关闭通道给接收者,他们得到本身的ether并销毁合约,发送剩余的Ether回发送者。要关闭通道,接收方须要共享由发送方签名的消息。

智能合约必须验证消息包含来自发送者的有效签名。进行此验证的过程与接收方使用的过程相同。isValidSignature recoverSigner函数与前一部分中的JavaScript代码对应。后者是在Signing and Verifying Messages in Ethereum中从ReceiverPays合约中copy来的。

function isValidSignature(uint256 amount, bytes signature)
    internal
    view
    returns (bool)
{
    bytes32 message = prefixed(keccak256(this, amount));

    // Check that the signature is from the payment sender.
    return recoverSigner(message, signature) == sender;
}

// The recipient can close the channel at any time by presenting a signed
// amount from the sender. The recipient will be sent that amount, and the
// remainder will go back to the sender.
function close(uint256 amount, bytes signature) public {
    require(msg.sender == recipient);
    require(isValidSignature(amount, signature));

    recipient.transfer(amount);
    selfdestruct(sender);
}

关闭功能只能由支付通道接收者来调用,而接收者天然会传递最新的支付消息,由于该消息具备最高的总费用。若是发送者被容许调用这个函数,他们能够提供一个较低费用的消息,并欺骗接收者。

函数验证签名的消息与给定的参数匹配。若是一切都被检测出来,收件人就发送了他们的部分ether,发送者经过selfdestruct发送其他部分。

关闭支付通道

接收方能够在任什么时候候关闭支付通道,可是若是他们不这样作,发送者须要一种方法来收回他们的托管资金。在合约部署时设置了expiration时间。一旦到达该时间,发送方能够调用claimTimeout来恢复其资金。

// If the timeout is reached without the recipient closing the channel, then
// the ether is released back to the sender.
function claimTimeout() public {
    require(now >= expiration);
    selfdestruct(sender);
}

在这个函数被调用以后,接收者不再能接收任何ether,因此接收者在到达期满以前关闭通道是很重要的。

总结

  • 支付通道支持安全的、区块链外的资金转移,同时避免每次转帐产生交易费用。
  • 付款是累积的,只有一个是在关闭频道时赎回的。
  • 转帐是经过托管资金和密码签名来保证的。
  • 超时保护发送者的资金免受不合做的接收者的影响。

完整源代码,simplePaymentChannel.sol

pragma solidity ^0.4.20;

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;
    }

    function isValidSignature(uint256 amount, bytes signature)
        internal
        view
        returns (bool)
    {
        bytes32 message = prefixed(keccak256(this, amount));

        // Check that the signature is from the payment sender.
        return recoverSigner(message, signature) == sender;
    }

    // The recipient can close the channel at any time by presenting a signed
    // amount from the sender. The recipient will be sent that amount, and the
    // remainder will go back to the sender.
    function close(uint256 amount, bytes signature) public {
        require(msg.sender == recipient);
        require(isValidSignature(amount, signature));

        recipient.transfer(amount);
        selfdestruct(sender);
    }

    // The sender can extend the expiration at any time.
    function extend(uint256 newExpiration) public {
        require(msg.sender == sender);
        require(newExpiration > expiration);

        expiration = newExpiration;
    }

    // If the timeout is reached without the recipient closing the channel, then
    // the ether is released back to the sender.
    function claimTimeout() public {
        require(now >= expiration);
        selfdestruct(sender);
    }

    function splitSignature(bytes sig)
        internal
        pure
        returns (uint8, bytes32, bytes32)
    {
        require(sig.length == 65);

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }

        return (v, r, s);
    }

    function recoverSigner(bytes32 message, bytes sig)
        internal
        pure
        returns (address)
    {
        uint8 v;
        bytes32 r;
        bytes32 s;

        (v, r, s) = splitSignature(sig);

        return ecrecover(message, v, r, s);
    }

    // Builds a prefixed hash to mimic the behavior of eth_sign.
    function prefixed(bytes32 hash) internal pure returns (bytes32) {
        return keccak256("\x19Ethereum Signed Message:\n32", hash);
    }
}

=========================================================================

若是你但愿快速的开始使用.net和C#开发以太坊应用,那这个咱们进行打造的课程会颇有帮助:

C#以太坊

若是是其余语言开发以太坊应用的也能够参考如下教程:

  • java以太坊教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • 以太坊教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行帐号建立、交易、转帐、代币开发以及过滤器和事件等内容。

这里是原文

相关文章
相关标签/搜索