虚拟机的 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 9 months ago