虚拟机的 Event 机制
Event 基础
Log 是虚拟机的重要功能之一,用于在虚拟机运行合约过程中,输出特定二进制数据并记录在交易回执(transaction-info / transaction-receipt)中。基于 Log 功能实现了 Event (事件)机制,可帮助开发人员确认,检查和快速检索智能合约的特定状态。本文介绍 Event 机制基础并介绍如何解码 Event Log 。
Solidity Event
Solidity 中的 Event 通过 event
关键字定义。触发并记录 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("Transfer(address,uint256)")
, 表示事件本身topics[1]
000000000000000000000000E552F6487585C2B58BC2C9BB4492BC1F17132CD0 是第一个indexed
参数toAddress
。需要注意的是,为了于 EVM 兼容,这里使用了兼容地址格式,即去掉地址的前缀0x41
,成为 20 字节地址。data
字段是 0000000000000000000000000000000000000000000000000000000000000001 。代表 amount=1 (uint256 类型)。当存在多个非indexed
参数时,按照 ABI 编码规则依次列出。
Event 的订阅
在实际应用中,可以通过订阅事件的方式辅助完成开发需求。可参考如下文档:
Updated about 1 year ago