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蜗细,以太坊也提供一個把資料永久寫入?yún)^(qū)塊鏈裡的方法?—?event,event所寫入的資料會被記錄在一個Receipt資料裡酬土。
每一筆transaction都會有一個對應(yīng)的Receipt霸奕,用來記錄這筆transaction的執(zhí)行結(jié)果
eth.getTransaction()
getTransaction可以得到這筆transaction的相關(guān)資料寝志,這些資料在transaction被製作出來的時候就有了,而Receipt則是直到被執(zhí)行完(也就是transaction被放進鏈裡)後才會有。
註:(1)gas是指這次提供的gas總量
(2)input是合約的code(這是一個部署合約的transaction单山,所以input就是整份合約的code)眼溶。
用getTransacitonReceipt("hash")來取得transaction對應(yīng)的Receipt:
eth.getTransactionReceipt()
註:(1)gasUsed是這筆transaction所花費的gas(我們提供了4700000悠砚,只花了118615,剩下的會退還給我們)
(2)logs則是我們這篇介紹的log堂飞,如果有event被觸發(fā)灌旧,資料就會被入在這
那什麼時候會需要用到event呢?
1. 當(dāng)作一個額外的儲存空間绰筛,而且很便宜枢泰。event寫入的成本和用合約變數(shù)來儲存的成本相比之下少了很多,如果你開發(fā)的dapp需要將使用者的使用紀(jì)錄(如付款紀(jì)錄)等記錄下來當(dāng)作證明铝噩,與其用一個陣列儲存宗苍,不如在每次使用時用event寫進log裡。
但要注意的是薄榛,這些寫進log裡的資料是沒辦法被合約所存取的讳窟。
2. 當(dāng)成return value來使用。合約裡函式的回傳值並非總是可以使用敞恋,假設(shè)一個有回傳值的函式:
contract f00{
function foo(int _value) returns (int) {
return _value * _value;
}
}
什麼時候你可以拿到這個回傳值丽啡?只有在你使用call在本地進行模擬的時候才會有回傳值,如下:
var ret = f00.foo.call(25);
console.log(ret);? ? //625
當(dāng)你使用sendTransaction硬猫,真的做出一筆交易的時候补箍,他會回傳你這筆transaction的hash值改执,所以這時候你可以使用event來將回傳值記錄起來:
contract f00{
event retValue(int _value);
function foo(int _value) returns(int) {
return _value * _value;
}
}
這時候在前端便可以利用像javascript那樣監(jiān)聽的功能:
var retValueEvent = f00.retValue();
retValueEvent.watch(function(err, result){
if(err){
console.log(err);
return;
}
console.log(result.args._value);
});
當(dāng)transaction被收入?yún)^(qū)塊鏈裡後,就會觸發(fā)監(jiān)聽器然後按照你設(shè)定的callback函式執(zhí)行對應(yīng)的動作坑雅。如果要結(jié)束監(jiān)聽辈挂,執(zhí)行?retValueEvent.stopWatching()。
3. 最後便是當(dāng)你開發(fā)dapp的時候裹粤,藉由觸發(fā)event寫入log终蒂,再觸發(fā)監(jiān)聽器執(zhí)行對應(yīng)動作,如此完成從 外界->鏈->鏈->外界 一個完整的執(zhí)行過程遥诉。對使用者來說就像一個是對資料庫操作的動作拇泣,只是這個資料庫變成了區(qū)塊鏈。
接下來以簡單的例子來介紹監(jiān)聽器更多的功能
contract depositAccount {
event Deposit(addressindexed_owner, uint _amount, uint _time);
function deposit() payable {
Deposit(msg.sender, msg.value, now);
}
}
首先先介紹第二行的indexed矮锈。
在Receipt裡如果有l(wèi)og霉翔,會寫在logs欄位裡,每筆log其中有兩部分:data和topics苞笨。一般event寫入的資料都會寫在data裡债朵,但如果在event的變數(shù)加入一個indexed屬性,到時候觸發(fā)時這個變數(shù)對應(yīng)寫入的值就會寫在topics裡瀑凝,在topcis裡的值可以用來當(dāng)作監(jiān)聽器的篩選條件序芦。
註:一份Receipt裡面可以有很多筆log(表示一次transaction可以觸發(fā)很多次event),一筆log最多只能儲存四個topics猜丹,而第一個topic必須是這個event的識別值identifier,代表一個event最多只能有三個變數(shù)可以有indexed屬性硅卢。
transaction的logs裡的其中一筆log
上面這張圖是其中某一筆log射窒,這個event沒有加indexed的變數(shù),值都會寫在data欄位裡(32byte為一單位接在一起将塑,圖中為十六進位的9和19)脉顿,topcis裡唯一一個值?0x35bd26...是該event的識別值。
下面這張圖是變數(shù)都加上indexed的event的log記錄:
event有indexed
因為所有變數(shù)都加上indexed点寥,所以data裡沒有值艾疟,值都寫在topics裡(十六進位的102和abcde)。
接下來在前端加入監(jiān)聽器:
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’}是加入的篩選條件敢辩,表示監(jiān)聽從第5000個區(qū)塊開始到最新的區(qū)塊蔽莱。如果event被觸發(fā),那callback函式就會印出是發(fā)生在第幾個區(qū)塊( result.blockNumber )戚长。
如果我們要用有加上indexed的變數(shù)(_owner變數(shù))來當(dāng)篩選條件的話盗冷,就指定_owner應(yīng)該要是多少,如果給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存錢的時候被觸發(fā)仪糖,存入超過0.05 ether的時候會印出存入的金額和存入時間柑司。
References:
[2]https://github.com/ethereum/wiki/wiki/JavaScript-API#contract-events