溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么實(shí)現(xiàn)以太坊支付

發(fā)布時間:2021-12-29 14:23:12 來源:億速云 閱讀:179 作者:iii 欄目:互聯(lián)網(wǎng)科技

這篇文章主要講解了“怎么實(shí)現(xiàn)以太坊支付”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么實(shí)現(xiàn)以太坊支付”吧!

什么是支付通道?

以太坊交易提供了一種安全的方式來轉(zhuǎn)賬,但每個交易需要被包括在一個區(qū)塊中和并被挖掘。這意味著交易需要一些時間,并要求支付一些費(fèi)用來補(bǔ)償?shù)V工的工作。特別是,這個交易費(fèi)用使得其產(chǎn)生的這種小額支付,成為了以太坊和其他類似于它的區(qū)塊鏈的使用,變得有點(diǎn)兒費(fèi)勁一個原因。

支付通道允許參與者在不使用交易的情況下重復(fù)發(fā)送Ether。這意味著可以避免與交易相關(guān)的延遲和因此產(chǎn)生費(fèi)用。在這篇文章中,我們將探討一個簡單的單向支付通道。這包括三個步驟:

  • 1.發(fā)送者用Ether支付一個智能合約。這會打開支付通道。

  • 2.發(fā)送者簽署消息,指明該ether中應(yīng)向接收者支付多少。對于每個支付,都重復(fù)這一步驟。

  • 3.接收者關(guān)閉支付通道,收取他們的那部分ether,并將其余部分返回發(fā)送者。

怎么實(shí)現(xiàn)以太坊支付

重要的是,只有步驟1和步驟3需要空缺交易。步驟2通過密碼簽名和兩方之間的通信(如電子郵件)完成。這意味著只需要兩個交易來支持任何數(shù)量的發(fā)送。

收件人保證收到他們的資金,因?yàn)橹悄芎霞s托管了ether并認(rèn)可有效簽署的消息。智能合約還強(qiáng)制執(zhí)行直到截止時間,而且發(fā)送方有權(quán)收回資金,即使接收方拒絕關(guān)閉支付通道。

這取決于支付通道的參與者決定多長時間保持開放。對于短時間的交互,例如對于提供網(wǎng)絡(luò)服務(wù)按每分鐘支付的網(wǎng)吧,使用只持續(xù)一個小時左右的支付通道就足夠了。對于一個較長期的支付關(guān)系,比如給員工支付按小時計(jì)的工資,支付通道可以持續(xù)數(shù)月或數(shù)年。

打開支付通道

為了打開支付通道,發(fā)送方部署智能合約,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;
    }

支付款項(xiàng)

發(fā)送者通過向接收者發(fā)送消息來進(jìn)行支付。該步驟完全在以太坊網(wǎng)絡(luò)之外執(zhí)行。消息由發(fā)送方進(jìn)行加密簽名,然后直接發(fā)送給接收方。

每個消息包括以下信息:

  • 智能合約的地址,用來防止跨合約replay攻擊。

  • 迄今為止,接受者所消耗的ether總量。

在一系列轉(zhuǎn)賬結(jié)束時,支付通道只關(guān)閉一次。正因?yàn)槿绱?,只有一個發(fā)送的消息將被贖回。這就是為什么每個消息都指定了累積的Ether消耗總量,而不是單個微支付的量。接收者自然會選擇贖回最近的消息,因?yàn)檫@是一個總擁有最高ether的消息。

請注意,因?yàn)橹悄芎霞s僅對單個消息進(jìn)行維護(hù),所以不需要每個臨時消息。智能合約的地址仍然用于防止用于一個支付通道的消息被用于不同的通道。

可以用支持加密的hash和簽名操作的任何語言構(gòu)建和簽名支付相應(yīng)的消息。下面的代碼是用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);
}

核實(shí)付款

與簽名不同,支付通道中的消息不會立即被贖回。接收方跟蹤最新消息并在關(guān)閉支付通道時贖回。這意味著接收方對每個消息進(jìn)行自己的驗(yàn)證是至關(guān)重要的。否則,不能保證收件人最終能得到報(bào)酬。

接收方應(yīng)使用以下過程驗(yàn)證每個消息:

  • 1.驗(yàn)證消息中的合約地址與支付通道相匹配。

  • 2.驗(yàn)證新合計(jì)是否為預(yù)期金額。

  • 3.驗(yàn)證新的總量不超過ether的量。

  • 4.驗(yàn)證簽名是否有效,并來自支付通道發(fā)送者。

前三個步驟很簡單。最后一步可以通過多種方式執(zhí)行,但是如果它在JavaScript中完成,我推薦ethereumjs-util庫。下面的代碼從上面的簽名代碼中借用constructMessage函數(shù):

// 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();
}

關(guān)閉支付通道

當(dāng)接受者準(zhǔn)備好接收他們的資金時,是時候通過在智能合約上調(diào)用close功能來關(guān)閉支付通道。關(guān)閉通道給接收者,他們獲得自己的ether并銷毀合約,發(fā)送剩余的Ether回發(fā)送者。要關(guān)閉通道,接收方需要共享由發(fā)送方簽名的消息。

智能合約必須驗(yàn)證消息包含來自發(fā)送者的有效簽名。進(jìn)行此驗(yàn)證的過程與接收方使用的過程相同。isValidSignature recoverSigner函數(shù)與前一部分中的JavaScript代碼對應(yīng)。后者是在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);
}

關(guān)閉功能只能由支付通道接收者來調(diào)用,而接收者自然會傳遞最新的支付消息,因?yàn)樵撓⒕哂凶罡叩目傎M(fèi)用。如果發(fā)送者被允許調(diào)用這個函數(shù),他們可以提供一個較低費(fèi)用的消息,并欺騙接收者。

函數(shù)驗(yàn)證簽名的消息與給定的參數(shù)匹配。如果一切都被檢測出來,收件人就發(fā)送了他們的部分ether,發(fā)送者通過selfdestruct發(fā)送其余部分。

關(guān)閉支付通道

接收方可以在任何時候關(guān)閉支付通道,但是如果他們不這樣做,發(fā)送者需要一種方法來收回他們的托管資金。在合約部署時設(shè)置了expiration時間。一旦到達(dá)該時間,發(fā)送方可以調(diào)用claimTimeout來恢復(fù)其資金。

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

在這個函數(shù)被調(diào)用之后,接收者再也不能接收任何ether,所以接收者在到達(dá)期滿之前關(guān)閉通道是很重要的。

總結(jié)

  • 支付通道支持安全的、區(qū)塊鏈外的資金轉(zhuǎn)移,同時避免每次轉(zhuǎn)賬產(chǎn)生交易費(fèi)用。

  • 付款是累積的,只有一個是在關(guān)閉頻道時贖回的。

  • 轉(zhuǎn)賬是通過托管資金和密碼簽名來保證的。

  • 超時保護(hù)發(fā)送者的資金免受不合作的接收者的影響。

完整源代碼,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);
    }
}

感謝各位的閱讀,以上就是“怎么實(shí)現(xiàn)以太坊支付”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對怎么實(shí)現(xiàn)以太坊支付這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI