Skip to content

Commit

Permalink
Revert "core/types: remove support for legacy receipt/log storage enc…
Browse files Browse the repository at this point in the history
…oding (ethereum#22852)"

This reverts commit 643fd0e.
  • Loading branch information
rjl493456442 committed Jul 22, 2021
1 parent d4a2c2e commit 02f3cdf
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 7 deletions.
39 changes: 35 additions & 4 deletions core/types/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ type rlpLog struct {
// rlpStorageLog is the storage encoding of a log.
type rlpStorageLog rlpLog

// legacyRlpStorageLog is the previous storage encoding of a log including some redundant fields.
type legacyRlpStorageLog struct {
Address common.Address
Topics []common.Hash
Data []byte
BlockNumber uint64
TxHash common.Hash
TxIndex uint
BlockHash common.Hash
Index uint
}

// EncodeRLP implements rlp.Encoder.
func (l *Log) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
Expand Down Expand Up @@ -103,10 +115,29 @@ func (l *LogForStorage) EncodeRLP(w io.Writer) error {
//
// Note some redundant fields(e.g. block number, tx hash etc) will be assembled later.
func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
var dec rlpStorageLog
if err := s.Decode(&dec); err != nil {
blob, err := s.Raw()
if err != nil {
return err
}
*l = LogForStorage{Address: dec.Address, Topics: dec.Topics, Data: dec.Data}
return nil
var dec rlpStorageLog
err = rlp.DecodeBytes(blob, &dec)
if err == nil {
*l = LogForStorage{
Address: dec.Address,
Topics: dec.Topics,
Data: dec.Data,
}
} else {
// Try to decode log with previous definition.
var dec legacyRlpStorageLog
err = rlp.DecodeBytes(blob, &dec)
if err == nil {
*l = LogForStorage{
Address: dec.Address,
Topics: dec.Topics,
Data: dec.Data,
}
}
}
return err
}
89 changes: 86 additions & 3 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,27 @@ type storedReceiptRLP struct {
Logs []*LogForStorage
}

// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
type v4StoredReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
TxHash common.Hash
ContractAddress common.Address
Logs []*LogForStorage
GasUsed uint64
}

// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields.
type v3StoredReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
Bloom Bloom
TxHash common.Hash
ContractAddress common.Address
Logs []*LogForStorage
GasUsed uint64
}

// 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 {
Expand Down Expand Up @@ -212,7 +233,8 @@ func (r *Receipt) Size() common.StorageSize {
// entire content of a receipt, as opposed to only the consensus fields originally.
type ReceiptForStorage Receipt

// EncodeRLP implements rlp.Encoder.
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
// into an RLP stream.
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
enc := &storedReceiptRLP{
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
Expand All @@ -225,21 +247,82 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, enc)
}

// DecodeRLP implements rlp.Decoder.
// 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 {
// Retrieve the entire receipt blob as we need to try multiple decoders
blob, err := s.Raw()
if err != nil {
return err
}
// Try decoding from the newest format for future proofness, then the older one
// for old nodes that just upgraded. V4 was an intermediate unreleased format so
// we do need to decode it, but it's not common (try last).
if err := decodeStoredReceiptRLP(r, blob); err == nil {
return nil
}
if err := decodeV3StoredReceiptRLP(r, blob); err == nil {
return nil
}
return decodeV4StoredReceiptRLP(r, blob)
}

func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
var stored storedReceiptRLP
if err := s.Decode(&stored); err != nil {
if err := rlp.DecodeBytes(blob, &stored); err != nil {
return err
}
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
return err
}
r.CumulativeGasUsed = stored.CumulativeGasUsed
r.Logs = make([]*Log, len(stored.Logs))
for i, log := range stored.Logs {
r.Logs[i] = (*Log)(log)
}
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})

return nil
}

func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
var stored v4StoredReceiptRLP
if err := rlp.DecodeBytes(blob, &stored); err != nil {
return err
}
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
return err
}
r.CumulativeGasUsed = stored.CumulativeGasUsed
r.TxHash = stored.TxHash
r.ContractAddress = stored.ContractAddress
r.GasUsed = stored.GasUsed
r.Logs = make([]*Log, len(stored.Logs))
for i, log := range stored.Logs {
r.Logs[i] = (*Log)(log)
}
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})

return nil
}

func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
var stored v3StoredReceiptRLP
if err := rlp.DecodeBytes(blob, &stored); err != nil {
return err
}
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
return err
}
r.CumulativeGasUsed = stored.CumulativeGasUsed
r.Bloom = stored.Bloom
r.TxHash = stored.TxHash
r.ContractAddress = stored.ContractAddress
r.GasUsed = stored.GasUsed
r.Logs = make([]*Log, len(stored.Logs))
for i, log := range stored.Logs {
r.Logs[i] = (*Log)(log)
}
return nil
}

