【区块链安全 | 第二十九篇】合约(三)_事件合约
文章目录
合约
事件(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 参数)。
注意:
由于交易日志仅存储事件数