From a27869615132088cb82cb446d1b5b4da7cda4805 Mon Sep 17 00:00:00 2001 From: JukLee0ira Date: Mon, 23 Sep 2024 13:38:24 +0800 Subject: [PATCH] core/rawdb: simple legacy receipt converter (24028) --- XDCxDAO/interfaces.go | 1 + XDCxDAO/leveldb.go | 6 ++ XDCxDAO/mongodb.go | 6 ++ cmd/XDC/chaincmd.go | 2 +- cmd/XDC/config.go | 1 - cmd/utils/utils.go | 35 ++++++---- core/rawdb/accessors_chain.go | 11 +--- core/rawdb/database.go | 6 ++ core/rawdb/freezer.go | 21 ++++++ core/rawdb/table.go | 6 ++ core/types/log.go | 59 ----------------- core/types/receipt.go | 102 +++-------------------------- core/types/receipt_test.go | 119 ---------------------------------- ethdb/database.go | 5 ++ node/node.go | 23 +++++++ 15 files changed, 106 insertions(+), 297 deletions(-) create mode 100644 core/rawdb/freezer.go diff --git a/XDCxDAO/interfaces.go b/XDCxDAO/interfaces.go index 066df40b0abd..b8f7fb5599f0 100644 --- a/XDCxDAO/interfaces.go +++ b/XDCxDAO/interfaces.go @@ -46,6 +46,7 @@ type XDCXDAO interface { AppendAncient(number uint64, hash, header, body, receipt, td []byte) error TruncateAncients(n uint64) error Sync() error + MigrateTable(string, func([]byte) ([]byte, error)) error //type convertLegacyFn = func([]byte) ([]byte, error) NewIterator(prefix []byte, start []byte) ethdb.Iterator Stat(property string) (string, error) diff --git a/XDCxDAO/leveldb.go b/XDCxDAO/leveldb.go index 2a76162d4ca4..6fbbd3314570 100644 --- a/XDCxDAO/leveldb.go +++ b/XDCxDAO/leveldb.go @@ -171,6 +171,12 @@ func (db *BatchDatabase) Sync() error { return errNotSupported } +// MigrateTable processes the entries in a given table in sequence +// converting them to a new format if they're of an old format. +func (db *BatchDatabase) MigrateTable(kind string, convert func([]byte) ([]byte, error)) error { + return errNotSupported +} + func (db *BatchDatabase) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("NewIterator from XDCxDAO leveldb is not supported") } diff --git a/XDCxDAO/mongodb.go b/XDCxDAO/mongodb.go index 3d2efef31715..5f21611e26d0 100644 --- a/XDCxDAO/mongodb.go +++ b/XDCxDAO/mongodb.go @@ -873,6 +873,12 @@ func (db *MongoDatabase) Sync() error { return errNotSupported } +// MigrateTable processes the entries in a given table in sequence +// converting them to a new format if they're of an old format. +func (db *MongoDatabase) MigrateTable(kind string, convert func([]byte) ([]byte, error)) error { + return errNotSupported +} + func (db *MongoDatabase) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("NewIterator from XDCxDAO mongodb is not supported") } diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go index ae4221e4b211..6864eece1787 100644 --- a/cmd/XDC/chaincmd.go +++ b/cmd/XDC/chaincmd.go @@ -463,7 +463,7 @@ func dump(ctx *cli.Context) error { stack, _ := makeFullNode(ctx) chain, chainDb := utils.MakeChain(ctx, stack) defer chainDb.Close() - + for _, arg := range ctx.Args() { var block *types.Block if hashish(arg) { diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index a47f19c7bf20..50abf3cd8ac2 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -247,7 +247,6 @@ func makeFullNode(ctx *cli.Context) (*node.Node, XDCConfig) { // enable in default utils.RegisterXDCXService(stack, &cfg.XDCX) utils.RegisterEthService(stack, &cfg.Eth) - // Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode shhEnabled := enableWhisper(ctx) shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name) diff --git a/cmd/utils/utils.go b/cmd/utils/utils.go index a329fc8c4d2e..18c8eadec22e 100644 --- a/cmd/utils/utils.go +++ b/cmd/utils/utils.go @@ -13,29 +13,36 @@ import ( ) // RegisterEthService adds an Ethereum client to the stack. -func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) { +func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) *eth.Ethereum { var err error if cfg.SyncMode == downloader.LightSync { err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return les.New(ctx, cfg) }) - } else { - err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - var XDCXServ *XDCx.XDCX - ctx.Service(&XDCXServ) - var lendingServ *XDCxlending.Lending - ctx.Service(&lendingServ) - fullNode, err := eth.New(ctx, cfg, XDCXServ, lendingServ) - if fullNode != nil && cfg.LightServ > 0 { - ls, _ := les.NewLesServer(fullNode, cfg) - fullNode.AddLesServer(ls) - } - return fullNode, err - }) + if err != nil { + Fatalf("Failed to register the Ethereum service: %v", err) + } + return nil } + var backend *eth.Ethereum + err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + var XDCXServ *XDCx.XDCX + ctx.Service(&XDCXServ) + var lendingServ *XDCxlending.Lending + ctx.Service(&lendingServ) + fullNode, err := eth.New(ctx, cfg, XDCXServ, lendingServ) + if fullNode != nil && cfg.LightServ > 0 { + ls, _ := les.NewLesServer(fullNode, cfg) + fullNode.AddLesServer(ls) + } + backend = fullNode + return fullNode, err + }) + if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) } + return backend } // RegisterShhService configures Whisper and adds it to the given node. diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 97ce46b293d3..c6cd71e4c2c8 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -258,11 +258,7 @@ func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { type storedReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 - Bloom types.Bloom - TxHash common.Hash - ContractAddress common.Address - Logs []*types.LogForStorage - GasUsed uint64 + Logs []*types.Log } // ReceiptLogs is a barebone version of ReceiptForStorage which only keeps @@ -278,10 +274,7 @@ func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error { if err := s.Decode(&stored); err != nil { return err } - r.Logs = make([]*types.Log, len(stored.Logs)) - for i, log := range stored.Logs { - r.Logs[i] = (*types.Log)(log) - } + r.Logs = stored.Logs return nil } diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 7142d3048f5f..b998a7b3a278 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -85,6 +85,12 @@ func (db *nofreezedb) Sync() error { return errNotSupported } +// MigrateTable processes the entries in a given table in sequence +// converting them to a new format if they're of an old format. +func (db *nofreezedb) MigrateTable(kind string, convert convertLegacyFn) error { + return errNotSupported +} + // NewDatabase creates a high level database on top of a given key-value data // store without a freezer moving immutable chain segments into cold storage. func NewDatabase(db ethdb.KeyValueStore) ethdb.Database { diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go new file mode 100644 index 000000000000..622cbb3ff9e5 --- /dev/null +++ b/core/rawdb/freezer.go @@ -0,0 +1,21 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +// convertLegacyFn takes a raw freezer entry in an older format and +// returns it in the new format. +type convertLegacyFn = func([]byte) ([]byte, error) diff --git a/core/rawdb/table.go b/core/rawdb/table.go index dfb58c07b622..ca65c3f51114 100644 --- a/core/rawdb/table.go +++ b/core/rawdb/table.go @@ -92,6 +92,12 @@ func (t *table) Sync() error { return t.db.Sync() } +// MigrateTable processes the entries in a given table in sequence +// converting them to a new format if they're of an old format. +func (t *table) MigrateTable(kind string, convert convertLegacyFn) error { + return t.db.MigrateTable(kind, convert) +} + // Put inserts the given value into the database at a prefixed version of the // provided key. func (t *table) Put(key []byte, value []byte) error { diff --git a/core/types/log.go b/core/types/log.go index 75d5d17d4748..b0176e3c2d70 100644 --- a/core/types/log.go +++ b/core/types/log.go @@ -69,21 +69,6 @@ type rlpLog struct { Data []byte } -// 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}) @@ -102,47 +87,3 @@ func (l *Log) DecodeRLP(s *rlp.Stream) error { func (l *Log) String() string { return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index) } - -// LogForStorage is a wrapper around a Log that flattens and parses the entire content of -// a log including non-consensus fields. -type LogForStorage Log - -// EncodeRLP implements rlp.Encoder. -func (l *LogForStorage) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, rlpStorageLog{ - Address: l.Address, - Topics: l.Topics, - Data: l.Data, - }) -} - -// DecodeRLP implements rlp.Decoder. -// -// Note some redundant fields(e.g. block number, tx hash etc) will be assembled later. -func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error { - blob, err := s.Raw() - if err != nil { - return err - } - 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 -} diff --git a/core/types/receipt.go b/core/types/receipt.go index db580e055b7f..70ae919c0890 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -94,28 +94,7 @@ type receiptRLP struct { type storedReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 - 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 + Logs []*Log } // receiptStorageRLP is the storage encoding of a receipt. @@ -124,7 +103,7 @@ type receiptStorageRLP struct { CumulativeGasUsed uint64 TxHash common.Hash ContractAddress common.Address - Logs []*LogForStorage + Logs []*Log GasUsed uint64 } @@ -135,7 +114,7 @@ type receiptSwollenStorageRLP struct { Bloom Bloom TxHash common.Hash ContractAddress common.Address - Logs []*LogForStorage + Logs []*Log GasUsed uint64 } @@ -269,16 +248,13 @@ 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 { - enc := &v3StoredReceiptRLP{ + enc := &storedReceiptRLP{ PostStateOrStatus: (*Receipt)(r).statusEncoding(), CumulativeGasUsed: r.CumulativeGasUsed, - Bloom: r.Bloom, - TxHash: r.TxHash, - ContractAddress: r.ContractAddress, - Logs: make([]*LogForStorage, len(r.Logs)), + Logs: make([]*Log, len(r.Logs)), } for i, log := range r.Logs { - enc.Logs[i] = (*LogForStorage)(log) + enc.Logs[i] = (*Log)(log) } return rlp.Encode(w, enc) } @@ -286,82 +262,20 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { // 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 := 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 { + if err := s.Decode(&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.Logs = stored.Logs 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 -} - // Receipts implements DerivableList for receipts. type Receipts []*Receipt diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index 9f2d44d52317..dfe50b3c8f1d 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -20,7 +20,6 @@ import ( "bytes" "math" "math/big" - "reflect" "testing" "github.com/XinFinOrg/XDPoSChain/common" @@ -38,124 +37,6 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) { } } -func TestLegacyReceiptDecoding(t *testing.T) { - tests := []struct { - name string - encode func(*Receipt) ([]byte, error) - }{ - { - "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 diff --git a/ethdb/database.go b/ethdb/database.go index 634bd1de2d98..2bad36767555 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -97,6 +97,11 @@ type AncientWriter interface { // Sync flushes all in-memory ancient store data to disk. Sync() error + + // MigrateTable processes and migrates entries of a given table to a new format. + // The second argument is a function that takes a raw entry and returns it + // in the newest format. + MigrateTable(string, func([]byte) ([]byte, error)) error } // Reader contains the methods required to read data from both key-value as well as diff --git a/node/node.go b/node/node.go index a9f767bf28f1..f871c00d1f40 100644 --- a/node/node.go +++ b/node/node.go @@ -233,6 +233,29 @@ func (n *Node) Start() error { return nil } +// Close stops the Node and releases resources acquired in +// Node constructor New. +func (n *Node) Close() error { + var errs []error + + // Terminate all subsystems and collect any errors + if err := n.Stop(); err != nil && err != ErrNodeStopped { + errs = append(errs, err) + } + if err := n.accman.Close(); err != nil { + errs = append(errs, err) + } + // Report any errors that might have occurred + switch len(errs) { + case 0: + return nil + case 1: + return errs[0] + default: + return fmt.Errorf("%v", errs) + } +} + func (n *Node) openDataDir() error { if n.config.DataDir == "" { return nil // ephemeral