Solidity撰寫智能合約與注意事項(二)

from:

https://medium.com/taipei-ethereum-meetup/solidity%E6%92%B0%E5%AF%AB%E6%99%BA%E8%83%BD%E5%90%88%E7%B4%84%E8%88%87%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A0%85-%E4%BA%8C-dd915bdeafa0


這篇將介紹以太坊的log資料及event的使用

類似於比特幣的OP_RETURN,以太坊也提供一個把資料永久寫入區塊鏈裡的方法 — event,event所寫入的資料會被記錄在一個Receipt資料裡。

每一筆transaction都會有一個對應的Receipt,用來記錄這筆transaction的執行結果

eth.getTransaction()

getTransaction可以得到這筆transaction的相關資料,這些資料在transaction被製作出來的時候就有了,而Receipt則是直到被執行完(也就是transaction被放進鏈裡)後才會有。

註:(1)gas是指這次提供的gas總量

(2)input是合約的code(這是一個部署合約的transaction,所以input就是整份合約的code)。

用getTransacitonReceipt("hash")來取得transaction對應的Receipt:

eth.getTransactionReceipt()

註:(1)gasUsed是這筆transaction所花費的gas(我們提供了4700000,只花了118615,剩下的會退還給我們)

(2)logs則是我們這篇介紹的log,如果有event被觸發,資料就會被入在這

那什麼時候會需要用到event呢?

1. 當作一個額外的儲存空間,而且很便宜。event寫入的成本和用合約變數來儲存的成本相比之下少了很多,如果你開發的dapp需要將使用者的使用紀錄(如付款紀錄)等記錄下來當作證明,與其用一個陣列儲存,不如在每次使用時用event寫進log裡。

但要注意的是,這些寫進log裡的資料是沒辦法被合約所存取的。

2. 當成return value來使用。合約裡函式的回傳值並非總是可以使用,假設一個有回傳值的函式:

contract f00{

function foo(int _value) returns (int) {

return _value * _value;

}

}

什麼時候你可以拿到這個回傳值?只有在你使用call在本地進行模擬的時候才會有回傳值,如下:

var ret = f00.foo.call(25);

console.log(ret);    //625

當你使用sendTransaction,真的做出一筆交易的時候,他會回傳你這筆transaction的hash值,所以這時候你可以使用event來將回傳值記錄起來:

contract f00{

event retValue(int _value);

function foo(int _value) returns(int) {

return _value * _value;

}

}

這時候在前端便可以利用像javascript那樣監聽的功能:

var retValueEvent = f00.retValue();

retValueEvent.watch(function(err, result){

if(err){

console.log(err);

return;

}

console.log(result.args._value);

});

當transaction被收入區塊鏈裡後,就會觸發監聽器然後按照你設定的callback函式執行對應的動作。如果要結束監聽,執行 retValueEvent.stopWatching()。

3. 最後便是當你開發dapp的時候,藉由觸發event寫入log,再觸發監聽器執行對應動作,如此完成從 外界->鏈->鏈->外界 一個完整的執行過程。對使用者來說就像一個是對資料庫操作的動作,只是這個資料庫變成了區塊鏈。

接下來以簡單的例子來介紹監聽器更多的功能

contract depositAccount {

event Deposit(addressindexed_owner, uint _amount, uint _time);

function deposit() payable {

Deposit(msg.sender, msg.value, now);

}

}

首先先介紹第二行的indexed。

在Receipt裡如果有log,會寫在logs欄位裡,每筆log其中有兩部分:data和topics。一般event寫入的資料都會寫在data裡,但如果在event的變數加入一個indexed屬性,到時候觸發時這個變數對應寫入的值就會寫在topics裡,在topcis裡的值可以用來當作監聽器的篩選條件。

註:一份Receipt裡面可以有很多筆log(表示一次transaction可以觸發很多次event),一筆log最多只能儲存四個topics,而第一個topic必須是這個event的識別值identifier,代表一個event最多只能有三個變數可以有indexed屬性。

transaction的logs裡的其中一筆log

上面這張圖是其中某一筆log,這個event沒有加indexed的變數,值都會寫在data欄位裡(32byte為一單位接在一起,圖中為十六進位的9和19),topcis裡唯一一個值 0x35bd26...是該event的識別值。

下面這張圖是變數都加上indexed的event的log記錄:

event有indexed

因為所有變數都加上indexed,所以data裡沒有值,值都寫在topics裡(十六進位的102和abcde)。

接下來在前端加入監聽器:

var depositEvent = depositAccount.Deposit({_owner:null},{fromBlock: 5000, toBlock: ‘latest’});

depositEvent.watch(function(err, results) {

if (err) {

console.log(err);

return;

}

console.log(results.blockNumber);

});

第一行的{fromBlock: 5000, toBlock: ‘latest’}是加入的篩選條件,表示監聽從第5000個區塊開始到最新的區塊。如果event被觸發,那callback函式就會印出是發生在第幾個區塊( result.blockNumber )。

如果我們要用有加上indexed的變數(_owner變數)來當篩選條件的話,就指定_owner應該要是多少,如果給null(像上面的例子),那就是任何address都可以的意思:

var depositEvent = depositaccount.Deposit({_owner:'0xbd7255b64eeb594ca57652c94249da6a9b37cd2f'});

depositEvent.watch(function(err, results) {

if (err) {

console.log(err);

return;

}

if(results.args._amount > web3.toWei(0.05,"ether")){

console.log("User: " + results.args._owner +

" deposits: " + results.args._amount +

" on: " +

JSON.stringify(new Date((parseInt(results.args._time)+28800)*1000))

);

}

});

這個callback會在"0xbd7255b64eeb594ca57652c94249da6a9b37cd2f"這個address存錢的時候被觸發,存入超過0.05 ether的時候會印出存入的金額和存入時間。

References:

[1]https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e#.u7l9ejta3

[2]https://github.com/ethereum/wiki/wiki/JavaScript-API#contract-events

你可能感兴趣的:(Solidity撰寫智能合約與注意事項(二))