概述
本篇文章將描述Ethereum的Event系統(tǒng)。在以太坊的合約代碼中,經(jīng)常會看到emit SomeEvent(...)的調(diào)用,對這里比較有困惑,查找了好些資料,整理出如下文檔。
官網(wǎng)描述
在solidity的官方文檔,對Event有如下描述:
-
Event是以太坊EVM日志功能的頂層抽象; - 應(yīng)用程序可以通過
Ethereum client的RPC接口來訂閱、監(jiān)聽指定的Event。
在Ethereum的節(jié)點中,Event通過機(jī)制如下實現(xiàn):
solidity的合約通過編譯為字節(jié)碼,存儲至Ethereum的區(qū)塊鏈中;當(dāng)一個交易中有合約調(diào)用時,先從區(qū)塊鏈的數(shù)據(jù)庫中加載當(dāng)前Ethereum的合約字節(jié)碼(通過合約地址標(biāo)識),然后依據(jù)交易傳入的ABI信息,調(diào)用合約中相應(yīng)的功能;當(dāng)合約中某個功能有Event調(diào)用時,字節(jié)碼指令(LOG?)會將Event參數(shù)存儲在交易日志中--塊鏈中的特殊數(shù)據(jù)結(jié)構(gòu);
該日志結(jié)構(gòu)與合約地址關(guān)聯(lián),被寫入塊鏈存儲且永不刪除(在以太坊的Frontier、Homestead階段,永遠(yuǎn)不會刪除這些日志數(shù)據(jù);但在Serenity階段,可能這些Log會從塊鏈數(shù)據(jù)中刪除)。
在以太坊的智能合約中,無法訪問這些日志數(shù)據(jù)。
以太坊的日志也可以提供SPV(simple payment verification)證明;可以通過提供塊哈希,以及merkle樹路徑,來驗證某條log存在于指定的區(qū)塊中。
同時可以給Event中最多三個參數(shù)添加索引indexed屬性,以太坊會將這些索引參數(shù)添加至類似于topics的結(jié)構(gòu)中,而不是放到日志的數(shù)據(jù)部分;如果使用數(shù)組作為索引,會計算該數(shù)組的Keccak-256索引,然后存儲在topics結(jié)構(gòu)中,因為一個topic只能存放32字節(jié)的數(shù)據(jù)。
所有不帶索引indexed屬性的參數(shù)會被編碼進(jìn)Log的數(shù)據(jù)部分。
可以使用topics中的條目來搜索一些區(qū)塊中的特定事件;也可以通過合約地址來過濾事件。
以太坊中的LOG指令處理
LOG0: {
execute: makeLog(0),
...
},
LOG1: {
execute: makeLog(1),
...
},
...
LOG4: {
execute: makeLog(4),
...
},
// following functions are used by the instruction jump table
// make log instruction function
func makeLog(size int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
topics := make([]common.Hash, size)
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
topics[i] = common.BigToHash(stack.pop())
}
d := memory.GetCopy(mStart.Int64(), mSize.Int64())
interpreter.evm.StateDB.AddLog(&types.Log{
Address: contract.Address(),
Topics: topics,
Data: d,
// This is a non-consensus field, but assigned here because
// core/state doesn't know the current block number.
BlockNumber: interpreter.evm.BlockNumber.Uint64(),
})
interpreter.intPool.put(mStart, mSize)
return nil, nil
}
}
在事件處理中,非索引indexed屬性的參數(shù)會被編碼為LOG的數(shù)據(jù)部分,放在調(diào)用合約數(shù)據(jù)的第一個參數(shù)位置,剩余的索引參數(shù)緊隨其后。當(dāng)前以太坊支持5個LOG指令(log0, log1, log2, log3, log4),log?中的?表示最多支持幾個索引;由于Log本身有一個索引,即Event的簽名.
查閱的資料
Where do contract event logs get stored in the Ethereum architecture?