Event Log

Event Log 是虚拟机的重要功能之一,用于在虚拟机运行合约过程中,输出特定二进制数据并记录在交易信息TransactionInfo中, Event Log可帮助开发人员确认,检查和快速检索智能合约的特定状态。本文介绍 Event 机制基础并介绍如何解码 Event Log 。

合约中如何定义和触发Event

使用Solidity语言编写合约时,通过 event 关键字定义Event,通过 emit 关键字触发 Event 。 在定义Event时,不仅可以指定事件名称,还可以指定若干参数,用来输出特定数据, 下面以TRC20合约的Transfer事件为例:

contract ExampleContractWithEvent {
    event Transfer(address indexed from,address indexed to, uint256 value);
    constructor() payable public{}
    function contractTransfer(address toAddress, uint256 amount){
        toAddress.transfer(amount);
        emit Transfer(msg.sender,toAddress, amount);
    }
}
  • event Transfer(address indexed from,address indexed to, uint256 value) 定义了一个名为Transfer的事件,包含三个参数,第一个是 from 表示转账发起地址,第二个是to表示转账目的地址,第三个是 value 表示转账的数额
  • emit Transfer(msg.sender,toAddress, amount) 指定在完成转账后,触发对应的Event,Event中包含转账发起地址,转账目的地址和转账金额。

注意:Solidity代码规范中,一般要求Event名字首字母大写,以区别于对应的函数。例如函数transfer和事件Transfer。

LOG

Solidity 使用 LOG 指令在交易信息TransactionInfo中记录事件信息。事件信息位于 TransactionInfo 的 log 字段,下面通过gettransactioninfobyid API 获取到一个TransactionInfo来说明event的结构:

{
    "id": "88c66d08f15b983183c7f7d23e3fafec0320bcc837d67957a8bda58d04ca53e1",
    
    ......
    
    "log": [
        {
            "address": "a614f803b6fd780986a42c78ec9c7f77e6ded13c",
            "topics": [
                "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
                "00000000000000000000000079309abcff2cf531070ca9222a1f72c4a5136874",
                "00000000000000000000000081b64b1c09d448d25c9eeb3ee3b8f3348a694c96"
            ],
            "data": "00000000000000000000000000000000000000000000000000000000b2d05e00"
        }
    ]
}

log : 交易中触发的Event列表,对于每一个Event,包含以下三部分内容:

  • address : 合约地址。为了兼容EVM, 虚拟机中的地址为不带前缀0x41的hex格式地址,因此如果要解析log中的地址,需要首先在log中的地址前面加上41,然后再转换为Base58格式。
  • topics : 事件的主题,包括事件本身和标记为indexed的参数。使用 topics 保存 indexed 参数的原因是:区块链储存一般会选择使用 LevelDB 或 RockDB 一类的 key-value 储存引擎。这些引擎一般都支持 prefix-scan 操作,将indexed参数放到topics中,既可以快速检索 Transfer 事件,又可以快速检索某一特定 toAddressTransfer 事件。
  • data : 事件的非indexed参数,例如amount

LOG 解码

对于上述LOG章节中的事件,想要对其解码,首先要知道合约中事件的ABI,下面是Transfer Event的ABI:

{
    "anonymous":false,
    "inputs":
    [
        {"indexed":true,"name":"from","type":"address"},
        {"indexed":true,"name":"to","type":"address"},
        {"indexed":false,"name":"value","type":"uint256"}
    ],
    "name":"Transfer",
    "type":"event"
}

将Event log与ABI对照来解码数据:

  • topics[0]:ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef 是事件本身,该值为keccak256('Transfer(address,address,uint256)')计算后的结果,因此该事件为Transfer事件。事件的keccak256哈希值可以通过tronweb.sha3()计算得到。注意:keccak256的参数为字符串,其中不能有任何空格,否则,计算出的哈希值将不同。
  • topics[1]:00000000000000000000000079309abcff2cf531070ca9222a1f72c4a5136874 是第一个indexed参数from。这里的地址为去掉前缀0x41的20个字节地址,因此对于这个参数,拿到最后40位数据,再在前面加上41就得到了TRON网络HEX格式地址。
  • topics[2]:00000000000000000000000081b64b1c09d448d25c9eeb3ee3b8f3348a694c96 是第二个indexed参数to,接收者账户地址,解析同上。
  • data : 00000000000000000000000000000000000000000000000000000000b2d05e00 是非indexed参数的值,如果存在多个非indexed参数,则按照ABI编码规则依次列出,具体请参考ABI编码规则。对于这个例子,非indexed参数只有一个,即value,转账数额,该数据为16进制数据,将其转为十进制即可。