事件日志
日志(Logs)是存储在以太坊区块链上的数据结构,用于记录发生在智能合约中的事件。
当事件被触发时,相应的日志条目会被创建并添加到区块中。这些信息可以由区块链上的外部消费者(比如前端应用和服务器应用)读取和做出响应。
通过日志,智能合约可以生成一种轻量级的输出,无需更改合约的状态或者进行复杂的计算。
获取交易日志
通过 eth_getTransactionReceipt
RPC 方法可以访问交易回执,回执里面的 logs 数据,就是这里所说的日志。
合约执行每触发一次事件,在交易回执里的 logs 数据项数组里就会多一条日志条目。
一个交易日志例子:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockHash": "0x6a0e591eb3c266bd1d499f17caea929dc3a1b396b6e08d3d12b1df7abb4b1daf",
"blockNumber": "0x12d11ab",
"contractAddress": null,
"cumulativeGasUsed": "0x679b6b",
"effectiveGasPrice": "0x1ff2895dc",
"from": "0xe4de99fbf078eff6ba02b076e503b304c2a7b1c5",
"gasUsed": "0xe074",
"logs": [
{
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000e4de99fbf078eff6ba02b076e503b304c2a7b1c5",
"0x0000000000000000000000006177d42837d88b1f6dd0ad14e2f80c17df3d0617"
],
"data": "0x000000000000000000000000000000000000000000000000000000000ee6b280",
"blockNumber": "0x12d11ab",
"transactionHash": "0xe26a8add9f159c22d8d10f4b4069ed20ec9245ba07bf742dc2054d6b2561654a",
"transactionIndex": "0x4b",
"blockHash": "0x6a0e591eb3c266bd1d499f17caea929dc3a1b396b6e08d3d12b1df7abb4b1daf",
"logIndex": "0xb2",
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000800000000000000000008000008000400000000010000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000100000000000000000000000010000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000002000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
"to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"transactionHash": "0xe26a8add9f159c22d8d10f4b4069ed20ec9245ba07bf742dc2054d6b2561654a",
"transactionIndex": "0x4b",
"type": "0x2"
}
}
事件日志由两部分组成:主题(topics)和数据(data)。
事件日志主题
主题用于对日志事件进行索引,从而可以通过这些索引更快地搜索和过滤事件。
第一个主题默认是事件签名的哈希值。这可以保证事件类型的唯一性,方便检索。
可以选择将事件的一些参数使用 indexed 关键字来标记指定为主题。每个事件最多可以有三个额外的 indexed 参数,因此最多有四个主题。
事件签名
事件签名是由事件的名称和输入参数的类型列表按顺序组合后形成的一个字符串。
例如,ERC20 转账事件:event Transfer(address indexed from, address indexed to, uint256 value);
其事件签名为:Transfer(address,address,uint256)。
对事件签名进行 keccak256 运算得到事件签名哈希值。
上面这个真实交易的第一个主题值就是事件签名哈希值(keccak256(Transfer(address,address,uint256)) = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
)
事件签名的哈希值作为事件的第一个主题,是事件唯一性的关键,使得不同的事件即使名称相同,只要参数类型列表不同,其签名也会不同,从而确保了区块链上的事件可以被准确地识别和搜索。
主题值的计算
由于EVM在很多方面基于32字节的字宽,这要求所有日志的主题也必须符合这种固定大小。当事件的参数(特别是索引参数)的大小不满32字节时,会进行特定的处理以匹配这个长度要求。
对于固定大小的类型(如 uint256, address 等),直接使用其 ABI 编码的结果。比如,地址会被填充到 32 字节。
对于动态类型(如 string 或 bytes),你不能直接将它们标记为 indexed,因为它们是动态的。
但如果你确实需要在事件中使用这些类型的数据作为筛选条件,通常的做法是在触发事件时,传入这些值的哈希值。这种方法允许你把动态大小的数据转化为固定大小的32字节数据,适合作为主题使用。
匿名事件
在Solidity中,事件也可以被声明为匿名事件。匿名事件的第一个主题不会自动包括事件的签名哈希。这意味着匿名事件的第一个主题可以自定义或使用与非匿名事件完全不同的数据,使得对于某些特定应用,可以有更灵活的索引和匹配方式。
匿名事件在某些特定情况下可以减少主题使用的空间,或者保持事件触发的细节不被轻易追踪。
事件日志数据
除了主题之外,合约事件还可以包含数据。 这些数据不是索引的一部分,但可以存储额外的信息。访问数据通常比访问主题成本稍高,因为数据在日志中是不进行索引的。
event Transfer(address indexed from, address indexed to, uint256 value);
在这个例子中,value 是数据参数。