50行程式碼寫就以太坊支付

末座少年發表於2018-09-03

狀態通道(State Channel)是非常流行的擴容方案。比特幣線下支付通道有閃電(lightning),以太坊有雷電(Raiden)  本文演示如何立刻用合約寫以太坊支付.

原始碼在這裡

假設在Alice和Bob之間發生的微支付場景: Bob僱傭Alice為他的推特的水軍,定期在Bob的推特上發推,回帖。為此BOB答應Alice,每發一個推(tweet),Bob支付0.001個ETH。這種微支付交易通過公鏈來做的話,20%的Alice的收入都會被以交易費的方式吃掉。

一方面,Alice不想每發100條推特後才信任Bob會付總額0.001ETH*100=0.1ETH。因為BOB有可能賴賬。另一方面,Bob不想一次性付Alice發100個推特的錢,因為Alice完全有可能拿了錢,人間蒸發而不幹活。

這時候,我們就需要一個支付通道,Bob預先打100*0.001 = 0.1 ETH 到通道的智慧合約,按照合約的規則,合約裡的錢可能給Alice,也可能退回給BOB. 建構函式如下:

contract Channel {

    address public channelSender;
    address public channelRecipient;
    uint public startDate;
    uint public channelTimeout;
    mapping (bytes32 => address) signatures;

    function Channel(address to, uint timeout) payable {
	    channelRecipient = to;
	    channelSender = msg.sender;
	    startDate = now;
	    channelTimeout = timeout;
    }

假設Bob送了0.1 ETH給這個合約,而且設定Timeout為1天。如果timeout發生,Bob可以撤銷這個合約,從而獲得被退回的餘額.

Alice看到支付通道合約裡有錢被鎖定了。

021647_lOEI_2981977.png

Alice就放心的開始工作:發推特。每發一個tweet, Bob就用私鑰簽名一個(contract_address, value)產生的雜湊值,並送給 Alice. 所以,Alice發第一個tweet, Bob 就籤(0x123…, 0.001 ETH), 發第二個推特,Bob籤(0x123, 0.002 ETH), etc…

每次Alice收到Bob簽名的資訊,Alice也簽名,但是並不送到公鏈。任何時候Alice決定終止這分工作時,alice需要發一個多人簽名(Bob和Alice)的訊息給合約。合約就會送給Alice事先約定好的酬勞 (say, 0.05 ETH=發了50個推特*0.001ETH) 並把合約裡剩餘的資金返回給Bob.

function CloseChannel(bytes32 h, uint8 v, bytes32 r, bytes32 s, uint value){
		address signer;
		bytes32 proof;

		// get signer from signature
		signer = ecrecover(h, v, r, s);

		// signature is invalid, throw
		if (signer != channelSender && signer != channelRecipient) throw;

		proof = sha3(this, value);

		// signature is valid but doesn`t match the data provided
		if (proof != h) throw;

		if (signatures[proof] == 0)
			signatures[proof] = signer;
		else if (signatures[proof] != signer){
			// channel completed, both signatures provided
			if (!channelRecipient.send(value)) throw;
			selfdestruct(channelSender);
		}
}

Alice可以呼叫這個函式中止通道。因為呼叫此函式需要Bob和Alice的簽名,Bob不能單方面的關閉合約(從而不付Alice她應得的報酬)

因為此函式需要Bob和Alice兩個人的簽名才能執行,Bob和Alice都不能單方面的從合約裡提錢。

如果Alice是惡意的,而且希望欺騙Bob:讓Bob把錢鎖進合約,但是Alice不幹任何事,並且偽造一個交易(讓合約送給Alice一半的錢)這種情況下,ChannelTimeout函式可以保護Bob:Bob可以等一天,然後呼叫, 把合約銷燬,並把合約裡所有的資金返回給Bob.

function ChannelTimeout(){
	if (startDate + channelTimeout > now)
		throw;

	selfdestruct(channelSender);
}

Timeout 函式保證Bob 免受勒索

同時,在Bob和Alice的簽名提交到公鏈之前,Bob沒有Alice的簽名。Bob不能單方面關閉通道從而免付Alice應得的報酬。如果Alice覺察到Bob沒有付錢了,Alice可以關閉通道從而拿到她應得的報酬。唯一的損失是Alice可能損失她發最後一個推特的錢。同時Bob和Alice節省了大量的交易費!

一個支付合約就寫完了

 

參照

附:

ecrecover函式是由以太坊提供的一個全域性函式,用於簽名資料的校驗。與上面所陳述的方式略有不同的是,這個函式返回的是簽名者的公匙地址。如果返回結果是簽名者的公匙地址,那麼說明資料是正確的。

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)

ecrecover函式需要四個引數,需要被簽名資料的雜湊結果值,r,s,v三個值。通過前面的說明,我們知道r,s,v是分別來自簽名結果串。

r = signature[0:64]
s = signature[64:128]
v = signature[128:130]

其中v取出來的值或者是0001。要使用時,我們先要將其轉為整型,再加上27,所以我們將得到27或28。在呼叫函式時v將填入27或28。

如果你使用以太坊的客戶端進行簽名時,它們會在你要簽名的資料前增加字首x19Ethereum Signed Message:

eth_sign

The sign method calculates an Ethereum specific signature with: sign(keccak256(“x19Ethereum Signed Message:
” + len(message) + message))).

 


相關文章