type
status
date
slug
summary
category
tags
icon
password
操作
事件(Event)是区块链智能合约中重要的组成部分,用于记录链上行为并触发链下监听器的反应。在本文中,我们将梳理事件的关键概念,结合两个示例解析其数据结构和设计逻辑。

什么是事件?

在智能合约中,事件是一种特殊的日志,用于向外界广播链上发生的特定行为。它包含事件签名(Event Signature)、索引参数(Indexed Parameters)和非索引参数(Unindexed Parameters),并以特定的数据结构形式存储在交易日志中。

事件的基本结构

在 Solidity 中,事件通常定义如下:
事件的触发(emit)会生成一条日志(log),其底层结构如下:

1. Event Signature Hash

事件签名是事件的唯一标识,其哈希值(Event Signature Hash)通过以下方式计算:
例如,对于一个事件:
其事件签名为:
而对应的哈希值为:

2. Topics

事件的topics是一个数组,用于存储事件签名哈希和索引参数:
  • 第一项(topics[0]):事件签名哈希。
  • 后续项(topics[1]topics[n]):索引参数(Indexed Arguments)。

3. Data

非索引参数(Unindexed Arguments)以字节形式存储在data字段中。这些参数通常包含更多细节信息,但无法直接通过索引查询。

4. Indexed 参数限制

每个事件最多允许三个索引参数,这是以太坊日志存储的限制之一。

字段命名和相似概念

为了更好地组织事件信息,以下是几个常用字段的命名建议:

字段列表

字段名称
描述
event_signature_hash
事件签名的哈希值(keccak256)。
event_signature
事件签名的明文(如 Transfer(address,address,uint256))。
topics
包含事件签名哈希和所有 indexed 参数的值。
data
包含所有未 indexed 参数的整体编码。
value(可选)
data 中提取的某个具体逻辑字段。根据实际情况可能包含多个字段,也可以不用这个名称。

相似概念对应关系

如下图,如果我们将事件定义拆分为事件标识符(即event_signature_hash)和参数列表(Args[]),则它们之间有如下对应关系:
notion image

示例解析

示例 1: 经典的 Transfer 事件

以下是 ERC20 合约中 Transfer 事件的定义和触发:
数据结构:
假设调用 transfer(0xB, 100),生成的日志如下:
字段名称
示例值
event_signature_hash
keccak256("Transfer(address,address,uint256)")
topics[0]
keccak256("Transfer(address,address,uint256)")
topics[1]
0xA (from 地址)
topics[2]
0xB (to 地址)
data
0x0000000000000000000000000000000000000000000000000000000000000064 (value: 100)
value
100 (本示例中,从data单独提取的逻辑字段)
解析:
  • topics[0] 是事件签名的哈希值,标识了事件类型。
  • topics[1]topics[2] 存储了 fromto 地址,这些是 indexed 参数。
  • data 存储未 indexedvalue,即转账金额。
  • value 提取自 data,逻辑上与其一致。

示例 2: 更复杂的事件

以下是一个包含多个未 indexed 参数的事件定义:
数据结构:
假设调用 purchase(42, "Laptop", 2, 1500),生成的日志如下:
字段名称
示例值
event_signature_hash
keccak256("ComplexEvent(address,uint256,string,uint256,uint256)")
topics[0]
keccak256("ComplexEvent(address,uint256,string,uint256,uint256)")
topics[1]
0xA (user 地址)
topics[2]
42 (itemId)
data
ABI.encode("Laptop", 2, 1500)
value
1500 (提取的价格字段)
解析:
  • topics[0] 是事件签名的哈希值。
  • topics[1]topics[2] 存储了 indexeduseritemId
  • data 包含未 indexeddescriptionquantityprice,按 ABI 编码存储。
  • value 单独存储 price,便于查询和逻辑处理。
注意:
这种事件示例展示了同时使用索引参数和非索引参数的场景。
  • data 包含了多个字段的信息(descriptionquantityprice)。
  • value 是从 data 中提取的具体字段。

常见问答

1. 事件签名是否使用了数字签名技术?为什么要这样命名?备选名称有哪些?

事件签名(Event Signature)并不涉及数字签名技术。它只是一个以keccak256哈希生成的标识符,用于唯一标记事件的结构。这种命名可能会让人误解为涉及加密学签名,因此一些备选名称可能更直观,例如:
  • event_fullname (个人推荐)
  • event_identifier

2. 为什么事件签名需要包含参数类型列表?为什么不包含参数名称?

事件签名包含参数类型列表(比如address, address, uint256)是为了确保其唯一性和可识别性。如果只有事件名称而不包括参数列表,可能导致事件重复。
参数名称(比如to, from)虽然便于人类理解,但不影响事件的功能实现,因为名称仅在编译器层面有意义,而链上的事件结构主要依赖于类型。

3. 既然函数有函数选择器,事件是否也有事件选择器?为什么?

合约存在“函数选择器”,它是函数签名的前4字节(即8个16进制字符)前缀,用于提高效率。
而事件并没有“事件选择器”,因为topics[0]本质上已经承担了类似选择器的功能,它存储事件签名的完整哈希值,确保事件的唯一性,无需进一步优化。

4. 当前选择的哈希算法简介

以太坊使用的keccak256是基于SHA-3标准的变种,与常见的哈希算法对比:
算法
哈希长度
特性
链上用途
MD5
128位
不安全,易被碰撞
几乎不使用
SHA-256
256位
安全,广泛用于区块链
比特币哈希计算
Keccak
256位
更快、更适合硬件实现
以太坊事件和函数标识

5. data和value是同一个概念吗?如何区分它们?

datavalue并不是同一个概念:
  • data:用于存储事件的非索引参数,可能包含多个字段。
  • value:通常指特定字段的值,可能是data的一部分。
例如,在ComplexEvent中,data包含了to地址、note字符串和amount整数,而value则可能单独指代amount字段。
 
Roam Research:如何在块内显示[[roam/js]]的结果为什么令人讨厌?两个沟通坏习惯
Loading...
董振业
董振业
产品经理 | 新手奶爸 | 新手创作者
最新发布
哭闹测试:设定规则的好办法
2025-2-6
带娃笔记:不要试图用笑来掩盖哭
2025-2-6
带娃笔记:学会告别
2025-2-6
Roam Research:如何在块内显示[[roam/js]]的结果
2025-1-5
在 Excel 中使用IMAGE函数批量展示图片
2025-1-4
理解EVM中的事件
2025-1-3