From 2c188b2bc61d4af44adf85518db7f1429e3b9642 Mon Sep 17 00:00:00 2001 From: buddh0 Date: Thu, 21 Mar 2024 18:51:25 +0800 Subject: [PATCH] core/rawdb: optimize write block with sidecars --- core/blockchain.go | 17 +++- core/rawdb/accessors_chain.go | 148 ++--------------------------- core/rawdb/accessors_chain_test.go | 8 +- core/rawdb/chain_freezer.go | 4 +- core/state/pruner/pruner.go | 17 +--- params/config.go | 9 ++ 6 files changed, 41 insertions(+), 162 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index ecb4cb8bd8..b633dfb03c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1408,12 +1408,19 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ writeSize int64 err error ) - if !bc.chainConfig.IsCancun(last.Number(), last.Time()) { - log.Info("WriteAncientBlocks", "startAt", blockChain[0].Number(), "last", last.Number()) - writeSize, err = rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, td) - } else { - writeSize, err = rawdb.WriteAncientBlocksAfterCancun(bc.db, bc.chainConfig, blockChain, receiptChain, td) + if bc.chainConfig.IsCancun(last.Number(), last.Time()) { + for _, block := range blockChain { + if bc.chainConfig.IsCancun(block.Number(), block.Time()) { + // try reset empty blob ancient table + if err := rawdb.TryResetEmptyBlobAncientTable(bc.db, block.NumberU64()); err != nil { + return 0, err + } + break + } + } } + writeSize, err = rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, td) + if err != nil { log.Error("Error importing chain data to ancients", "err", err) return 0, err diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 030dd20864..01656f3de9 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -823,123 +823,6 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts }) } -// WriteAncientBlocksAfterCancun writes entire block data into ancient store and returns the total written size. -func WriteAncientBlocksAfterCancun(db ethdb.AncientStore, config *params.ChainConfig, blocks []*types.Block, receipts []types.Receipts, td *big.Int) (int64, error) { - if len(blocks) == 0 { - return 0, nil - } - - cancunIndex := -1 - for i, block := range blocks { - if config.IsCancun(block.Number(), block.Time()) { - cancunIndex = i - break - } - } - log.Info("WriteAncientBlocksAfterCancun", "startAt", blocks[0].Number(), "cancunIndex", cancunIndex, "len", len(blocks)) - if cancunIndex < 0 { - return WriteAncientBlocks(db, blocks, receipts, td) - } - - // if there has pre-cancun and post-cancun blocks, write them separately - var ( - tdSum = new(big.Int).Set(td) - stReceipts []*types.ReceiptForStorage - ) - - // write pre-cancun blocks - preBlocks := blocks[:cancunIndex] - preReceipts := receipts[:cancunIndex] - preSize, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { - for i, block := range preBlocks { - // Convert receipts to storage format and sum up total difficulty. - stReceipts = stReceipts[:0] - for _, receipt := range preReceipts[i] { - stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt)) - } - header := block.Header() - if i > 0 { - tdSum.Add(tdSum, header.Difficulty) - } - if err := writeAncientBlock(op, block, header, stReceipts, tdSum); err != nil { - return err - } - } - return nil - }) - if err != nil { - return preSize, err - } - - // write post-cancun blocks - postBlocks := blocks[cancunIndex:] - postReceipts := receipts[cancunIndex:] - // try reset empty blob ancient table - if err := ResetEmptyBlobAncientTable(db, postBlocks[0].NumberU64()); err != nil { - return 0, err - } - postSize, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { - for i, block := range postBlocks { - // Convert receipts to storage format and sum up total difficulty. - stReceipts = stReceipts[:0] - for _, receipt := range postReceipts[i] { - stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt)) - } - header := block.Header() - if i > 0 { - tdSum.Add(tdSum, header.Difficulty) - } - if err := writeAncientBlockWithSidecar(op, block, header, stReceipts, tdSum, block.Sidecars()); err != nil { - return err - } - } - return nil - }) - return preSize + postSize, err -} - -// WriteAncientBlocksWithSidecars writes entire block data into ancient store and returns the total written size. -// Attention: The caller must set blobs after cancun -func WriteAncientBlocksWithSidecars(db ethdb.AncientStore, blocks []*types.Block, receipts []types.Receipts, td *big.Int, sidecars []types.BlobSidecars) (int64, error) { - if len(blocks) == 0 { - return 0, nil - } - - // do some sanity check - if len(blocks) != len(sidecars) { - return 0, fmt.Errorf("the sidecars len is different with blocks, %v:%v", len(sidecars), len(blocks)) - } - if len(blocks) != len(receipts) { - return 0, fmt.Errorf("the receipts len is different with blocks, %v:%v", len(receipts), len(blocks)) - } - // try reset empty blob ancient table - if err := ResetEmptyBlobAncientTable(db, blocks[0].NumberU64()); err != nil { - return 0, err - } - - var ( - tdSum = new(big.Int).Set(td) - stReceipts []*types.ReceiptForStorage - ) - return db.ModifyAncients(func(op ethdb.AncientWriteOp) error { - for i, block := range blocks { - // Convert receipts to storage format and sum up total difficulty. - stReceipts = stReceipts[:0] - for _, receipt := range receipts[i] { - stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt)) - } - header := block.Header() - if i > 0 { - tdSum.Add(tdSum, header.Difficulty) - } - if err := writeAncientBlockWithSidecar(op, block, header, stReceipts, tdSum, sidecars[i]); err != nil { - return err - } - } - return nil - }) -} - // ReadBlobSidecarsRLP retrieves all the transaction blobs belonging to a block in RLP encoding. func ReadBlobSidecarsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { var data []byte @@ -983,8 +866,8 @@ func WriteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64, } } -// DeleteBlobSidecars removes all blob data associated with a block hash. -func DeleteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { +// deleteBlobSidecars removes all blob data associated with a block hash. +func deleteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { if err := db.Delete(blockBlobSidecarsKey(number, hash)); err != nil { log.Crit("Failed to delete block blobs", "err", err) } @@ -1007,25 +890,10 @@ func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *type if err := op.Append(ChainFreezerDifficultyTable, num, td); err != nil { return fmt.Errorf("can't append block %d total difficulty: %v", num, err) } - return nil -} - -func writeAncientSidecar(op ethdb.AncientWriteOp, num uint64, blobs types.BlobSidecars) error { - if err := op.Append(ChainFreezerBlobSidecarTable, num, blobs); err != nil { - return fmt.Errorf("can't append block %d blobs: %v", num, err) - } - return nil -} - -// writeAncientBlockWithSidecar writes entire block data into ancient store and returns the total written size. -// Attention: The caller must set blobs after cancun -func writeAncientBlockWithSidecar(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage, td *big.Int, sidecars types.BlobSidecars) error { - num := block.NumberU64() - if err := writeAncientBlock(op, block, header, receipts, td); err != nil { - return err - } - if err := writeAncientSidecar(op, num, sidecars); err != nil { - return err + if len(block.Sidecars()) > 0 { + if err := op.Append(ChainFreezerBlobSidecarTable, num, block.Sidecars()); err != nil { + return fmt.Errorf("can't append block %d blobs: %v", num, err) + } } return nil } @@ -1036,7 +904,7 @@ func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { DeleteHeader(db, hash, number) DeleteBody(db, hash, number) DeleteTd(db, hash, number) - DeleteBlobSidecars(db, hash, number) // it is safe to delete non-exist blob + deleteBlobSidecars(db, hash, number) // it is safe to delete non-exist blob } // DeleteBlockWithoutNumber removes all block data associated with a hash, except @@ -1046,7 +914,7 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number deleteHeaderWithoutNumber(db, hash, number) DeleteBody(db, hash, number) DeleteTd(db, hash, number) - DeleteBlobSidecars(db, hash, number) + deleteBlobSidecars(db, hash, number) } const badBlockToKeep = 10 diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index d969dd2fa2..7cced42469 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -469,7 +469,7 @@ func TestBlockBlobSidecarsStorage(t *testing.T) { } } - DeleteBlobSidecars(db, blkHash, 0) + deleteBlobSidecars(db, blkHash, 0) if bs := ReadRawBlobSidecars(db, blkHash, 0); len(bs) != 0 { t.Fatalf("deleted sidecars returned: %v", bs) } @@ -686,8 +686,10 @@ func BenchmarkWriteAncientBlocks(b *testing.B) { blocks := allBlocks[i : i+length] receipts := batchReceipts[:length] - sidecars := batchSidecars[:length] - writeSize, err := WriteAncientBlocksWithSidecars(db, blocks, receipts, td, sidecars) + for j := 0; j < length; j++ { + blocks[i+j].WithSidecars(batchSidecars[j]) + } + writeSize, err := WriteAncientBlocks(db, blocks, receipts, td) if err != nil { b.Fatal(err) } diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index 6f5e18743a..619ecbee24 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -339,7 +339,7 @@ func (f *chainFreezer) freezeRangeWithBlobs(nfdb *nofreezedb, number, limit uint return preHashes, err } - if err = ResetEmptyBlobAncientTable(f, cancunNumber); err != nil { + if err = TryResetEmptyBlobAncientTable(f, cancunNumber); err != nil { return preHashes, err } // freeze post cancun @@ -437,6 +437,6 @@ func isCancun(env *ethdb.FreezerEnv, num *big.Int, time uint64) bool { return env.ChainCfg.IsCancun(num, time) } -func ResetEmptyBlobAncientTable(db ethdb.AncientStore, next uint64) error { +func TryResetEmptyBlobAncientTable(db ethdb.AncientStore, next uint64) error { return db.ResetTable(ChainFreezerBlobSidecarTable, next, next, true) } diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index 33796c925f..6adfbbede7 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -455,18 +455,11 @@ func (p *BlockPruner) backUpOldDb(name string, cache, handles int, namespace str } // if there has blobs, it needs to back up too. blobs := rawdb.ReadRawBlobSidecars(chainDb, blockHash, blockNumber) - if blobs != nil { - // Write into new ancient_back db. - if _, err := rawdb.WriteAncientBlocksWithSidecars(frdbBack, []*types.Block{block}, []types.Receipts{receipts}, td, []types.BlobSidecars{blobs}); err != nil { - log.Error("failed to write new ancient", "error", err) - return err - } - } else { - // Write into new ancient_back db. - if _, err := rawdb.WriteAncientBlocks(frdbBack, []*types.Block{block}, []types.Receipts{receipts}, td); err != nil { - log.Error("failed to write new ancient", "error", err) - return err - } + block.WithSidecars(blobs) + // Write into new ancient_back db. + if _, err := rawdb.WriteAncientBlocks(frdbBack, []*types.Block{block}, []types.Receipts{receipts}, td); err != nil { + log.Error("failed to write new ancient", "error", err) + return err } // Print the log every 5s for better trace. if common.PrettyDuration(time.Since(start)) > common.PrettyDuration(5*time.Second) { diff --git a/params/config.go b/params/config.go index bc438b25f8..a3a0063670 100644 --- a/params/config.go +++ b/params/config.go @@ -918,6 +918,15 @@ func (c *ChainConfig) IsCancun(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.CancunTime, time) } +// IsOnCancun returns whether currentBlockTime is either equal to the Cancun fork time or greater firstly. +func (c *ChainConfig) IsOnCancun(currentBlockNumber *big.Int, lastBlockTime uint64, currentBlockTime uint64) bool { + lastBlockNumber := new(big.Int) + if currentBlockNumber.Cmp(big.NewInt(1)) >= 0 { + lastBlockNumber.Sub(currentBlockNumber, big.NewInt(1)) + } + return !c.IsCancun(lastBlockNumber, lastBlockTime) && c.IsCancun(currentBlockNumber, currentBlockTime) +} + // IsPrague returns whether num is either equal to the Prague fork time or greater. func (c *ChainConfig) IsPrague(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.PragueTime, time)