摘要:在這篇文章中,我將實(shí)現(xiàn)一個(gè)簡(jiǎn)單但完整的以太坊支付通道。發(fā)送者簽署消息,指明該中應(yīng)向接收者支付多少。以太坊,主要是針對(duì)工程師使用進(jìn)行區(qū)塊鏈以太坊開發(fā)的詳解。
在這篇文章中,我將實(shí)現(xiàn)一個(gè)簡(jiǎn)單但完整的以太坊支付通道。支付通道使用密碼簽名,以安全、即時(shí)、無(wú)交易費(fèi)用重復(fù)地傳送Ether。
什么是支付通道?以太坊交易提供了一種安全的方式來(lái)轉(zhuǎn)賬,但每個(gè)交易需要被包括在一個(gè)區(qū)塊中和并被挖掘。這意味著交易需要一些時(shí)間,并要求支付一些費(fèi)用來(lái)補(bǔ)償?shù)V工的工作。特別是,這個(gè)交易費(fèi)用使得其產(chǎn)生的這種小額支付,成為了以太坊和其他類似于它的區(qū)塊鏈的使用,變得有點(diǎn)兒費(fèi)勁一個(gè)原因。
支付通道允許參與者在不使用交易的情況下重復(fù)發(fā)送Ether。這意味著可以避免與交易相關(guān)的延遲和因此產(chǎn)生費(fèi)用。在這篇文章中,我們將探討一個(gè)簡(jiǎn)單的單向支付通道。這包括三個(gè)步驟:
1.發(fā)送者用Ether支付一個(gè)智能合約。這會(huì)打開支付通道。
2.發(fā)送者簽署消息,指明該ether中應(yīng)向接收者支付多少。對(duì)于每個(gè)支付,都重復(fù)這一步驟。
3.接收者關(guān)閉支付通道,收取他們的那部分ether,并將其余部分返回發(fā)送者。
重要的是,只有步驟1和步驟3需要空缺交易。步驟2通過(guò)密碼簽名和兩方之間的通信(如電子郵件)完成。這意味著只需要兩個(gè)交易來(lái)支持任何數(shù)量的發(fā)送。
收件人保證收到他們的資金,因?yàn)橹悄芎霞s托管了ether并認(rèn)可有效簽署的消息。智能合約還強(qiáng)制執(zhí)行直到截止時(shí)間,而且發(fā)送方有權(quán)收回資金,即使接收方拒絕關(guān)閉支付通道。
這取決于支付通道的參與者決定多長(zhǎng)時(shí)間保持開放。對(duì)于短時(shí)間的交互,例如對(duì)于提供網(wǎng)絡(luò)服務(wù)按每分鐘支付的網(wǎng)吧,使用只持續(xù)一個(gè)小時(shí)左右的支付通道就足夠了。對(duì)于一個(gè)較長(zhǎng)期的支付關(guān)系,比如給員工支付按小時(shí)計(jì)的工資,支付通道可以持續(xù)數(shù)月或數(shù)年。
打開支付通道為了打開支付通道,發(fā)送方部署智能合約,ether也將被托管,并指定接收方和通道存在的最晚截止時(shí)間。
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ā)送者通過(guò)向接收者發(fā)送消息來(lái)進(jìn)行支付。該步驟完全在以太坊網(wǎng)絡(luò)之外執(zhí)行。消息由發(fā)送方進(jìn)行加密簽名,然后直接發(fā)送給接收方。
每個(gè)消息包括以下信息:
智能合約的地址,用來(lái)防止跨合約replay攻擊。
迄今為止,接受者所消耗的ether總量。
在一系列轉(zhuǎn)賬結(jié)束時(shí),支付通道只關(guān)閉一次。正因?yàn)槿绱?,只有一個(gè)發(fā)送的消息將被贖回。這就是為什么每個(gè)消息都指定了累積的Ether消耗總量,而不是單個(gè)微支付的量。接收者自然會(huì)選擇贖回最近的消息,因?yàn)檫@是一個(gè)總擁有最高ether的消息。
請(qǐng)注意,因?yàn)橹悄芎霞s僅對(duì)單個(gè)消息進(jìn)行維護(hù),所以不需要每個(gè)臨時(shí)消息。智能合約的地址仍然用于防止用于一個(gè)支付通道的消息被用于不同的通道。
可以用支持加密的hash和簽名操作的任何語(yǔ)言構(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í)付款
與簽名不同,支付通道中的消息不會(huì)立即被贖回。接收方跟蹤最新消息并在關(guān)閉支付通道時(shí)贖回。這意味著接收方對(duì)每個(gè)消息進(jìn)行自己的驗(yàn)證是至關(guān)重要的。否則,不能保證收件人最終能得到報(bào)酬。
接收方應(yīng)使用以下過(guò)程驗(yàn)證每個(gè)消息:
1.驗(yàn)證消息中的合約地址與支付通道相匹配。
2.驗(yàn)證新合計(jì)是否為預(yù)期金額。
3.驗(yàn)證新的總量不超過(guò)ether的量。
4.驗(yàn)證簽名是否有效,并來(lái)自支付通道發(fā)送者。
前三個(gè)步驟很簡(jiǎn)單。最后一步可以通過(guò)多種方式執(zhí)行,但是如果它在JavaScript中完成,我推薦ethereumjs-util庫(kù)。下面的代碼從上面的簽名代碼中借用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: 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(); }關(guān)閉支付通道
當(dāng)接受者準(zhǔn)備好接收他們的資金時(shí),是時(shí)候通過(guò)在智能合約上調(diào)用close功能來(lái)關(guān)閉支付通道。關(guān)閉通道給接收者,他們獲得自己的ether并銷毀合約,發(fā)送剩余的Ether回發(fā)送者。要關(guān)閉通道,接收方需要共享由發(fā)送方簽名的消息。
智能合約必須驗(yàn)證消息包含來(lái)自發(fā)送者的有效簽名。進(jìn)行此驗(yàn)證的過(guò)程與接收方使用的過(guò)程相同。isValidSignature 和recoverSigner函數(shù)與前一部分中的JavaScript代碼對(duì)應(yīng)。后者是在Signing and Verifying Messages in Ethereum中從ReceiverPays合約中copy來(lái)的。
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)閉功能只能由支付通道接收者來(lái)調(diào)用,而接收者自然會(huì)傳遞最新的支付消息,因?yàn)樵撓⒕哂凶罡叩目傎M(fèi)用。如果發(fā)送者被允許調(diào)用這個(gè)函數(shù),他們可以提供一個(gè)較低費(fèi)用的消息,并欺騙接收者。
函數(shù)驗(yàn)證簽名的消息與給定的參數(shù)匹配。如果一切都被檢測(cè)出來(lái),收件人就發(fā)送了他們的部分ether,發(fā)送者通過(guò)selfdestruct發(fā)送其余部分。
關(guān)閉支付通道接收方可以在任何時(shí)候關(guān)閉支付通道,但是如果他們不這樣做,發(fā)送者需要一種方法來(lái)收回他們的托管資金。在合約部署時(shí)設(shè)置了expiration時(shí)間。一旦到達(dá)該時(shí)間,發(fā)送方可以調(diào)用claimTimeout來(lái)恢復(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); }
在這個(gè)函數(shù)被調(diào)用之后,接收者再也不能接收任何ether,所以接收者在到達(dá)期滿之前關(guān)閉通道是很重要的。
總結(jié)支付通道支持安全的、區(qū)塊鏈外的資金轉(zhuǎn)移,同時(shí)避免每次轉(zhuǎn)賬產(chǎn)生交易費(fèi)用。
付款是累積的,只有一個(gè)是在關(guān)閉頻道時(shí)贖回的。
轉(zhuǎn)賬是通過(guò)托管資金和密碼簽名來(lái)保證的。
超時(shí)保護(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: 32", hash); } }
=========================================================================
如果你希望快速的開始使用.net和C#開發(fā)以太坊應(yīng)用,那這個(gè)我們進(jìn)行打造的課程會(huì)很有幫助:
C#以太坊
如果是其他語(yǔ)言開發(fā)以太坊應(yīng)用的也可以參考以下教程:
java以太坊教程,主要是針對(duì)java和android程序員進(jìn)行區(qū)塊鏈以太坊開發(fā)的web3j詳解。
以太坊教程,主要介紹智能合約與dapp應(yīng)用開發(fā),適合入門。
以太坊開發(fā),主要是介紹使用node.js、mongodb、區(qū)塊鏈、ipfs實(shí)現(xiàn)去中心化電商DApp實(shí)戰(zhàn),適合進(jìn)階。
python以太坊,主要是針對(duì)python工程師使用web3.py進(jìn)行區(qū)塊鏈以太坊開發(fā)的詳解。
php以太坊,主要是介紹使用php進(jìn)行智能合約開發(fā)交互,進(jìn)行賬號(hào)創(chuàng)建、交易、轉(zhuǎn)賬、代幣開發(fā)以及過(guò)濾器和事件等內(nèi)容。
這里是原文
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/24226.html
摘要:最重要的是,您不需要外部服務(wù)來(lái)在您的網(wǎng)站上集成以太坊支付系統(tǒng)。來(lái)一起邊學(xué)邊玩以太坊吧。 當(dāng)我第一次考慮通過(guò)加密貨幣實(shí)施支付時(shí),我查看了像Stripe這樣的可用解決方案。我覺得Stripe的問(wèn)題在于,它只允許使用美國(guó)商家?guī)暨M(jìn)行比特幣支付,所以這對(duì)我來(lái)說(shuō)不是一個(gè)選擇。在以太坊世界,它看起來(lái)更糟糕。有一些較新的服務(wù),但他們都想要分享蛋糕。 那么從頭開始構(gòu)建以太坊支付系統(tǒng),我們需要什么? ...
摘要:最重要的是,您不需要外部服務(wù)來(lái)在您的網(wǎng)站上集成以太坊支付系統(tǒng)。來(lái)一起邊學(xué)邊玩以太坊吧。 當(dāng)我第一次考慮通過(guò)加密貨幣實(shí)施支付時(shí),我查看了像Stripe這樣的可用解決方案。我覺得Stripe的問(wèn)題在于,它只允許使用美國(guó)商家?guī)暨M(jìn)行比特幣支付,所以這對(duì)我來(lái)說(shuō)不是一個(gè)選擇。在以太坊世界,它看起來(lái)更糟糕。有一些較新的服務(wù),但他們都想要分享蛋糕。 那么從頭開始構(gòu)建以太坊支付系統(tǒng),我們需要什么? ...
摘要:加入以太坊生態(tài)系統(tǒng),一起征服世界。數(shù)字,字符串等要注意的第二件事是以太坊中的是位。一旦你掌握了這些東西,我個(gè)人會(huì)認(rèn)為你是一個(gè)有能力的以太坊開發(fā)者 我經(jīng)常構(gòu)建使用以太坊的Web應(yīng)用程序,我理所當(dāng)然地認(rèn)為每天都使用的是神奇的工具集。我們的生態(tài)系統(tǒng)正在迅速發(fā)展,我認(rèn)為很多新人都感到不知所措。以太坊是一項(xiàng)了不起的技術(shù),但它也是新生的,而且根本沒有足夠的時(shí)間讓專業(yè)知識(shí)充分滲透。我希望人們知道以太...
摘要:加入以太坊生態(tài)系統(tǒng),一起征服世界。數(shù)字,字符串等要注意的第二件事是以太坊中的是位。一旦你掌握了這些東西,我個(gè)人會(huì)認(rèn)為你是一個(gè)有能力的以太坊開發(fā)者 我經(jīng)常構(gòu)建使用以太坊的Web應(yīng)用程序,我理所當(dāng)然地認(rèn)為每天都使用的是神奇的工具集。我們的生態(tài)系統(tǒng)正在迅速發(fā)展,我認(rèn)為很多新人都感到不知所措。以太坊是一項(xiàng)了不起的技術(shù),但它也是新生的,而且根本沒有足夠的時(shí)間讓專業(yè)知識(shí)充分滲透。我希望人們知道以太...
摘要:本文是在一塊聽聽上的語(yǔ)音直播的文字精簡(jiǎn)版。主網(wǎng)上線的細(xì)節(jié)主網(wǎng)在北京時(shí)間年月日早上點(diǎn)正式完成了上線。目前主網(wǎng)上線工作已經(jīng)完成,正在把測(cè)試網(wǎng)上的資產(chǎn)遷移到主網(wǎng)上。主網(wǎng)上線意味著什么真的是一個(gè)去中心化的區(qū)塊鏈項(xiàng)目了。主網(wǎng)上線對(duì)來(lái)說(shuō)只是一個(gè)起點(diǎn)。 本文是在一塊聽聽上的語(yǔ)音直播的文字精簡(jiǎn)版。 Mixin Network的成績(jī),主網(wǎng)和展望 大家好,我是Mixin Network 的李林。非常高興能...
閱讀 1165·2021-11-25 09:43
閱讀 2976·2019-08-30 15:54
閱讀 3363·2019-08-30 15:54
閱讀 3010·2019-08-30 15:44
閱讀 1636·2019-08-26 12:18
閱讀 2265·2019-08-26 11:42
閱讀 887·2019-08-26 11:35
閱讀 3305·2019-08-23 18:22