Commit ad16aeb0 authored by Felix Lange's avatar Felix Lange

core/types: encode receipt status in PostState field

This fixes a regression where the new Failed field in ReceiptForStorage
rejected previously stored receipts. Fix it by removing the new field
and store status in the PostState field. This also removes massive RLP
hackery around the status field.
parent 68955ed2
...@@ -340,7 +340,7 @@ func TestBlockReceiptStorage(t *testing.T) { ...@@ -340,7 +340,7 @@ func TestBlockReceiptStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
receipt1 := &types.Receipt{ receipt1 := &types.Receipt{
PostState: []byte{0x01}, Failed: true,
CumulativeGasUsed: big.NewInt(1), CumulativeGasUsed: big.NewInt(1),
Logs: []*types.Log{ Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x11})}, {Address: common.BytesToAddress([]byte{0x11})},
...@@ -351,7 +351,7 @@ func TestBlockReceiptStorage(t *testing.T) { ...@@ -351,7 +351,7 @@ func TestBlockReceiptStorage(t *testing.T) {
GasUsed: big.NewInt(111111), GasUsed: big.NewInt(111111),
} }
receipt2 := &types.Receipt{ receipt2 := &types.Receipt{
PostState: []byte{0x02}, PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: big.NewInt(2), CumulativeGasUsed: big.NewInt(2),
Logs: []*types.Log{ Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x22})}, {Address: common.BytesToAddress([]byte{0x22})},
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package types package types
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
...@@ -28,9 +29,9 @@ import ( ...@@ -28,9 +29,9 @@ import (
//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go //go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
const ( var (
receiptStatusSuccessful = byte(0x01) receiptStatusFailed = []byte{}
receiptStatusFailed = byte(0x00) receiptStatusSuccessful = []byte{0x01}
) )
// Receipt represents the results of a transaction. // Receipt represents the results of a transaction.
...@@ -54,22 +55,22 @@ type receiptMarshaling struct { ...@@ -54,22 +55,22 @@ type receiptMarshaling struct {
GasUsed *hexutil.Big GasUsed *hexutil.Big
} }
// homesteadReceiptRLP contains the receipt's Homestead consensus fields, used // receiptRLP is the consensus encoding of a receipt.
// during RLP serialization. type receiptRLP struct {
type homesteadReceiptRLP struct { PostStateOrStatus []byte
PostState []byte
CumulativeGasUsed *big.Int CumulativeGasUsed *big.Int
Bloom Bloom Bloom Bloom
Logs []*Log Logs []*Log
} }
// metropolisReceiptRLP contains the receipt's Metropolis consensus fields, used type receiptStorageRLP struct {
// during RLP serialization. PostStateOrStatus []byte
type metropolisReceiptRLP struct {
Status byte
CumulativeGasUsed *big.Int CumulativeGasUsed *big.Int
Bloom Bloom Bloom Bloom
Logs []*Log TxHash common.Hash
ContractAddress common.Address
Logs []*LogForStorage
GasUsed *big.Int
} }
// NewReceipt creates a barebone transaction receipt, copying the init fields. // NewReceipt creates a barebone transaction receipt, copying the init fields.
...@@ -80,69 +81,46 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed *big.Int) *Receipt { ...@@ -80,69 +81,46 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed *big.Int) *Receipt {
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt // EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
// into an RLP stream. If no post state is present, metropolis fork is assumed. // into an RLP stream. If no post state is present, metropolis fork is assumed.
func (r *Receipt) EncodeRLP(w io.Writer) error { func (r *Receipt) EncodeRLP(w io.Writer) error {
if r.PostState == nil { return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs})
status := receiptStatusSuccessful
if r.Failed {
status = receiptStatusFailed
}
return rlp.Encode(w, &metropolisReceiptRLP{status, r.CumulativeGasUsed, r.Bloom, r.Logs})
}
return rlp.Encode(w, &homesteadReceiptRLP{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs})
} }
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt // DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream. // from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error { func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
// Load the raw bytes since we have multiple possible formats var dec receiptRLP
raw, err := s.Raw() if err := s.Decode(&dec); err != nil {
if err != nil {
return err return err
} }
content, _, err := rlp.SplitList(raw) if err := r.setStatus(dec.PostStateOrStatus); err != nil {
if err != nil {
return err return err
} }
kind, cnt, _, err := rlp.Split(content) r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs
if err != nil { return nil
return err }
}
// Deserialize based on the first component type.
switch {
case kind == rlp.Byte || (kind == rlp.String && len(cnt) == 0):
// The first component of metropolis receipts is Byte (0x01), or the empty
// string (0x80, decoded as a byte with 0x00 value).
var metro metropolisReceiptRLP
if err := rlp.DecodeBytes(raw, &metro); err != nil {
return err
}
switch metro.Status {
case receiptStatusSuccessful:
r.Failed = false
case receiptStatusFailed:
r.Failed = true
default:
return fmt.Errorf("invalid status byte: 0x%x", metro.Status)
}
r.CumulativeGasUsed = metro.CumulativeGasUsed
r.Bloom = metro.Bloom
r.Logs = metro.Logs
return nil
case kind == rlp.String:
// The first component of homestead receipts is non-empty String.
var home homesteadReceiptRLP
if err := rlp.DecodeBytes(raw, &home); err != nil {
return err
}
r.PostState = home.PostState[:]
r.CumulativeGasUsed = home.CumulativeGasUsed
r.Bloom = home.Bloom
r.Logs = home.Logs
return nil
func (r *Receipt) setStatus(postStateOrStatus []byte) error {
switch {
case bytes.Equal(postStateOrStatus, receiptStatusSuccessful):
r.Failed = false
case bytes.Equal(postStateOrStatus, receiptStatusFailed):
r.Failed = true
case len(postStateOrStatus) == len(common.Hash{}):
r.PostState = postStateOrStatus
default: default:
return fmt.Errorf("invalid first receipt component: %v", kind) return fmt.Errorf("invalid receipt status %x", postStateOrStatus)
} }
return nil
}
func (r *Receipt) statusEncoding() []byte {
if len(r.PostState) == 0 {
if r.Failed {
return receiptStatusFailed
} else {
return receiptStatusSuccessful
}
}
return r.PostState
} }
// String implements the Stringer interface. // String implements the Stringer interface.
...@@ -160,38 +138,39 @@ type ReceiptForStorage Receipt ...@@ -160,38 +138,39 @@ type ReceiptForStorage Receipt
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt // EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
// into an RLP stream. // into an RLP stream.
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
logs := make([]*LogForStorage, len(r.Logs)) enc := &receiptStorageRLP{
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
CumulativeGasUsed: r.CumulativeGasUsed,
Bloom: r.Bloom,
TxHash: r.TxHash,
ContractAddress: r.ContractAddress,
Logs: make([]*LogForStorage, len(r.Logs)),
GasUsed: r.GasUsed,
}
for i, log := range r.Logs { for i, log := range r.Logs {
logs[i] = (*LogForStorage)(log) enc.Logs[i] = (*LogForStorage)(log)
} }
return rlp.Encode(w, []interface{}{r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed}) return rlp.Encode(w, enc)
} }
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation // DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
// fields of a receipt from an RLP stream. // fields of a receipt from an RLP stream.
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
var receipt struct { var dec receiptStorageRLP
PostState []byte if err := s.Decode(&dec); err != nil {
Failed bool return err
CumulativeGasUsed *big.Int
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
Logs []*LogForStorage
GasUsed *big.Int
} }
if err := s.Decode(&receipt); err != nil { if err := (*Receipt)(r).setStatus(dec.PostStateOrStatus); err != nil {
return err return err
} }
// Assign the consensus fields // Assign the consensus fields
r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.Failed, receipt.CumulativeGasUsed, receipt.Bloom r.CumulativeGasUsed, r.Bloom = dec.CumulativeGasUsed, dec.Bloom
r.Logs = make([]*Log, len(receipt.Logs)) r.Logs = make([]*Log, len(dec.Logs))
for i, log := range receipt.Logs { for i, log := range dec.Logs {
r.Logs[i] = (*Log)(log) r.Logs[i] = (*Log)(log)
} }
// Assign the implementation fields // Assign the implementation fields
r.TxHash, r.ContractAddress, r.GasUsed = receipt.TxHash, receipt.ContractAddress, receipt.GasUsed r.TxHash, r.ContractAddress, r.GasUsed = dec.TxHash, dec.ContractAddress, dec.GasUsed
return nil return nil
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment