合约
事件(Events)
Solidity 中的事件是对 EVM 日志功能的抽象封装。应用程序可以通过以太坊客户端的 RPC 接口订阅和监听这些事件。
事件可以在文件级别定义,或作为合约(包括接口和库)的可继承成员定义。当事件被触发时,其参数会被记录在交易的日志(log)中 —— 区块链上的一种特殊数据结构。这些日志与触发事件的合约地址相关联,包含在区块链中,并会一直保留,只要区块仍可访问(目前是永久保留,但未来可能会改变)。日志及事件数据在合约内部不可访问(即使是触发该事件的合约本身也不行)。
日志可以请求 Merkle 证明,因此如果外部实体向合约提供此类证明,合约可以验证该日志确实存在于区块链中。但由于合约只能访问最近 256 个区块的哈希,因此必须提供区块头信息。
你可以给最多三个事件参数添加 indexed
属性,使它们被加入到称为“主题(topics)”的特殊数据结构中,而不是日志的 data 部分。每个 topic 只能保存一个 word(32 字节),因此如果为引用类型参数加上 indexed
,将存储其 Keccak-256 哈希值。
没有 indexed
修饰的参数,会被 ABI 编码后存入日志的 data 部分。
使用 topics
可以方便地筛选事件,例如从一系列区块中过滤出特定事件。你还可以通过合约地址过滤事件。
例如,以下代码使用 web3.js 的 subscribe("logs")
方法,按特定地址匹配某个 topic:
var options = {
fromBlock: 0,
address: web3.eth.defaultAccount,
topics: ["0x0000000000000000000000000000000000000000000000000000000000000000", null, null]
};
web3.eth.subscribe('logs', options, function (error, result) {
if (!error)
console.log(result);
})
.on("data", function (log) {
console.log(log);
})
.on("changed", function (log) {
});
事件的 函数签名哈希 是默认写入 topics 的一项,除非你在声明事件时使用了 anonymous
关键字。也就是说:
- 非匿名事件 可以通过事件签名筛选;
- 匿名事件 无法通过事件名筛选,只能按合约地址筛选;
- 但匿名事件的好处是:部署和调用成本更低,并且允许使用 4 个
indexed
参数(非匿名最多只能用 3 个)。
注意:由于交易日志仅存储事件数据,而不记录其类型信息,因此你必须清楚事件的结构,才能正确解析日志。这包括哪些参数是 indexed
&#