资讯专栏INFORMATION COLUMN

如何开发以太坊支付?

Anleb / 578人阅读

摘要:在这篇文章中,我将实现一个简单但完整的以太坊支付通道。发送者签署消息,指明该中应向接收者支付多少。以太坊,主要是针对工程师使用进行区块链以太坊开发的详解。

在这篇文章中,我将实现一个简单但完整的以太坊支付通道。支付通道使用密码签名,以安全、即时、无交易费用重复地传送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);
}

// 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:
32", 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:
32", hash);
    }
}

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

如果你希望快速的开始使用.net和C#开发以太坊应用,那这个我们进行打造的课程会很有帮助:

C#以太坊

如果是其他语言开发以太坊应用的也可以参考以下教程:

java以太坊教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。

以太坊教程,主要介绍智能合约与dapp应用开发,适合入门。

以太坊开发,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。

python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。

php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和事件等内容。

这里是原文

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/24226.html

相关文章

  • php如何开发一个以太支付系统?

    摘要:最重要的是,您不需要外部服务来在您的网站上集成以太坊支付系统。来一起边学边玩以太坊吧。 当我第一次考虑通过加密货币实施支付时,我查看了像Stripe这样的可用解决方案。我觉得Stripe的问题在于,它只允许使用美国商家帐户进行比特币支付,所以这对我来说不是一个选择。在以太坊世界,它看起来更糟糕。有一些较新的服务,但他们都想要分享蛋糕。 那么从头开始构建以太坊支付系统,我们需要什么? ...

    fxp 评论0 收藏0
  • php如何开发一个以太支付系统?

    摘要:最重要的是,您不需要外部服务来在您的网站上集成以太坊支付系统。来一起边学边玩以太坊吧。 当我第一次考虑通过加密货币实施支付时,我查看了像Stripe这样的可用解决方案。我觉得Stripe的问题在于,它只允许使用美国商家帐户进行比特币支付,所以这对我来说不是一个选择。在以太坊世界,它看起来更糟糕。有一些较新的服务,但他们都想要分享蛋糕。 那么从头开始构建以太坊支付系统,我们需要什么? ...

    lidashuang 评论0 收藏0
  • Web程序员如何入门以太开发

    摘要:加入以太坊生态系统,一起征服世界。数字,字符串等要注意的第二件事是以太坊中的是位。一旦你掌握了这些东西,我个人会认为你是一个有能力的以太坊开发者 我经常构建使用以太坊的Web应用程序,我理所当然地认为每天都使用的是神奇的工具集。我们的生态系统正在迅速发展,我认为很多新人都感到不知所措。以太坊是一项了不起的技术,但它也是新生的,而且根本没有足够的时间让专业知识充分渗透。我希望人们知道以太...

    SwordFly 评论0 收藏0
  • Web程序员如何入门以太开发

    摘要:加入以太坊生态系统,一起征服世界。数字,字符串等要注意的第二件事是以太坊中的是位。一旦你掌握了这些东西,我个人会认为你是一个有能力的以太坊开发者 我经常构建使用以太坊的Web应用程序,我理所当然地认为每天都使用的是神奇的工具集。我们的生态系统正在迅速发展,我认为很多新人都感到不知所措。以太坊是一项了不起的技术,但它也是新生的,而且根本没有足够的时间让专业知识充分渗透。我希望人们知道以太...

    kidsamong 评论0 收藏0
  • 一块听听:Mixin 主网上线语音直播文字稿

    摘要:本文是在一块听听上的语音直播的文字精简版。主网上线的细节主网在北京时间年月日早上点正式完成了上线。目前主网上线工作已经完成,正在把测试网上的资产迁移到主网上。主网上线意味着什么真的是一个去中心化的区块链项目了。主网上线对来说只是一个起点。 本文是在一块听听上的语音直播的文字精简版。 Mixin Network的成绩,主网和展望 大家好,我是Mixin Network 的李林。非常高兴能...

    nanchen2251 评论0 收藏0

发表评论

0条评论

Anleb

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<