Solidity-事件日志
定义
事件是外部事件获取EVM内部状态变化的一个手段,使用 event 关键字定义事件,使用 emit 来触发定义的事件
// 定义事件
event Deposit(address indexed _from, uint _value);
// 触发事件
emit Deposit(msg.sender, value);
日志包含的内容有:
-
address
:表示当前事件来自哪个合约。 -
topics
:事件的主题, 定义事件参数时加上indexed
来标记指定为主题, 每个事件最多可以有三个额外的 indexed 参数,因此最多有四个主题。 -
data
: 事件的参数数据(非索引的参数数据)
每个事件的第一个主题默认是事件签名的哈希值, 是事件唯一性的关键,使得不同的事件即使名称相同,只要参数类型列表不同,其签名也会不同,从而确保了区块链上的事件可以被准确地识别和搜索
有索引的参数放在 topics 下,没有索引的参数放在 data 下,以太坊会为日志地址及主题创建 Bloom
过滤器,以便更快的对数据检索
事件操作
在合约内触发事件后,在外部可以通过一些手段获取或监听到该事件:
-
通过交易收据获取事件:
eth_gettransactionreceipt()
-
使用过滤器获取过去事件:
eth_getlogs()
-
使用过滤器获取实时事件: 建立长链接, 使用
eth_subscribe
订阅
使用场景
-
链上存储成本很高,一些变量定义如果不是被经常调用或者必须使用,就可以考虑使用事件来存储,能降低很多 Gas 成本
-
用事件记录完整的交易历史
日志的底层存储
以太坊区块结构中区块头中有一个ReceiptHash
字段,代表交易回执的 Merkel 树结构的根节点哈希值, 日志就存储于交易回执数据中
// core/types/block.go
if len(receipts) == 0 {
b.header.ReceiptHash = EmptyReceiptsHash
} else {
b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)
b.header.Bloom = CreateBloom(receipts)
}
交易回执数据结构
// core/types/receipt.go
type Receipt struct {
....
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
...
}
- Logs: 交易事件日志集合
- Bloom:是从 Logs 中提取的事件布隆过滤器,用于快速检测某主题的事件是否存在于 Logs 中,也就是上面提到的为没有加 indexed 关键字创建索引对应的数据日志创建的
Bloom
过滤器,以便更快的对数据检索
Log 数据结构
type Log struct {
// Consensus fields:
// address of the contract that generated the event
Address common.Address `json:"address" gencodec:"required"`
// list of topics provided by the contract.
Topics []common.Hash `json:"topics" gencodec:"required"`
// supplied by the contract, usually ABI-encoded
Data []byte `json:"data" gencodec:"required"`
// Derived fields. These fields are filled in by the node
// but not secured by consensus.
// block in which the transaction was included
BlockNumber uint64 `json:"blockNumber" rlp:"-"`
// hash of the transaction
TxHash common.Hash `json:"transactionHash" gencodec:"required" rlp:"-"`
// index of the transaction in the block
TxIndex uint `json:"transactionIndex" rlp:"-"`
// hash of the block in which the transaction was included
BlockHash common.Hash `json:"blockHash" rlp:"-"`
// index of the log in the block
Index uint `json:"logIndex" rlp:"-"`
// The Removed field is true if this log was reverted due to a chain reorganisation.
// You must pay attention to this field if you receive logs through a filter query.
Removed bool `json:"removed" rlp:"-"`
}