• 原文 [1] 作者:wissal haji[2]

  • 译文出自:登链翻译计划 [3]

  • 译者:翻译小组 [4]

  • 校对:Tiny 熊 [5]

欢迎订阅《Solidity 智能合约专栏》[6] 系列文章。

在上一篇 [7] 中,我们已经看到了如何使用 web3.js[8] 来构建一个 dapp。在今天的文章中,我们将看到如何使用 web3.js 从区块链中读取事件。

Solidity 中的事件就像你习惯于使用其他语言的日志记录功能一样,只是日志不是记录到控制台或文件,而是保存在以太坊区块链中。在下面的内容中,我们将看到:

  • 如何声明和触发一个事件

  • 什么是 "logsBloom" ,它如何用于高效搜索区块链数据?

  • 如何从区块链中读取一个事件?

  • 如何订阅事件

如何声明和触发事件?

为了声明一个事件,你需要使用以下语法:

    event event_name([data to record: var_type var_name])  

    // 例如 :  
    event MyEvent (  
          uint256 date,  
          string value  
    );  

事件的定义包含了事件的名称和你想在触发事件时保存的参数。

一旦你声明了你的事件,你就可以从一个函数中发出一个事件,如下所示 :

    emit MyEvent(block.timestamp, 'hello')。  

让我们来用一个例子,看看这到底是如何工作的。

在你的命令行终端,创建一个新的目录,并使用 truffle init 建立一个新的 Truffle 项目。使用你最喜欢的代码编辑器打开项目,并在合约文件夹中创建一个名为 EventExample.sol 的新合约,然后复制粘贴以下代码。

    // SPDX-License-Identifier: MIT  
    pragma solidity >=0.4.22 <0.8.0;  
    contract EventExample {  
       event DataStored(uint256 val);  
       uint256 val;  
       function storeData(uint256_val) external {  
             val =_val;  
             emit DataStored(val);  
      }  
    }  

创建相应的迁移文件 2_deploy_eventExample.js

    const EventExample = artifacts.require("EventExample");  

    module.exports = function (deployer) {  
       deployer.deploy(EventExample)。  
    };  

现在让我们使用 "truffle migrate" 部署合约,但在迁移之前不要忘记编辑 "truffle-config.js " 文件来配置网络和编译器。

在你的终端,使用 truffle console 启动 Truffle 控制台。我们要调用 storeData 函数,检查创建的交易的日志。先获取部署合约的实例。

    let eventExample = await EventExample.deployment()  

现在我们可以使用以下方法调用该函数。

    let tx = await eventExample.storeData(10)  

如果你打印交易收据日志 tx.receive.rawLogs 的内容,其中存储了交易执行过程中发生的触发事件的数组,你将得到一个数组,其中有一个对象,看起来像这样。

跟我学 Solidity:事件收据交易记录的内容交易日志

你可以看到,事件数据是以十六进制存储在数据字段下的。

Bloom 过滤器 如何帮助高效地检索区块链数据

为了高效地扫描区块链,寻找具有某些数据的事件,你可以在你想用来过滤区块链中记录的事件数据前面添加 indexed 关键字,例如:

    event DataStored(uint256 data1, uint256 indexed data2)  

让我们在刚才的例子中试一试:

    pragma solidity >=0.4.22 <0.8.0;  
    contract EventExample {  
       event DataStored(uint256 data1, uint256 indexed data2);  
       uint256 data1;  
       uint256 data2;  
       function storeData(uint256_data1, uint256_data2) external {  
          data1 =_data1;  
          data2 =_data2;  
          emit DataStored(data1, data2);  
       }  
    }  

你需要使用 migrate --reset 重新部署合约。

重复前面相同的步骤,获取已部署合约的实例,并调用 storeData 函数。

    > eventExample = await EventExample.deployment()  
    > tx = await eventExample.storeData(10, 15)  

然后检查 tx.receive.rawLogs,你会看到以下结果。

跟我学 Solidity:事件收据交易记录的内容交易日志

data1 字段显示在 data 下,而索引字段 data2 显示在 topics 字段下。一般来说,有索引的参数放在 topics 下,没有索引的参数放在 data 下。

第一个话题是事件签名的哈希值,在 Solidity 文档 [9] 中有如下描述:

topics[0]: keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")"。(canonical_type_of 是一个简单地返回给定参数的规范类型的函数,例如,对于 uint indexed foo,它将返回 uint256)。如果事件被声明为 " 匿名 ",则不会生成 "topics[0]"。

注:一个事件中可索引的参数数量最多为三个。

每个区块头都包含一个名为 "logsBloom " 的字段,这是 Bloom 过滤器 [10],由区块交易列表部分的每笔交易的收据中包含的可索引信息 (日志地址和日志主题) 组成。

当在整个区块链或部分区块链数据中寻找属于某个地址或包含某些数据的特定事件时,节点会通过区块头并检查每个区块的 Bloom 过滤器,以决定该区块是否包含所需的日志。跟我学 Solidity:事件