// Copyright 2014 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package types import ( "bytes" "errors" "fmt" "io" "math/big" "unsafe" "github.com/openrelayxyz/plugeth-utils/core" "github.com/openrelayxyz/plugeth-utils/restricted/hexutil" "github.com/openrelayxyz/plugeth-utils/restricted/crypto" "github.com/openrelayxyz/plugeth-utils/restricted/params" "github.com/openrelayxyz/plugeth-utils/restricted/rlp" ) //go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go var ( receiptStatusFailedRLP = []byte{} receiptStatusSuccessfulRLP = []byte{0x01} ) // This error is returned when a typed receipt is decoded, but the string is empty. var errEmptyTypedReceipt = errors.New("empty typed receipt bytes") const ( // ReceiptStatusFailed is the status code of a transaction if execution failed. ReceiptStatusFailed = uint64(0) // ReceiptStatusSuccessful is the status code of a transaction if execution succeeded. ReceiptStatusSuccessful = uint64(1) ) // 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"` Status uint64 `json:"status"` CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` 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 core.Hash `json:"transactionHash" gencodec:"required"` ContractAddress core.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 core.Hash `json:"blockHash,omitempty"` BlockNumber *big.Int `json:"blockNumber,omitempty"` TransactionIndex uint `json:"transactionIndex"` } type receiptMarshaling struct { Type hexutil.Uint64 PostState hexutil.Bytes Status hexutil.Uint64 CumulativeGasUsed hexutil.Uint64 GasUsed hexutil.Uint64 BlockNumber *hexutil.Big TransactionIndex hexutil.Uint } // 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 []*Log } // NewReceipt creates a barebone transaction receipt, copying the init fields. // Deprecated: create receipts using a struct literal instead. func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { r := &Receipt{ Type: LegacyTxType, PostState: core.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed, } if failed { r.Status = ReceiptStatusFailed } else { r.Status = ReceiptStatusSuccessful } return r } // 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() buf.WriteByte(r.Type) if err := rlp.Encode(buf, data); err != nil { return err } return rlp.Encode(w, buf.Bytes()) } // 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 } if len(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 } } func (r *Receipt) setFromRLP(data receiptRLP) error { r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs return r.setStatus(data.PostStateOrStatus) } func (r *Receipt) setStatus(postStateOrStatus []byte) error { switch { case bytes.Equal(postStateOrStatus, receiptStatusSuccessfulRLP): r.Status = ReceiptStatusSuccessful case bytes.Equal(postStateOrStatus, receiptStatusFailedRLP): r.Status = ReceiptStatusFailed case len(postStateOrStatus) == len(core.Hash{}): r.PostState = postStateOrStatus default: return fmt.Errorf("invalid receipt status %x", postStateOrStatus) } return nil } func (r *Receipt) statusEncoding() []byte { if len(r.PostState) == 0 { if r.Status == ReceiptStatusFailed { return receiptStatusFailedRLP } return receiptStatusSuccessfulRLP } return r.PostState } // Size returns the approximate memory used by all internal contents. It is used // to approximate and limit the memory consumption of various caches. func (r *Receipt) Size() float64 { size := float64(unsafe.Sizeof(*r)) + float64(len(r.PostState)) size += float64(len(r.Logs)) * float64(unsafe.Sizeof(Log{})) for _, log := range r.Logs { size += float64(len(log.Topics)*32 + len(log.Data)) } return size } // 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. func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { enc := &storedReceiptRLP{ PostStateOrStatus: (*Receipt)(r).statusEncoding(), CumulativeGasUsed: r.CumulativeGasUsed, Logs: r.Logs, } return rlp.Encode(w, enc) } // DecodeRLP implements rlp.Decoder. func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { var stored storedReceiptRLP if err := s.Decode(&stored); err != nil { return err } if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil { return err } r.CumulativeGasUsed = stored.CumulativeGasUsed r.Logs = stored.Logs r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) return nil } // Receipts implements DerivableList for receipts. type Receipts []*Receipt // Len returns the number of receipts in this list. func (rs Receipts) Len() int { return len(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 (r Receipts) DeriveFields(config *params.ChainConfig, hash core.Hash, number uint64, txs Transactions) error { signer := MakeSigner(config, new(big.Int).SetUint64(number)) logIndex := uint(0) if len(txs) != len(r) { return errors.New("transaction and receipt count mismatch") } for i := 0; i < len(r); i++ { // The transaction type and hash can be retrieved from the transaction itself r[i].Type = txs[i].Type() r[i].TxHash = txs[i].Hash() // block location fields r[i].BlockHash = hash r[i].BlockNumber = new(big.Int).SetUint64(number) r[i].TransactionIndex = uint(i) // 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]) r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) } // The used gas can be calculated based on previous r if i == 0 { r[i].GasUsed = r[i].CumulativeGasUsed } else { r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed } // The derived log fields can simply be set from the block and transaction for j := 0; j < len(r[i].Logs); j++ { r[i].Logs[j].BlockNumber = number r[i].Logs[j].BlockHash = hash r[i].Logs[j].TxHash = r[i].TxHash r[i].Logs[j].TxIndex = uint(i) r[i].Logs[j].Index = logIndex logIndex++ } } return nil }