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