ETH Event中的Topic

在ETH合约执行过程中,为了让外界感知某些执行的结果,通常会选择emit Event。这其中ERC20系列的Token最为典型。通常在Token转账过程中会发送一个Transfer Event来告知使用者交易的结果。其他人通过Web3监听事件,或者通过获取交易的Receipt中的Log来获取交易结果。今天我们主要讲解的是ReceiptLog的构成及解析。

Log的形成

Receipt中的Log,就是对应Event的执行的结果,我们首先来看一个Receipt的结构:

1
eth.getTransactionReceipt('0xcd5362e18dc85b339cd625bf8ef79760684169f1bcdab302f16bee0c753a731e')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
blockHash: "0x3575b2f910bc812a37d0adf77b999a269832ba59bb25cc78c58c4a0f948b2481",
blockNumber: 12751,
contractAddress: null,
cumulativeGasUsed: 113863,
from: "0x81f138f2893bcf738879832934a41dab1477e931",
gasUsed: 113863,
logs: [{
address: "0xe35cd82bdb5b39973254d4e75b49715c2ff2af90",
blockHash: "0x3575b2f910bc812a37d0adf77b999a269832ba59bb25cc78c58c4a0f948b2481",
blockNumber: 12751,
data: "0x00000000000000000000000000000000000000000000000579a814e10a7400000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000081f138f2893bcf738879832934a41dab1477e9310000000000000000000000000000000000000000000000000000000000000042353866363637356635366334333533303539626462363365613965363430333363666564663063333962626131326432383234313932396436623862646261365f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023324e354c6a4d6255764a4c547a3653444b556477433252333273384e5143456e3258380000000000000000000000000000000000000000000000000000000000",
logIndex: 0,
removed: false,
topics: ["0xf5577aec900779fdc4060207f95a90077d57e4ac39af49c805a073b9b1b852ea", "0x00000000000000000000000022b0588fc66a4bd4e90d6b456a0a774e403b1068"],
transactionHash: "0xcd5362e18dc85b339cd625bf8ef79760684169f1bcdab302f16bee0c753a731e",
transactionIndex: 0
}, {
address: "0xe35cd82bdb5b39973254d4e75b49715c2ff2af90",
blockHash: "0x3575b2f910bc812a37d0adf77b999a269832ba59bb25cc78c58c4a0f948b2481",
blockNumber: 12751,
data: "0x00000000000000000000000000000000000000000000000579a814e10a740000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000042353866363637356635366334333533303539626462363365613965363430333363666564663063333962626131326432383234313932396436623862646261365f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023324e354c6a4d6255764a4c547a3653444b556477433252333273384e5143456e3258380000000000000000000000000000000000000000000000000000000000",
logIndex: 1,
removed: false,
topics: ["0xe7fe72e51b458dcd29475a3be9675669af7aa5c3d7e9161fdb6cbba71803dd50", "0x00000000000000000000000022b0588fc66a4bd4e90d6b456a0a774e403b1068"],
transactionHash: "0xcd5362e18dc85b339cd625bf8ef79760684169f1bcdab302f16bee0c753a731e",
transactionIndex: 0
}, {
address: "0xe35cd82bdb5b39973254d4e75b49715c2ff2af90",
blockHash: "0x3575b2f910bc812a37d0adf77b999a269832ba59bb25cc78c58c4a0f948b2481",
blockNumber: 12751,
data: "0x00000000000000000000000000000000000000000000000579a814e10a740000",
logIndex: 2,
removed: false,
topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000022b0588fc66a4bd4e90d6b456a0a774e403b1068", "0x0000000000000000000000000000000000000000000000000000000000000000"],
transactionHash: "0xcd5362e18dc85b339cd625bf8ef79760684169f1bcdab302f16bee0c753a731e",
transactionIndex: 0
}],
logsBloom: "0x00000000084000000000000000000000000000000000000000000000000000000022040000000000000000000000000020000000000000000000000000000000000000000000000000000008000000000000000000000000000020000000000000000000020000000000000000000800000000000000000000000098000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000010000000000000800000000000000000000000000002000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000",
status: "0x1",
to: "0xe35cd82bdb5b39973254d4e75b49715c2ff2af90",
transactionHash: "0xcd5362e18dc85b339cd625bf8ef79760684169f1bcdab302f16bee0c753a731e",
transactionIndex: 0
}

以上结构中,包含了Block TX等相关信息,不是我们关注的重点。我们主要看这几个参数:

  1. status。 这个字段标明了整个TX的执行结果,如果非0,则表示TX执行成功。
  2. to。 这个字段在合约交易中表明了被调用的Conatract Address。至于contractAddress字段,则是在TX创建新的合约时,返回新合约的地址,如无创建合约的行为,则值为null。 这两个字段不要混淆。
  3. logs。 这个数组是我们这次关注的重点,这里包含了交易发出的所有的Event信息。

Log字段解析

每个Log对应一个Event的执行,每个Log中都包含以下信息:

  • address。 发送此EventContract
  • blockHash/blockNumber。对应执行所在的block
  • transactionHash/transactionIndex。 对应执行所在的TX
  • logIndex。此条Log在整个block中的位置
  • topics。 这个是我们这次讲解的重点之一,这个字段包含一个数组,第一个字段是Event的签名信息,后边的是indexed的参数信息
  • data。 这个是剩余的非indexed参数的信息

Topic

Event签名

Topic数组中第一个元素是对应的Event的签名信息。比如对于最常见的Transfer(indexed address from, indexed address to, uint256 amount),其函数签名是e7fe72e51b458dcd29475a3be9675669af7aa5c3d7e9161fdb6cbba71803dd50。大家在捕捉Event信息的时候,可以通过这个字段进行过滤,快速找到自己关心的Event
他的计算方法如下:

1
ByteUtil.toHexString(HashUtil.sha3("Transfer(address,address,uint256)".getBytes()))

indexed参数

Topic中除了签名,剩下的就都是Event中的indexed参数了,以Transfer为例,剩下的就应该是fromto地址信息。这里的信息可以通过web3进行解析,实际就是先转为byte数组,然后根据参数类型,转为对应的值。但是这里也有其他情况,稍后我们重点讲解。

Data

Event中没有被装入Topic的参数,则会统一放置在data字段中,通过对dataDecode,可以获取到其中的值。

特殊情况

这里要说的是几种特殊情况:

  1. 不是所有的Topic中的值都可以被Decode成原始的Input的。因为某些数据类型比如stringbytes长度是不固定的,但是Topic的长度是固定的,所以在生成Topic的时候,会对原始数据做一个sha3,这个尤其需要注意。如果希望后期可以将参数decode出来的话,可以将数据放到非indexed的参数里,最后会直接编码成二进制放到data中。
  2. 不是所有的Topic第一个都是Event签名,比如匿名Event,这里也需要注意。