理解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
}
comments powered by Disqus