// 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/crypto" "github.com/openrelayxyz/plugeth-utils/restricted/hexutil" "github.com/openrelayxyz/plugeth-utils/restricted/params" "github.com/openrelayxyz/plugeth-utils/restricted/rlp" ) //go:generate go run github.com/fjl/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. TxHash core.Hash `json:"transactionHash" gencodec:"required"` ContractAddress core.Address `json:"contractAddress"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // 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. // For a legacy Receipt this returns RLP([PostStateOrStatus, CumulativeGasUsed, Bloom, Logs]) // For a EIP-2718 Receipt this returns RLP(TxType || ReceiptPayload) // For a EIP-2930 Receipt, TxType == 0x01 and ReceiptPayload == RLP([PostStateOrStatus, CumulativeGasUsed, Bloom, Logs]) 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) default: // It's an EIP-2718 typed tx receipt. b, err := s.Bytes() if err != nil { return err } return r.decodeTyped(b) } } // UnmarshalBinary decodes the consensus encoding of receipts. // It supports legacy RLP receipts and EIP-2718 typed receipts. func (r *Receipt) UnmarshalBinary(b []byte) error { if len(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 { if len(b) <= 1 { 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 } } 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 with RLP serialization // that omits the Bloom field and deserialization that re-computes it. 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, and loads both consensus and implementation // fields of a receipt from an RLP stream. 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 (rs Receipts) DeriveFields(config *params.ChainConfig, hash core.Hash, number uint64, baseFee *big.Int, txs []*Transaction) error { signer := MakeSigner(config, new(big.Int).SetUint64(number)) logIndex := uint(0) if len(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() rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee) // block location fields rs[i].BlockHash = hash rs[i].BlockNumber = new(big.Int).SetUint64(number) rs[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]) rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) } else { rs[i].ContractAddress = core.Address{} } // 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++ } } return nil }