理解Geth--交易回执
交易回执结构
receipt是交易执行的回执,交易的执行结果都包括在receipt结构中:
// Receipt represents the results of a transaction.
type Receipt struct {
// Consensus fields: These fields are defined by the Yellow Paper
Type uint8 `json:"type,omitempty"` // 交易类型:与transaction的类型0 1 2对应
PostState []byte `json:"root"` // 此交易执行后的账户状态树根哈希值
Status uint64 `json:"status"` // 0失败 1成功
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` // 截至此交易,本区块内累计gas消耗
Bloom Bloom `json:"logsBloom" gencodec:"required"` // bloom过滤器
Logs []*Log `json:"logs" gencodec:"required"` // 交易产生的日志列表
// Implementation fields: These fields are added by geth when processing a transaction.
// They are stored in the chain database.
TxHash common.Hash `json:"transactionHash" gencodec:"required"` // 交易hash
ContractAddress common.Address `json:"contractAddress"` // 交易发送的合约地址
GasUsed uint64 `json:"gasUsed" gencodec:"required"` // 交易消耗的gas
EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // required, but tag omitted for backwards compatibility
BlobGasUsed uint64 `json:"blobGasUsed,omitempty"`
BlobGasPrice *big.Int `json:"blobGasPrice,omitempty"`
// Inclusion information: These fields provide information about the inclusion of the
// transaction corresponding to this receipt.
BlockHash common.Hash `json:"blockHash,omitempty"` // 交易所属区块hash
BlockNumber *big.Int `json:"blockNumber,omitempty"` // 交易所属区块高度
TransactionIndex uint `json:"transactionIndex"` // 交易在区块内的次序
}
通过注释就能看出分为三部分:共识信息,交易信息,区块信息
Receipt的存储
具体receipt中的哪些内容会存储在区块链上由另一个结构体 ReceiptForStorage 来定义:
写入数据库的实现代码:这里hash和number都是指区块信息,也就是把一个区块内的所有receipts聚合写入
func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts types.Receipts) {
// Convert the receipts into their storage form and serialize them
storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
for i, receipt := range receipts {
storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
}
bytes, err := rlp.EncodeToBytes(storageReceipts)
if err != nil {
log.Crit("Failed to encode block receipts", "err", err)
}
// Store the flattened receipt slice
if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil {
log.Crit("Failed to store block receipts", "err", err)
}
}
通过ReceiptForStorage的编码函数,可以看到只记录了receipt的Status,CumulativeGasUsed,Logs字段
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
// into an RLP stream.
func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
w := rlp.NewEncoderBuffer(_w)
outerList := w.List()
w.WriteBytes((*Receipt)(r).statusEncoding())
w.WriteUint64(r.CumulativeGasUsed)
logList := w.List()
for _, log := range r.Logs {
if err := rlp.Encode(w, log); err != nil {
return err
}
}
w.ListEnd(logList)
w.ListEnd(outerList)
return w.Flush()
}
receipt中的其他数据存储在其他部分,当根据tx-hash来查询receipt时,节点会从多处聚合查询结果来返回前面看到的receipt结构:
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
// 先根据hash查出tx及block信息
found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
if err != nil {
return nil, NewTxIndexingError() // transaction is not fully indexed
}
// 查询区块头信息
header, err := s.b.HeaderByHash(ctx, blockHash)
if err != nil {
return nil, err
}
// 根据blockhash查询此区块内的所有receipts
receipts, err := s.b.GetReceipts(ctx, blockHash)
if err != nil {
return nil, err
}
if len(receipts) <= int(index) {
return nil, nil
}
// 根据tx在block中的索引找到对应的receipt
receipt := receipts[index]
// Derive the sender. 构建并解析到receipt结构中
signer := types.MakeSigner(s.b.ChainConfig(), header.Number, header.Time)
return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil
}