Expand Down
123 changes: 123 additions & 0 deletions core/types/receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"math"
"math/big"
"reflect"
"testing"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -37,6 +38,128 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) {
}
}

func TestLegacyReceiptDecoding(t *testing.T) {
tests := []struct {
name string
encode func(*Receipt) ([]byte, error)
}{
{
"StoredReceiptRLP",
encodeAsStoredReceiptRLP,
},
{
"V4StoredReceiptRLP",
encodeAsV4StoredReceiptRLP,
},
{
"V3StoredReceiptRLP",
encodeAsV3StoredReceiptRLP,
},
}

tx := NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
receipt := &Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
{
Address: common.BytesToAddress([]byte{0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
{
Address: common.BytesToAddress([]byte{0x01, 0x11}),
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
Data: []byte{0x01, 0x00, 0xff},
},
},
TxHash: tx.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
GasUsed: 111111,
}
receipt.Bloom = CreateBloom(Receipts{receipt})

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
enc, err := tc.encode(receipt)
if err != nil {
t.Fatalf("Error encoding receipt: %v", err)
}
var dec ReceiptForStorage
if err := rlp.DecodeBytes(enc, &dec); err != nil {
t.Fatalf("Error decoding RLP receipt: %v", err)
}
// Check whether all consensus fields are correct.
if dec.Status != receipt.Status {
t.Fatalf("Receipt status mismatch, want %v, have %v", receipt.Status, dec.Status)
}
if dec.CumulativeGasUsed != receipt.CumulativeGasUsed {
t.Fatalf("Receipt CumulativeGasUsed mismatch, want %v, have %v", receipt.CumulativeGasUsed, dec.CumulativeGasUsed)
}
if dec.Bloom != receipt.Bloom {
t.Fatalf("Bloom data mismatch, want %v, have %v", receipt.Bloom, dec.Bloom)
}
if len(dec.Logs) != len(receipt.Logs) {
t.Fatalf("Receipt log number mismatch, want %v, have %v", len(receipt.Logs), len(dec.Logs))
}
for i := 0; i < len(dec.Logs); i++ {
if dec.Logs[i].Address != receipt.Logs[i].Address {
t.Fatalf("Receipt log %d address mismatch, want %v, have %v", i, receipt.Logs[i].Address, dec.Logs[i].Address)
}
if !reflect.DeepEqual(dec.Logs[i].Topics, receipt.Logs[i].Topics) {
t.Fatalf("Receipt log %d topics mismatch, want %v, have %v", i, receipt.Logs[i].Topics, dec.Logs[i].Topics)
}
if !bytes.Equal(dec.Logs[i].Data, receipt.Logs[i].Data) {
t.Fatalf("Receipt log %d data mismatch, want %v, have %v", i, receipt.Logs[i].Data, dec.Logs[i].Data)
}
}
})
}
}

func encodeAsStoredReceiptRLP(want *Receipt) ([]byte, error) {
stored := &storedReceiptRLP{
PostStateOrStatus: want.statusEncoding(),
CumulativeGasUsed: want.CumulativeGasUsed,
Logs: make([]*LogForStorage, len(want.Logs)),
}
for i, log := range want.Logs {
stored.Logs[i] = (*LogForStorage)(log)
}
return rlp.EncodeToBytes(stored)
}

func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) {
stored := &v4StoredReceiptRLP{
PostStateOrStatus: want.statusEncoding(),
CumulativeGasUsed: want.CumulativeGasUsed,
TxHash: want.TxHash,
ContractAddress: want.ContractAddress,
Logs: make([]*LogForStorage, len(want.Logs)),
GasUsed: want.GasUsed,
}
for i, log := range want.Logs {
stored.Logs[i] = (*LogForStorage)(log)
}
return rlp.EncodeToBytes(stored)
}

func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
stored := &v3StoredReceiptRLP{
PostStateOrStatus: want.statusEncoding(),
CumulativeGasUsed: want.CumulativeGasUsed,
Bloom: want.Bloom,
TxHash: want.TxHash,
ContractAddress: want.ContractAddress,
Logs: make([]*LogForStorage, len(want.Logs)),
GasUsed: want.GasUsed,
}
for i, log := range want.Logs {
stored.Logs[i] = (*LogForStorage)(log)
}
return rlp.EncodeToBytes(stored)
}

// Tests that receipt data can be correctly derived from the contextual infos
func TestDeriveFields(t *testing.T) {
// Create a few transactions to have receipts for
Expand Down

0 comments on commit 02f3cdf

Please sign in to comment.