Browse Source

Merge pull request #15033 from fjl/core-receipt-status-bytes

core/types: encode receipt status in PostState field
Péter Szilágyi 8 years ago
parent
commit
8596fc5974
2 changed files with 62 additions and 83 deletions
  1. 2 2
      core/database_util_test.go
  2. 60 81
      core/types/receipt.go

+ 2 - 2
core/database_util_test.go

@@ -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})},

+ 60 - 81
core/types/receipt.go

@@ -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.
-	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
+	r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.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:
-		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.
@@ -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))
+	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 {
-		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
 // 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
 }