Skip to content

Commit

Permalink
update doc of event
Browse files Browse the repository at this point in the history
  • Loading branch information
vita-dounai committed Sep 19, 2020
1 parent cf1f700 commit 5bfbf05
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
64 changes: 64 additions & 0 deletions docs/in_depth/event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# 事件

事件是区块链底层虚拟机日志基础设施提供的一个便利接口。当触发事件时,事件中的参数存储到交易收据的日志字段中,日志时一种特殊的数据结构,这些日志与合约地址关联,并随交易收据记录到区块链中。每条交易收据中可以包含0条或多条日志记录,其代表了智能合约出发的事件。在DApp应用中,如果监听了某事件,则当该事件发生时,便会触发应用的回调。

## 创建事件

Liquid中使用结构体语法定义事件,以`struct`关键字表示。结构体中的每一个字段便是事件的参数,为向Liquid表明该此结构体定义了事件,您需要为此结构体添加`#[liquid(event)]`属性:

```rust
#[liquid(event)]
struct Foo {
s: String,
i: i32,
}
```

如上述代码所示,我们定义了一个名为Foo的事件,事件中包含两个参数,分别为`String``i32`。更进一步,您还可以使用`#[liquid(indexed)]`属性将事件参数指定为可被索引:

```rust
#[liquid(event)]
struct Foo {
#[liquid(indexed)]
s: String,
i: i32,
}
```

被索引的参数本身不会被保存,但是Dapp应用可以通过被索引参数的值来对事件进行检索。在Liquid中,一个事件最多可有四个值用于被索引,但是第一个索引值恒定为事件签名(事件名及其参数类型)的哈希值,因此在事件定义中,您最多可以将三个参数被标注为`#[liquid(indexed)]`

与状态变量定义类似,不能为定义事件的结构体添加可见性声明或模板参数,但和状态变量定义不同的时,其内部每个字段也不允许添加可见性声明。事件参数及索引参数的类型均受到限制,具体的限制可参考[类型](../types.html)一节。

## 触发事件

在Liquid中,通过环境对象触发事件。环境对象由Liquid自动为您生成,您可以在合约方法中通过调用`self.env()`来获取环境对象。获取环境对象后,您可以调用环境对象的`emit`方法来触发我们在上面定义的事件:

```rust
self.env().emit(Foo {
s: String::from("hello"),
i: 42,
})
```

如上述代码所示,`emit`方法以事件实例为参数,而事件实例可通过结构体初始化语法直接进行构造。提供给`emit`方法的参数的类型一定需要是事件类型(即存在`#[liquid(event)]`属性标注),否则在编译会抛出类型不匹配的错误。触发事件后,对应交易的回执中会多出一条日志记录:

```json
"logs": [
{
"address": "0x6119432a43a2a5da27f31fa4912f1c43400b1690",
"data": "0x00000000000000000000000000000000000000000000000000000000000002a",
"topics": [
"0x1be2d150ed559c350b05f7dfa5a74669ec8d2ce63bb14c134730ffa02d2d111c",
"0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
]
}
]
```

其中,`address`字段是合约地址;`data`字段中保存了非索引参数的[ABI编解码](https://solidity.readthedocs.io/en/v0.7.1/abi-spec.html#formal-specification-of-the-encoding),此处因为我们只有一个非索引参数`i`,因此`data`字段中只保存了它的值42;`topics`字段包含了两个可用于索引该事件的值,其中第一个是事件签名的哈希值,而第二个值则是事件中索引参数`s`的值的哈希值。对于`String`这类动态对象,Liquid会将它们的哈希值作为索引值,以提高检索效率及减少存储空间占用。因此若您需要在应用中按照字符串检索事件,您需要在本地计算待检索字符串的哈希值。

```eval_rst
.. admonition:: 注意
Liquid目前支持两种哈希计算方式(`keccak256`及`sm3`,见`创建合约模块`一章),若您在应用中需要使用动态对象索引事件,请确保您所用的哈希计算方式与Liquid合约一致。
```
59 changes: 59 additions & 0 deletions docs/in_depth/method.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,62 @@ impl Foo {
|序列容器`Vec<T>`| `pub fn foo(&self, index: u32) -> T` |
|映射容器`Mapping<K, V>`| `pub fn foo(&self, key: K) -> V` |
|可迭代映射容器`IterableMapping<K, V>`| `pub fn foo(&self, key: K) -> V` |

## 杂注

在合约模块中,Liquid只允许存在一个`impl`代码块,且只能用于为定义状态变量的结构体添加成员函数(即合约方法)。当您在合约模块内定义了某个另外的结构体并试图为其实现成员函数时,Liquid将会发出编译错误:

```rust
#[liquid::contract(version = "0.1.0")]
mod foo {
#[liquid(storage)]
struct Foo {
bar: String,
}

// 合约方法
impl Foo {
// ...
}

// 另外一个普通结构体的定义
struct Ace {
// ...
}

// 编译错误,存在多个impl代码块
impl Ace {
// ...
}
}
```

若您的确有这种需求,可以采用将该结构体的定义及成员函数的实现挪出合约模块,然后在合约模块内引用该结构的定义:

```rust
// 另外一个普通结构体的定义
struct Ace {
// ...
}

// 编译通过
impl Ace {
// ...
}

#[liquid::contract(version = "0.1.0")]
mod foo {
// 引用外部定义
use super::Ace;

#[liquid(storage)]
struct Foo {
bar: String,
}

// 合约方法
impl Foo {
// ...
}
}
```
1 change: 1 addition & 0 deletions index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ Liquid是一种专用于为区块链底层平台[FISCO BCOS](https://fisco-bcos-
docs/in_depth/create_contract.md
docs/in_depth/state.md
docs/in_depth/method.md
docs/in_depth/event.md
docs/in_depth/types.md
```

0 comments on commit 5bfbf05

Please sign in to comment.