From 63ccabc72fc68d94daaf963f3d0e542d4db1969e Mon Sep 17 00:00:00 2001
From: ledgerwatch <akhounov@gmail.com>
Date: Sun, 4 Jul 2021 14:48:13 +0100
Subject: [PATCH] Receipt repair migration (#2291)

* Fixes in rpctest and rpcdaemon for debug_traceTransaction

* Fix for opcode tracer

* Tool to fix receipts

* Better mechanism for detecting broken receipts

* Fixes

* Introduce receipt_repair migration

Co-authored-by: Alexey Sharp <alexeysharp@Alexeys-iMac.local>
---
 cmd/hack/hack.go             | 126 +++++------
 migrations/migrations.go     |   1 +
 migrations/receipt_cbor.go   | 393 -----------------------------------
 migrations/receipt_repair.go | 161 ++++++++++++++
 4 files changed, 226 insertions(+), 455 deletions(-)
 create mode 100644 migrations/receipt_repair.go

diff --git a/cmd/hack/hack.go b/cmd/hack/hack.go
index ee5921ff28..a203afd751 100644
--- a/cmd/hack/hack.go
+++ b/cmd/hack/hack.go
@@ -26,7 +26,6 @@ import (
 	"github.com/ledgerwatch/erigon/core"
 	"github.com/ledgerwatch/erigon/ethdb/cbor"
 	kv2 "github.com/ledgerwatch/erigon/ethdb/kv"
-	"github.com/ledgerwatch/erigon/migrations"
 	"github.com/ledgerwatch/erigon/params"
 	"github.com/wcharczuk/go-chart"
 	"github.com/wcharczuk/go-chart/util"
@@ -2076,6 +2075,13 @@ func scanTxs(chaindata string) error {
 }
 
 func scanReceipts(chaindata string, block uint64) error {
+	f, err := os.Create("fixed.txt")
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	w := bufio.NewWriter(f)
+	defer w.Flush()
 	dbdb := kv2.MustOpen(chaindata).RwKV()
 	defer dbdb.Close()
 	tx, err := dbdb.BeginRw(context.Background())
@@ -2083,6 +2089,14 @@ func scanReceipts(chaindata string, block uint64) error {
 		return err
 	}
 	defer tx.Rollback()
+	if sm, smErr := ethdb.GetStorageModeFromDB(tx); smErr != nil {
+		return smErr
+	} else {
+		if !sm.History {
+			log.Warn("Could not perform this migration because history is not in storage mode")
+			return nil
+		}
+	}
 	genesisBlock, err := rawdb.ReadBlockByNumber(tx, 0)
 	if err != nil {
 		return err
@@ -2097,7 +2111,9 @@ func scanReceipts(chaindata string, block uint64) error {
 	fixedCount := 0
 	logInterval := 30 * time.Second
 	logEvery := time.NewTicker(logInterval)
-	for blockNum := block; blockNum < block+100000; blockNum++ {
+	var key [8]byte
+	var v []byte
+	for blockNum := block; true; blockNum++ {
 		select {
 		default:
 		case <-logEvery.C:
@@ -2114,21 +2130,40 @@ func scanReceipts(chaindata string, block uint64) error {
 		if hash == (common.Hash{}) {
 			break
 		}
-		var block *types.Block
-		var senders []common.Address
-		if block, senders, err = rawdb.ReadBlockWithSenders(tx, hash, blockNum); err != nil {
+		binary.BigEndian.PutUint64(key[:], blockNum)
+		if v, err = tx.GetOne(dbutils.BlockReceiptsPrefix, key[:]); err != nil {
 			return err
 		}
-		receipts := rawdb.ReadReceipts(tx, block, senders)
-		for _, receipt := range receipts {
-			receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
-		}
-		if chainConfig.IsByzantium(block.Number().Uint64()) {
-			receiptSha := types.DeriveSha(receipts)
-			if receiptSha == block.Header().ReceiptHash {
+		var receipts types.Receipts
+		if err = cbor.Unmarshal(&receipts, bytes.NewReader(v)); err == nil {
+			broken := false
+			for _, receipt := range receipts {
+				if receipt.CumulativeGasUsed < 10000 {
+					broken = true
+					break
+				}
+			}
+			if !broken {
 				continue
 			}
 		}
+		var block *types.Block
+		//var senders []common.Address
+		if block, _, err = rawdb.ReadBlockWithSenders(tx, hash, blockNum); err != nil {
+			return err
+		}
+		/*
+			receipts = rawdb.ReadReceipts(tx, block, senders)
+			for _, receipt := range receipts {
+				receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
+			}
+			if chainConfig.IsByzantium(block.Number().Uint64()) {
+				receiptSha := types.DeriveSha(receipts)
+				if receiptSha == block.Header().ReceiptHash {
+					continue
+				}
+			}
+		*/
 
 		dbstate := state.NewPlainKvState(tx, block.NumberU64()-1)
 		intraBlockState := state.New(dbstate)
@@ -2138,23 +2173,29 @@ func scanReceipts(chaindata string, block uint64) error {
 		if err1 != nil {
 			return err1
 		}
+		fix := true
 		if chainConfig.IsByzantium(block.Number().Uint64()) {
 			receiptSha := types.DeriveSha(receipts1)
 			if receiptSha != block.Header().ReceiptHash {
 				fmt.Printf("(retrace) mismatched receipt headers for block %d: %x, %x\n", block.NumberU64(), receiptSha, block.Header().ReceiptHash)
-			} else {
-				// All good, we can fix receipt record
-				buf.Reset()
-				err := cbor.Marshal(&buf, receipts1)
-				if err != nil {
-					return fmt.Errorf("encode block receipts for block %d: %v", blockNum, err)
-				}
-				if err = tx.Put(dbutils.BlockReceiptsPrefix, dbutils.ReceiptsKey(blockNum), buf.Bytes()); err != nil {
-					return fmt.Errorf("writing receipts for block %d: %v", blockNum, err)
-				}
-				fixedCount++
+				fix = false
 			}
 		}
+		if fix {
+			// All good, we can fix receipt record
+			buf.Reset()
+			err := cbor.Marshal(&buf, receipts1)
+			if err != nil {
+				return fmt.Errorf("encode block receipts for block %d: %v", blockNum, err)
+			}
+			if err = tx.Put(dbutils.BlockReceiptsPrefix, key[:], buf.Bytes()); err != nil {
+				return fmt.Errorf("writing receipts for block %d: %v", blockNum, err)
+			}
+			if _, err = w.Write([]byte(fmt.Sprintf("%d\n", blockNum))); err != nil {
+				return err
+			}
+			fixedCount++
+		}
 	}
 	return tx.Commit()
 }
@@ -2241,43 +2282,6 @@ func testTxPool() error {
 	return nil
 }
 
-var receiptTests = []struct {
-	cborStr string
-}{
-	{cborStr: "98a08400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000084004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400000840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000008400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400000840040000184004000018400400001840040000084004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000008400400001840040000184004000018400400001840040000184004000018400400000840040000184004000018400400001840040000184004000008400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001"},
-	{cborStr: "98a08400f60119e8948400f6011a00028ffb8400f6011a0003777d8400f6011a000a19e18400f6011a000e662c8400f6011a000eb8348400f6011a000f8c3a8400f6011a001040638400f6011a0011374c8400f6011a0011b0d18400f6011a00140bba8400f6011a00145dc28400f6011a00197eab8400f6011a0019d0b38400f6011a001a69898400f6011a001b60728400f6011a001da1118400f6011a001e92a58400f6011a001f5c478400f6011a001fae4f8400f6011a002000578400f6011a0020525f8400f6011a0020a4678400f6011a0020f66f8400f6011a002148778400f6011a00219a7f8400f6011a0021ec878400f6011a00223e8f8400f6011a002290978400f6011a0022e29f8400f6011a00235c248400f6011a0023ae2c8400f6011a002400348400f6011a0024523c8400f6011a0024cbc18400f6011a00251dc98400f6011a00256fd18400f6011a0025c1d98400f6011a0027505d8400f6011a0027a2658400f6011a00282a038400f6011a002be66d8400f6011a003536528400f6011a0035885a8400f6011a0035da628400f6011a0038f4e88400f6011a003946f08400f6011a003b664f8400f6011a003e02188400f6001a003e54208400f6011a004101348400f6011a004417598400f6011a0046439c8400f6011a00470f098400f6011a00479e928400f6011a0047f09a8400f6011a004842a28400f6011a0048f91a8400f6011a00494b228400f6011a00499d2a8400f6011a004a34c88400f6011a004a86d08400f6011a004ad8d88400f6011a004b2ae08400f6011a004d40c38400f6011a004e37ac8400f6011a005220de8400f6011a006680b08400f6011a0068b65f8400f6011a006cada28400f6011a006e77bf8400f6011a007117398400f6011a0071b02a8400f6011a007b8b518400f6011a007c4c088400f6011a007c98b78400f6011a007ceabf8400f6011a0080e8bf8400f6001a008175678400f6011a0081c76f8400f6011a00867d0d8400f6011a0087312a8400f6011a00881e0e8400f6011a008ad2058400f6011a008b86bd8400f6011a008bd8c58400f6011a008c2acd8400f6011a008c7cd58400f6011a008ccedd8400f6011a008d84808400f6011a008dd6888400f6011a008e28908400f6011a008eb8bb8400f6011a00902b2d8400f6011a0091b4508400f6001a0092b8098400f6011a00930a118400f6011a00935c198400f6011a0093ae218400f6011a009400298400f6011a00991f568400f6011a009a97b58400f6011a009ae9bd8400f6011a009b9ed78400f6011a009f69cf8400f6011a00a1364d8400f6011a00a188558400f6011a00a355a28400f6011a00a614ca8400f6011a00a6f05d8400f6011a00a7af5e8400f6011a00aaa96f8400f6011a00acd7898400f6011a00ae6c558400f6011a00b2a59f8400f6011a00b63dc68400f6011a00b8d7bb8400f6001a00b9591b8400f6011a00b9d0528400f6011a00ba22918400f6011a00ba74998400f6001a00baefa68400f6011a00bb41ae8400f6011a00bb93b68400f6011a00bc68e28400f6011a00bcbaea8400f6011a00bd0cf28400f6011a00bd5efa8400f6011a00bdb1028400f6011a00be030a8400f6011a00c1caed8400f6011a00c21cf58400f6011a00c26efd8400f6011a00c2c1058400f6001a00c33c008400f6011a00c3a63b8400f6011a00c585798400f6011a00c70f258400f6011a00c7612d8400f6011a00c98d4c8400f6011a00cb71988400f6001a00cbec998400f6011a00cfe49f8400f6011a00d1c6868400f6011a00d3a86d8400f6011a00d4671a8400f6001a00d4e2f08400f6011a00d6aca68400f6011a00d6feae8400f6011a00d750b68400f6011a00d80d078400f6011a00d85f0f8400f6011a00d915308400f6011a00dbc9d08400f6011a00ddcb408400f6011a00dfad278400f6011a00dfff2f8400f6011a00e272e38400f6011a00e4333c8400f6011a00e4c2c5"},
-}
-
-func testReceipts() error {
-	for i, tt := range receiptTests {
-		var cborBytes []byte
-		var err error
-		if cborBytes, err = hex.DecodeString(tt.cborStr); err != nil {
-			return err
-		}
-		var receipts types.Receipts
-		if err := cbor.Unmarshal(&receipts, bytes.NewReader(cborBytes)); err != nil {
-			log.Error("receipt unmarshal failed", "err", err)
-			return nil
-		}
-		fmt.Printf("%d: Number of receipts: %d\n", i, len(receipts))
-		for j, receipt := range receipts {
-			fmt.Printf("%d) type: %d, postState: %x, status: %d, cumulative gas: %d\n", j, receipt.Type, receipt.PostState, receipt.Status, receipt.CumulativeGasUsed)
-		}
-		var oldReceipts migrations.OldReceipts
-		reader := bytes.NewReader(cborBytes)
-		if err = cbor.Unmarshal(&oldReceipts, reader); err != nil {
-			fmt.Printf("Error parsing as old: %v\n", err)
-		} else {
-			fmt.Printf("Unread portion: %d\n", reader.Len())
-			for j, receipt := range oldReceipts {
-				fmt.Printf("old-%d) postState: %x, status: %d, cumulative gas: %d\n", j, receipt.PostState, receipt.Status, receipt.CumulativeGasUsed)
-			}
-		}
-	}
-	return nil
-}
-
 func main() {
 	flag.Parse()
 
@@ -2441,8 +2445,6 @@ func main() {
 	case "testTxPool":
 		err = testTxPool()
 
-	case "testReceipts":
-		err = testReceipts()
 	}
 
 	if err != nil {
diff --git a/migrations/migrations.go b/migrations/migrations.go
index bdcc50ce8c..87800ba3cc 100644
--- a/migrations/migrations.go
+++ b/migrations/migrations.go
@@ -63,6 +63,7 @@ var migrations = map[ethdb.Label][]Migration{
 		rebuilCallTraceIndex,
 		fixSequences,
 		receiptCbor,
+		receiptRepair,
 	},
 	ethdb.TxPool: {},
 	ethdb.Sentry: {},
diff --git a/migrations/receipt_cbor.go b/migrations/receipt_cbor.go
index b8a6e48ec4..05260d3a6a 100644
--- a/migrations/receipt_cbor.go
+++ b/migrations/receipt_cbor.go
@@ -1,406 +1,13 @@
 package migrations
 
 import (
-	"bytes"
-	"encoding/binary"
-	"fmt"
-	"time"
-
-	"github.com/ledgerwatch/erigon/common"
-	"github.com/ledgerwatch/erigon/common/dbutils"
 	"github.com/ledgerwatch/erigon/common/etl"
-	"github.com/ledgerwatch/erigon/core/rawdb"
-	"github.com/ledgerwatch/erigon/core/types"
-	"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
 	"github.com/ledgerwatch/erigon/ethdb"
-	"github.com/ledgerwatch/erigon/ethdb/cbor"
-	"github.com/ledgerwatch/erigon/log"
-
-	"errors"
-	pkg2_big "math/big"
-	"runtime"
-	"strconv"
-
-	pkg1_common "github.com/ledgerwatch/erigon/common"
-	codec1978 "github.com/ugorji/go/codec"
 )
 
-// OldReceipt is receipt structure before introduction of Type field
-// to be able to read old records
-type OldReceipt struct {
-	// Consensus fields: These fields are defined by the Yellow Paper
-	PostState         []byte `json:"root" codec:"1"`
-	Status            uint64 `json:"status" codec:"2"`
-	CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required" codec:"3"`
-}
-
-type OldReceipts []*OldReceipt
-
 var receiptCbor = Migration{
 	Name: "receipt_cbor",
 	Up: func(db ethdb.Database, tmpdir string, progress []byte, CommitProgress etl.LoadCommitHandler) (err error) {
-		var tx ethdb.RwTx
-		if hasTx, ok := db.(ethdb.HasTx); ok {
-			tx = hasTx.Tx().(ethdb.RwTx)
-		} else {
-			return fmt.Errorf("no transaction")
-		}
-		genesisBlock, err := rawdb.ReadBlockByNumber(tx, 0)
-		if err != nil {
-			return err
-		}
-		if genesisBlock == nil {
-			// Empty database check
-			return CommitProgress(db, nil, true)
-		}
-		chainConfig, cerr := rawdb.ReadChainConfig(tx, genesisBlock.Hash())
-		if cerr != nil {
-			return cerr
-		}
-		logInterval := 30 * time.Second
-		logEvery := time.NewTicker(logInterval)
-		defer logEvery.Stop()
-		var buf bytes.Buffer
-		var key [8]byte
-		var v []byte
-		var to uint64
-		if to, err = stages.GetStageProgress(tx, stages.Execution); err != nil {
-			return err
-		}
-		for blockNum := uint64(1); blockNum <= to; blockNum++ {
-			binary.BigEndian.PutUint64(key[:], blockNum)
-			if v, err = tx.GetOne(dbutils.BlockReceiptsPrefix, key[:]); err != nil {
-				return err
-			}
-			if v == nil {
-				continue
-			}
-			select {
-			default:
-			case <-logEvery.C:
-				log.Info("Scanned receipts up to", "block", blockNum)
-			}
-			var receipts types.Receipts
-			var oldReceipts OldReceipts
-			if err = cbor.Unmarshal(&oldReceipts, bytes.NewReader(v)); err != nil {
-				continue
-			}
-
-			var blockHash common.Hash
-			if blockHash, err = rawdb.ReadCanonicalHash(tx, blockNum); err != nil {
-				return err
-			}
-			var body *types.Body
-			if chainConfig.IsBerlin(blockNum) {
-				body = rawdb.ReadBody(tx, blockHash, blockNum)
-			}
-			receipts = make(types.Receipts, len(oldReceipts))
-			for i, oldReceipt := range oldReceipts {
-				receipts[i] = new(types.Receipt)
-				receipts[i].PostState = oldReceipt.PostState
-				receipts[i].Status = oldReceipt.Status
-				receipts[i].CumulativeGasUsed = oldReceipt.CumulativeGasUsed
-				if body != nil {
-					receipts[i].Type = body.Transactions[i].Type()
-				}
-			}
-			buf.Reset()
-			if err = cbor.Marshal(&buf, receipts); err != nil {
-				return err
-			}
-			if err = tx.Put(dbutils.BlockReceiptsPrefix, common.CopyBytes(key[:]), common.CopyBytes(buf.Bytes())); err != nil {
-				return err
-			}
-		}
 		return CommitProgress(db, nil, true)
 	},
 }
-
-const (
-	// ----- value types used ----
-	codecSelferValueTypeArray2 = 10
-	codecSelferValueTypeMap2   = 9
-	codecSelferValueTypeNil2   = 1
-)
-
-var (
-	errCodecSelferOnlyMapOrArrayEncodeToStruct2 = errors.New(`only encoded map or array can be decoded into a struct`)
-)
-
-type codecSelfer2 struct{}
-
-func init() {
-	if codec1978.GenVersion != 19 {
-		_, file, _, _ := runtime.Caller(0)
-		ver := strconv.FormatInt(int64(codec1978.GenVersion), 10)
-		panic(errors.New("codecgen version mismatch: current: 19, need " + ver + ". Re-generate file: " + file))
-	}
-	if false { // reference the types, but skip this branch at build/run time
-		var _ pkg1_common.Address
-		var _ pkg2_big.Int
-	}
-}
-
-func (x *OldReceipt) CodecEncodeSelf(e *codec1978.Encoder) {
-	var h codecSelfer2
-	z, r := codec1978.GenHelperEncoder(e)
-	_, _, _ = h, z, r
-	if x == nil {
-		r.EncodeNil()
-	} else {
-		yy2arr2 := z.EncBasicHandle().StructToArray
-		_ = yy2arr2
-		z.EncWriteArrayStart(3)
-		z.EncWriteArrayElem()
-		if x.PostState == nil {
-			r.EncodeNil()
-		} else {
-			r.EncodeStringBytesRaw([]byte(x.PostState))
-		} // end block: if x.PostState slice == nil
-		z.EncWriteArrayElem()
-		r.EncodeUint(uint64(x.Status))
-		z.EncWriteArrayElem()
-		r.EncodeUint(uint64(x.CumulativeGasUsed))
-		z.EncWriteArrayEnd()
-	}
-}
-
-func (x *OldReceipt) CodecDecodeSelf(d *codec1978.Decoder) {
-	var h codecSelfer2
-	z, r := codec1978.GenHelperDecoder(d)
-	_, _, _ = h, z, r
-	yyct2 := r.ContainerType()
-	if yyct2 == codecSelferValueTypeNil2 {
-		*(x) = OldReceipt{}
-	} else if yyct2 == codecSelferValueTypeMap2 {
-		yyl2 := z.DecReadMapStart()
-		if yyl2 == 0 {
-		} else {
-			x.codecDecodeSelfFromMap(yyl2, d)
-		}
-		z.DecReadMapEnd()
-	} else if yyct2 == codecSelferValueTypeArray2 {
-		yyl2 := z.DecReadArrayStart()
-		if yyl2 != 0 {
-			x.codecDecodeSelfFromArray(yyl2, d)
-		}
-		z.DecReadArrayEnd()
-	} else {
-		panic(errCodecSelferOnlyMapOrArrayEncodeToStruct2)
-	}
-}
-
-func (x *OldReceipt) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
-	var h codecSelfer2
-	z, r := codec1978.GenHelperDecoder(d)
-	_, _, _ = h, z, r
-	var yyhl3 bool = l >= 0
-	for yyj3 := 0; ; yyj3++ {
-		if yyhl3 {
-			if yyj3 >= l {
-				break
-			}
-		} else {
-			if z.DecCheckBreak() {
-				break
-			}
-		}
-		z.DecReadMapElemKey()
-		yys3 := z.StringView(r.DecodeStringAsBytes())
-		z.DecReadMapElemValue()
-		switch yys3 {
-		case "1":
-			x.PostState = r.DecodeBytes(([]byte)(x.PostState), false)
-		case "2":
-			x.Status = (uint64)(r.DecodeUint64())
-		case "3":
-			x.CumulativeGasUsed = (uint64)(r.DecodeUint64())
-		default:
-			z.DecStructFieldNotFound(-1, yys3)
-		} // end switch yys3
-	} // end for yyj3
-}
-
-func (x *OldReceipt) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
-	var h codecSelfer2
-	z, r := codec1978.GenHelperDecoder(d)
-	_, _, _ = h, z, r
-	var yyj8 int
-	var yyb8 bool
-	var yyhl8 bool = l >= 0
-	yyj8++
-	if yyhl8 {
-		yyb8 = yyj8 > l
-	} else {
-		yyb8 = z.DecCheckBreak()
-	}
-	if yyb8 {
-		z.DecReadArrayEnd()
-		return
-	}
-	z.DecReadArrayElem()
-	x.PostState = r.DecodeBytes(([]byte)(x.PostState), false)
-	yyj8++
-	if yyhl8 {
-		yyb8 = yyj8 > l
-	} else {
-		yyb8 = z.DecCheckBreak()
-	}
-	if yyb8 {
-		z.DecReadArrayEnd()
-		return
-	}
-	z.DecReadArrayElem()
-	x.Status = (uint64)(r.DecodeUint64())
-	yyj8++
-	if yyhl8 {
-		yyb8 = yyj8 > l
-	} else {
-		yyb8 = z.DecCheckBreak()
-	}
-	if yyb8 {
-		z.DecReadArrayEnd()
-		return
-	}
-	z.DecReadArrayElem()
-	x.CumulativeGasUsed = (uint64)(r.DecodeUint64())
-	for {
-		yyj8++
-		if yyhl8 {
-			yyb8 = yyj8 > l
-		} else {
-			yyb8 = z.DecCheckBreak()
-		}
-		if yyb8 {
-			break
-		}
-		z.DecReadArrayElem()
-		z.DecStructFieldNotFound(yyj8-1, "")
-	}
-}
-
-func (x *OldReceipt) IsCodecEmpty() bool {
-	return !(len(x.PostState) != 0 && x.Status != 0 && x.CumulativeGasUsed != 0 && true)
-}
-
-func (x OldReceipts) CodecEncodeSelf(e *codec1978.Encoder) {
-	var h codecSelfer2
-	z, r := codec1978.GenHelperEncoder(e)
-	_, _, _ = h, z, r
-	if x == nil {
-		r.EncodeNil()
-	} else {
-		h.encReceipts((OldReceipts)(x), e)
-	} // end block: if x slice == nil
-}
-
-func (x *OldReceipts) CodecDecodeSelf(d *codec1978.Decoder) {
-	var h codecSelfer2
-	z, r := codec1978.GenHelperDecoder(d)
-	_, _, _ = h, z, r
-	h.decReceipts((*OldReceipts)(x), d)
-}
-
-func (x codecSelfer2) encReceipts(v OldReceipts, e *codec1978.Encoder) {
-	var h codecSelfer2
-	z, r := codec1978.GenHelperEncoder(e)
-	_, _, _ = h, z, r
-	if v == nil {
-		r.EncodeNil()
-		return
-	}
-	z.EncWriteArrayStart(len(v))
-	for _, yyv1 := range v {
-		z.EncWriteArrayElem()
-		if yyv1 == nil {
-			r.EncodeNil()
-		} else {
-			yyv1.CodecEncodeSelf(e)
-		}
-	}
-	z.EncWriteArrayEnd()
-}
-
-func (x codecSelfer2) decReceipts(v *OldReceipts, d *codec1978.Decoder) {
-	var h codecSelfer2
-	z, r := codec1978.GenHelperDecoder(d)
-	_, _, _ = h, z, r
-
-	yyv1 := *v
-	yyh1, yyl1 := z.DecSliceHelperStart()
-	var yyc1 bool
-	_ = yyc1
-	if yyh1.IsNil {
-		if yyv1 != nil {
-			yyv1 = nil
-			yyc1 = true
-		}
-	} else if yyl1 == 0 {
-		if yyv1 == nil {
-			yyv1 = []*OldReceipt{}
-			yyc1 = true
-		} else if len(yyv1) != 0 {
-			yyv1 = yyv1[:0]
-			yyc1 = true
-		}
-	} else {
-		yyhl1 := yyl1 > 0
-		var yyrl1 int
-		_ = yyrl1
-		if yyhl1 {
-			if yyl1 > cap(yyv1) {
-				yyrl1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 8)
-				if yyrl1 <= cap(yyv1) {
-					yyv1 = yyv1[:yyrl1]
-				} else {
-					yyv1 = make([]*OldReceipt, yyrl1)
-				}
-				yyc1 = true
-			} else if yyl1 != len(yyv1) {
-				yyv1 = yyv1[:yyl1]
-				yyc1 = true
-			}
-		}
-		var yyj1 int
-		for yyj1 = 0; (yyhl1 && yyj1 < yyl1) || !(yyhl1 || z.DecCheckBreak()); yyj1++ { // bounds-check-elimination
-			if yyj1 == 0 && yyv1 == nil {
-				if yyhl1 {
-					yyrl1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 8)
-				} else {
-					yyrl1 = 8
-				}
-				yyv1 = make([]*OldReceipt, yyrl1)
-				yyc1 = true
-			}
-			yyh1.ElemContainerState(yyj1)
-			var yydb1 bool
-			if yyj1 >= len(yyv1) {
-				yyv1 = append(yyv1, nil)
-				yyc1 = true
-			}
-			if yydb1 {
-				z.DecSwallow()
-			} else {
-				if r.TryNil() {
-					yyv1[yyj1] = nil
-				} else {
-					if yyv1[yyj1] == nil {
-						yyv1[yyj1] = new(OldReceipt)
-					}
-					yyv1[yyj1].CodecDecodeSelf(d)
-				}
-			}
-		}
-		if yyj1 < len(yyv1) {
-			yyv1 = yyv1[:yyj1]
-			yyc1 = true
-		} else if yyj1 == 0 && yyv1 == nil {
-			yyv1 = make([]*OldReceipt, 0)
-			yyc1 = true
-		}
-	}
-	yyh1.End()
-	if yyc1 {
-		*v = yyv1
-	}
-}
diff --git a/migrations/receipt_repair.go b/migrations/receipt_repair.go
new file mode 100644
index 0000000000..eea42aaf25
--- /dev/null
+++ b/migrations/receipt_repair.go
@@ -0,0 +1,161 @@
+package migrations
+
+import (
+	"bytes"
+	"context"
+	"encoding/binary"
+	"fmt"
+	"time"
+
+	"github.com/ledgerwatch/erigon/common"
+	"github.com/ledgerwatch/erigon/common/dbutils"
+	"github.com/ledgerwatch/erigon/common/etl"
+	"github.com/ledgerwatch/erigon/consensus/ethash"
+	"github.com/ledgerwatch/erigon/consensus/misc"
+	"github.com/ledgerwatch/erigon/core"
+	"github.com/ledgerwatch/erigon/core/rawdb"
+	"github.com/ledgerwatch/erigon/core/state"
+	"github.com/ledgerwatch/erigon/core/types"
+	"github.com/ledgerwatch/erigon/core/vm"
+	"github.com/ledgerwatch/erigon/ethdb"
+	"github.com/ledgerwatch/erigon/ethdb/cbor"
+	"github.com/ledgerwatch/erigon/log"
+	"github.com/ledgerwatch/erigon/params"
+)
+
+var receiptRepair = Migration{
+	Name: "receipt_repair",
+	Up: func(db ethdb.Database, tmpdir string, progress []byte, CommitProgress etl.LoadCommitHandler) (err error) {
+		var tx ethdb.RwTx
+		if hasTx, ok := db.(ethdb.HasTx); ok {
+			tx = hasTx.Tx().(ethdb.RwTx)
+		} else {
+			return fmt.Errorf("no transaction")
+		}
+		if sm, smErr := ethdb.GetStorageModeFromDB(tx); smErr != nil {
+			return smErr
+		} else {
+			if !sm.History {
+				log.Warn("Could not perform this migration because history is not in storage mode")
+				return CommitProgress(db, nil, true)
+			}
+		}
+
+		genesisBlock, err := rawdb.ReadBlockByNumber(tx, 0)
+		if err != nil {
+			return err
+		}
+		chainConfig, cerr := rawdb.ReadChainConfig(tx, genesisBlock.Hash())
+		if cerr != nil {
+			return cerr
+		}
+		vmConfig := vm.Config{}
+		noOpWriter := state.NewNoopWriter()
+		var buf bytes.Buffer
+		fixedCount := 0
+		logInterval := 30 * time.Second
+		logEvery := time.NewTicker(logInterval)
+		var key [8]byte
+		var v []byte
+		for blockNum := uint64(1); true; blockNum++ {
+			select {
+			default:
+			case <-logEvery.C:
+				log.Info("Progress", "block", blockNum, "fixed", fixedCount)
+			}
+			var hash common.Hash
+			if hash, err = rawdb.ReadCanonicalHash(tx, blockNum); err != nil {
+				return err
+			}
+			if hash == (common.Hash{}) {
+				break
+			}
+			binary.BigEndian.PutUint64(key[:], blockNum)
+			if v, err = tx.GetOne(dbutils.BlockReceiptsPrefix, key[:]); err != nil {
+				return err
+			}
+			var receipts types.Receipts
+			if err = cbor.Unmarshal(&receipts, bytes.NewReader(v)); err == nil {
+				broken := false
+				for _, receipt := range receipts {
+					if receipt.CumulativeGasUsed < 10000 {
+						broken = true
+						break
+					}
+				}
+				if !broken {
+					continue
+				}
+			}
+			var block *types.Block
+			if block, _, err = rawdb.ReadBlockWithSenders(tx, hash, blockNum); err != nil {
+				return err
+			}
+
+			dbstate := state.NewPlainKvState(tx, block.NumberU64()-1)
+			intraBlockState := state.New(dbstate)
+
+			getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(tx, hash, number) }
+			receipts1, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, nil /* checkTEVM */, block, vmConfig)
+			if err1 != nil {
+				return err1
+			}
+			fix := true
+			if chainConfig.IsByzantium(block.Number().Uint64()) {
+				receiptSha := types.DeriveSha(receipts1)
+				if receiptSha != block.Header().ReceiptHash {
+					fmt.Printf("(retrace) mismatched receipt headers for block %d: %x, %x\n", block.NumberU64(), receiptSha, block.Header().ReceiptHash)
+					fix = false
+				}
+			}
+			if fix {
+				// All good, we can fix receipt record
+				buf.Reset()
+				err := cbor.Marshal(&buf, receipts1)
+				if err != nil {
+					return fmt.Errorf("encode block receipts for block %d: %v", blockNum, err)
+				}
+				if err = tx.Put(dbutils.BlockReceiptsPrefix, key[:], buf.Bytes()); err != nil {
+					return fmt.Errorf("writing receipts for block %d: %v", blockNum, err)
+				}
+				fixedCount++
+			}
+		}
+		return CommitProgress(db, nil, true)
+	},
+}
+
+func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWriter state.StateWriter,
+	chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, checkTEVM func(common.Hash) (bool, error), block *types.Block, vmConfig vm.Config) (types.Receipts, error) {
+	header := block.Header()
+	vmConfig.TraceJumpDest = true
+	engine := ethash.NewFullFaker()
+	gp := new(core.GasPool).AddGas(block.GasLimit())
+	usedGas := new(uint64)
+	var receipts types.Receipts
+	if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
+		misc.ApplyDAOHardFork(ibs)
+	}
+	for i, tx := range block.Transactions() {
+		ibs.Prepare(tx.Hash(), block.Hash(), i)
+		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, checkTEVM)
+		if err != nil {
+			return nil, fmt.Errorf("could not apply tx %d [%x] failed: %v", i, tx.Hash(), err)
+		}
+		receipts = append(receipts, receipt)
+	}
+
+	if !vmConfig.ReadOnly {
+		// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
+		if _, err := engine.FinalizeAndAssemble(chainConfig, header, ibs, block.Transactions(), block.Uncles(), receipts, nil); err != nil {
+			return nil, fmt.Errorf("finalize of block %d failed: %v", block.NumberU64(), err)
+		}
+
+		ctx := chainConfig.WithEIPsFlags(context.Background(), header.Number.Uint64())
+		if err := ibs.CommitBlock(ctx, blockWriter); err != nil {
+			return nil, fmt.Errorf("committing block %d failed: %v", block.NumberU64(), err)
+		}
+	}
+
+	return receipts, nil
+}
-- 
GitLab