// 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"`//交易类型 PostState []byte`json:"root"`//交易成功/失败时的 RLP 编码 Status uint64`json:"status"`//交易成功/失败的状态码 //区块中直到这一笔交易累积使用的 gas CumulativeGasUsed uint64`json:"cumulativeGasUsed" gencodec:"required"` //布隆过滤q Bloom Bloom `json:"logsBloom" gencodec:"required"` //合约的日志列表 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"` ContractAddress common.Address `json:"contractAddress"` GasUsed uint64`json:"gasUsed" gencodec:"required"`
//记录区块信息和交易索引,用于叫检查交易与对应收据的兼容性 // Inclusion information: These fields provide information about the inclusion of the // transaction corresponding to this receipt. BlockHash common.Hash `json:"blockHash,omitempty"` BlockNumber *big.Int `json:"blockNumber,omitempty"` TransactionIndex uint`json:"transactionIndex"` }
收据的部分字段需要编码后永久存储,因此提供了编码时的中间结构和永久存储的结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
//收据 RLP 编码时的中间结构
// receiptRLP is the consensus encoding of a receipt. type receiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Bloom Bloom Logs []*Log }
//将会永久存储的部分
// storedReceiptRLP is the storage encoding of a receipt. type storedReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Logs []*LogForStorage }
// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4. type v4StoredReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 TxHash common.Hash ContractAddress common.Address Logs []*LogForStorage GasUsed uint64 }
// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields. type v3StoredReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Bloom Bloom TxHash common.Hash ContractAddress common.Address Logs []*LogForStorage GasUsed uint64 }
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt // into an RLP stream. If no post state is present, byzantium fork is assumed. func(r *Receipt) EncodeRLP(w io.Writer) error { data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} if r.Type == LegacyTxType { return rlp.Encode(w, data) } buf := encodeBufferPool.Get().(*bytes.Buffer) defer encodeBufferPool.Put(buf) buf.Reset() if err := r.encodeTyped(data, buf); err != nil { return err } return rlp.Encode(w, buf.Bytes()) }
// encodeTyped writes the canonical encoding of a typed receipt to w. func(r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error { w.WriteByte(r.Type) return rlp.Encode(w, data) }
// MarshalBinary returns the consensus encoding of the receipt. func(r *Receipt) MarshalBinary() ([]byte, error) { if r.Type == LegacyTxType { return rlp.EncodeToBytes(r) } data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} var buf bytes.Buffer err := r.encodeTyped(data, &buf) return buf.Bytes(), err }
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt // from an RLP stream. func(r *Receipt) DecodeRLP(s *rlp.Stream) error { kind, _, err := s.Kind() switch { case err != nil: return err case kind == rlp.List: // It's a legacy receipt. var dec receiptRLP if err := s.Decode(&dec); err != nil { return err } r.Type = LegacyTxType return r.setFromRLP(dec) case kind == rlp.String: // It's an EIP-2718 typed tx receipt. b, err := s.Bytes() if err != nil { return err } iflen(b) == 0 { return errEmptyTypedReceipt } r.Type = b[0] if r.Type == AccessListTxType || r.Type == DynamicFeeTxType { var dec receiptRLP if err := rlp.DecodeBytes(b[1:], &dec); err != nil { return err } return r.setFromRLP(dec) } return ErrTxTypeNotSupported default: return rlp.ErrExpectedList } }
// UnmarshalBinary decodes the consensus encoding of receipts. // It supports legacy RLP receipts and EIP-2718 typed receipts. func(r *Receipt) UnmarshalBinary(b []byte) error { iflen(b) > 0 && b[0] > 0x7f { // It's a legacy receipt decode the RLP var data receiptRLP err := rlp.DecodeBytes(b, &data) if err != nil { return err } r.Type = LegacyTxType return r.setFromRLP(data) } // It's an EIP2718 typed transaction envelope. return r.decodeTyped(b) }
// decodeTyped decodes a typed receipt from the canonical format. func(r *Receipt) decodeTyped(b []byte) error { iflen(b) == 0 { return errEmptyTypedReceipt } switch b[0] { case DynamicFeeTxType, AccessListTxType: var data receiptRLP err := rlp.DecodeBytes(b[1:], &data) if err != nil { return err } r.Type = b[0] return r.setFromRLP(data) default: return ErrTxTypeNotSupported } }
// ReceiptForStorage is a wrapper around a Receipt that flattens and parses the // entire content of a receipt, as opposed to only the consensus fields originally. type ReceiptForStorage Receipt
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt // into an RLP stream. func(r *ReceiptForStorage) EncodeRLP(w io.Writer) error { enc := &storedReceiptRLP{ PostStateOrStatus: (*Receipt)(r).statusEncoding(), CumulativeGasUsed: r.CumulativeGasUsed, Logs: make([]*LogForStorage, len(r.Logs)), } for i, log := range r.Logs { enc.Logs[i] = (*LogForStorage)(log) } return rlp.Encode(w, enc) }
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation // fields of a receipt from an RLP stream. func(r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { // Retrieve the entire receipt blob as we need to try multiple decoders blob, err := s.Raw() if err != nil { return err } // Try decoding from the newest format for future proofness, then the older one // for old nodes that just upgraded. V4 was an intermediate unreleased format so // we do need to decode it, but it's not common (try last). if err := decodeStoredReceiptRLP(r, blob); err == nil { returnnil } if err := decodeV3StoredReceiptRLP(r, blob); err == nil { returnnil } return decodeV4StoredReceiptRLP(r, blob) }
funcdecodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte)error { var stored storedReceiptRLP if err := rlp.DecodeBytes(blob, &stored); err != nil { return err } if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil { return err } r.CumulativeGasUsed = stored.CumulativeGasUsed r.Logs = make([]*Log, len(stored.Logs)) for i, log := range stored.Logs { r.Logs[i] = (*Log)(log) } r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
// Len returns the number of receipts in this list. func(rs Receipts) Len() int { returnlen(rs) }
// EncodeIndex encodes the i'th receipt to w. func(rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { r := rs[i] data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} switch r.Type { case LegacyTxType: rlp.Encode(w, data) case AccessListTxType: w.WriteByte(AccessListTxType) rlp.Encode(w, data) case DynamicFeeTxType: w.WriteByte(DynamicFeeTxType) rlp.Encode(w, data) default: // For unsupported types, write nothing. Since this is for // DeriveSha, the error will be caught matching the derived hash // to the block. } }
// DeriveFields fills the receipts with their computed fields based on consensus // data and contextual infos like containing block and transactions. func(rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error { signer := MakeSigner(config, new(big.Int).SetUint64(number))
logIndex := uint(0) iflen(txs) != len(rs) { return errors.New("transaction and receipt count mismatch") } for i := 0; i < len(rs); i++ { // The transaction type and hash can be retrieved from the transaction itself rs[i].Type = txs[i].Type() rs[i].TxHash = txs[i].Hash()
// The contract address can be derived from the transaction itself if txs[i].To() == nil { // Deriving the signer is expensive, only do if it's actually needed from, _ := Sender(signer, txs[i]) rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) } // The used gas can be calculated based on previous r if i == 0 { rs[i].GasUsed = rs[i].CumulativeGasUsed } else { rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed } // The derived log fields can simply be set from the block and transaction for j := 0; j < len(rs[i].Logs); j++ { rs[i].Logs[j].BlockNumber = number rs[i].Logs[j].BlockHash = hash rs[i].Logs[j].TxHash = rs[i].TxHash rs[i].Logs[j].TxIndex = uint(i) rs[i].Logs[j].Index = logIndex logIndex++ } } returnnil }