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.
This commit is contained in:
Felix Lange 2017-08-24 15:17:02 +02:00
parent 68955ed2eb
commit ad16aeb0a2
2 changed files with 63 additions and 84 deletions

View File

@ -340,7 +340,7 @@ func TestBlockReceiptStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
receipt1 := &types.Receipt{
PostState: []byte{0x01},
Failed: true,
CumulativeGasUsed: big.NewInt(1),
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x11})},
@ -351,7 +351,7 @@ func TestBlockReceiptStorage(t *testing.T) {
GasUsed: big.NewInt(111111),
}
receipt2 := &types.Receipt{
PostState: []byte{0x02},
PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: big.NewInt(2),
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x22})},

View File

@ -17,6 +17,7 @@
package types
import (
"bytes"
"fmt"
"io"
"math/big"
@ -28,9 +29,9 @@ import (
//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
const (
receiptStatusSuccessful = byte(0x01)
receiptStatusFailed = byte(0x00)
var (
receiptStatusFailed = []byte{}
receiptStatusSuccessful = []byte{0x01}
)
// Receipt represents the results of a transaction.
@ -54,22 +55,22 @@ type receiptMarshaling struct {
GasUsed *hexutil.Big
}
// homesteadReceiptRLP contains the receipt's Homestead consensus fields, used
// during RLP serialization.
type homesteadReceiptRLP struct {
PostState []byte
// receiptRLP is the consensus encoding of a receipt.
type receiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed *big.Int
Bloom Bloom
Logs []*Log
}
// metropolisReceiptRLP contains the receipt's Metropolis consensus fields, used
// during RLP serialization.
type metropolisReceiptRLP struct {
Status byte
type receiptStorageRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed *big.Int
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.
@ -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
// into an RLP stream. If no post state is present, metropolis fork is assumed.
func (r *Receipt) EncodeRLP(w io.Writer) error {
if r.PostState == nil {
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})
return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs})
}
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
// Load the raw bytes since we have multiple possible formats
raw, err := s.Raw()
if err != nil {
var dec receiptRLP
if err := s.Decode(&dec); err != nil {
return err
}
content, _, err := rlp.SplitList(raw)
if err != nil {
if err := r.setStatus(dec.PostStateOrStatus); err != nil {
return err
}
kind, cnt, _, err := rlp.Split(content)
if err != nil {
return err
}
// Deserialize based on the first component type.
r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs
return nil
}
func (r *Receipt) setStatus(postStateOrStatus []byte) error {
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:
case bytes.Equal(postStateOrStatus, receiptStatusSuccessful):
r.Failed = false
case receiptStatusFailed:
case bytes.Equal(postStateOrStatus, receiptStatusFailed):
r.Failed = true
case len(postStateOrStatus) == len(common.Hash{}):
r.PostState = postStateOrStatus
default:
return fmt.Errorf("invalid status byte: 0x%x", metro.Status)
return fmt.Errorf("invalid receipt status %x", postStateOrStatus)
}
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
func (r *Receipt) statusEncoding() []byte {
if len(r.PostState) == 0 {
if r.Failed {
return receiptStatusFailed
} else {
return receiptStatusSuccessful
}
r.PostState = home.PostState[:]
r.CumulativeGasUsed = home.CumulativeGasUsed
r.Bloom = home.Bloom
r.Logs = home.Logs
return nil
default:
return fmt.Errorf("invalid first receipt component: %v", kind)
}
return r.PostState
}
// String implements the Stringer interface.
@ -160,38 +138,39 @@ 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 {
logs := make([]*LogForStorage, len(r.Logs))
for i, log := range r.Logs {
logs[i] = (*LogForStorage)(log)
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,
}
return rlp.Encode(w, []interface{}{r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom, r.TxHash, r.ContractAddress, logs, r.GasUsed})
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 {
var receipt struct {
PostState []byte
Failed bool
CumulativeGasUsed *big.Int
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
Logs []*LogForStorage
GasUsed *big.Int
var dec receiptStorageRLP
if err := s.Decode(&dec); err != nil {
return err
}
if err := s.Decode(&receipt); err != nil {
if err := (*Receipt)(r).setStatus(dec.PostStateOrStatus); err != nil {
return err
}
// Assign the consensus fields
r.PostState, r.Failed, r.CumulativeGasUsed, r.Bloom = receipt.PostState, receipt.Failed, receipt.CumulativeGasUsed, receipt.Bloom
r.Logs = make([]*Log, len(receipt.Logs))
for i, log := range receipt.Logs {
r.CumulativeGasUsed, r.Bloom = dec.CumulativeGasUsed, dec.Bloom
r.Logs = make([]*Log, len(dec.Logs))
for i, log := range dec.Logs {
r.Logs[i] = (*Log)(log)
}
// 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
}