在ETH合约执行过程中,为了让外界感知某些执行的结果,通常会选择emit Event
。这其中ERC20
系列的Token
最为典型。通常在Token
转账过程中会发送一个Transfer
Event来告知使用者交易的结果。其他人通过Web3
监听事件,或者通过获取交易的Receipt
中的Log
来获取交易结果。今天我们主要讲解的是Receipt
中Log
的构成及解析。
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
等相关信息,不是我们关注的重点。我们主要看这几个参数:
status
。 这个字段标明了整个TX
的执行结果,如果非0,则表示TX
执行成功。
to
。 这个字段在合约交易中表明了被调用的Conatract Address
。至于contractAddress
字段,则是在TX
创建新的合约时,返回新合约的地址,如无创建合约的行为,则值为null。 这两个字段不要混淆。
logs
。 这个数组是我们这次关注的重点,这里包含了交易发出的所有的Event
信息。
Log字段解析
每个Log
对应一个Event的执行,每个Log
中都包含以下信息:
address
。 发送此Event
的Contract
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
为例,剩下的就应该是from
和to
地址信息。这里的信息可以通过web3
进行解析,实际就是先转为byte
数组,然后根据参数类型,转为对应的值。但是这里也有其他情况,稍后我们重点讲解。
Data
Event
中没有被装入Topic
的参数,则会统一放置在data
字段中,通过对data
的Decode
,可以获取到其中的值。
特殊情况
这里要说的是几种特殊情况:
- 不是所有的
Topic
中的值都可以被Decode
成原始的Input
的。因为某些数据类型比如string
、bytes
长度是不固定的,但是Topic
的长度是固定的,所以在生成Topic
的时候,会对原始数据做一个sha3
,这个尤其需要注意。如果希望后期可以将参数decode
出来的话,可以将数据放到非indexed
的参数里,最后会直接编码成二进制放到data
中。
- 不是所有的
Topic
第一个都是Event
签名,比如匿名Event
,这里也需要注意。