Event 机制
Event 基础
Log是虚拟机的重要功能之一,用于在虚拟机运行合约过程中,输出特定二进制数据并记录在交易回执(transaction-info / transaction-receipt)中。基于Log功能实现了 Event(事件)机制,可帮助开发人员确认,检查和快速检索智能合约的特定状态。本文介绍 Event 机制基础并介绍如何解码Event Log 。
Solidity Event
Solidity中通过event关键字定义事件。通过emit关键字触发并记录事件。Event不止是特定事件名称,还可以包含若干参数。
contract EventExampleContract {
event Transfer(address indexed toAddress, uint256 amount);
constructor() payable public{}
function contractTransfer(address toAddress, uint256 amount){
toAddress.transfer(amount);
emit Transfer(toAddress, amount);
}
}
如上代码:
- event Transfer定义了一个Transfer事件,其中的参数:toAddress 表示转账目标地址;amount代表转账的数额
- emit Transfer(toAddress, amount) 在这段代码上触发同名的Event
代码规范中一般要求 Event 名字首字母大写,区别于对应函数。例如事件Transfer和transfer函数。
Event结构
Solidity使用LOG 指令在交易回执(transaction-info / transaction-receipt)中记录事件信息。关键代码在TransactionInfo的log字段,相关 protobuf 定义如下:
message Log {
bytes address = 1;
repeated bytes topics = 2;
bytes data = 3;
}
message TransactionInfo {
// A list of LOG represent list of events in a transaction
repeated Log log = 8;
}
其中 topics 字段表示事件的主题,例如Transfer(...)。同时,所有标记为indexed 的参数也依次在 topics 字段列出。
data 字段表示事件的其他非 indexed 参数,例如:amount。
使用 topics 用来保存 indexed 参数,区块链储存一般会选择使用 LevelDB 或 RockDB 等 key-value 储存引擎。这些引擎一般都支持 prefix-scan 的操作,既可以快速检索 Transfer 事件,又可以快速检索某一特定 toAddress 的 Transfer 事件。
Event 解码示例
上述 Transfer 事件的ABI定义为:
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "toAddress",
"type": "address"
},
{
"indexed": false,
"name": "amount",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
}
使用 gettransactioninfobyid API 获取到如下结果:
log:
address:
289C4D540B32C7BC56953E55631F8B141EB86434 # contract address in 20-byte format
data:
0000000000000000000000000000000000000000000000000000000000000001
topics:
69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2 # topics[0]
000000000000000000000000E552F6487585C2B58BC2C9BB4492BC1F17132CD0 # topics[1]
与 ABI 一起对照可以得到:
- topics[0] 69ca02dd4edd7bf0a4abb9ed3b7af3f14778db5d61921c7dc7cd545266326de2 即 keccak256("event(address,uint256)"), 表示事件本身
- topics[1] 000000000000000000000000E552F6487585C2B58BC2C9BB4492BC1F17132CD0 是第一个 indexed 参数 toAddress。需要注意的是,为了于 EVM 兼容,这里使用了兼容地址格式,即去掉地址的前缀 0x46,成为20字节地址。
- data 字段是 0000000000000000000000000000000000000000000000000000000000000001 。代表 amount=1 (uint256 类型)。当存在多个非 indexed 参数时,按照 ABI 编码规则依次列出。
Updated over 3 years ago