diff --git a/cmd/integration/commands/stages.go b/cmd/integration/commands/stages.go index 94ed2d53f76..12b24244537 100644 --- a/cmd/integration/commands/stages.go +++ b/cmd/integration/commands/stages.go @@ -1157,7 +1157,7 @@ func stageHashState(db kv.RwDB, ctx context.Context, logger log.Logger) error { } func stageLogIndex(db kv.RwDB, ctx context.Context, logger log.Logger) error { - dirs, pm, historyV3 := datadir.New(datadirCli), fromdb.PruneMode(db), kvcfg.HistoryV3.FromDB(db) + dirs, pm, historyV3, chainConfig := datadir.New(datadirCli), fromdb.PruneMode(db), kvcfg.HistoryV3.FromDB(db), fromdb.ChainConfig(db) if historyV3 { return fmt.Errorf("this stage is disable in --history.v3=true") } @@ -1187,7 +1187,7 @@ func stageLogIndex(db kv.RwDB, ctx context.Context, logger log.Logger) error { logger.Info("Stage exec", "progress", execAt) logger.Info("Stage", "name", s.ID, "progress", s.BlockNumber) - cfg := stagedsync.StageLogIndexCfg(db, pm, dirs.Tmp) + cfg := stagedsync.StageLogIndexCfg(db, pm, dirs.Tmp, chainConfig.NoPruneContracts) if unwind > 0 { u := sync.NewUnwindState(stages.LogIndex, s.BlockNumber-unwind, s.BlockNumber) err = stagedsync.UnwindLogIndex(u, s, tx, cfg, ctx) diff --git a/erigon-lib/chain/chain_config.go b/erigon-lib/chain/chain_config.go index 4d2069ee396..3573a16d917 100644 --- a/erigon-lib/chain/chain_config.go +++ b/erigon-lib/chain/chain_config.go @@ -84,6 +84,11 @@ type Config struct { Bor BorConfig `json:"-"` BorJSON json.RawMessage `json:"bor,omitempty"` + + // For not pruning the logs of these contracts + // For depsoit contract logs are needed by CL to validate/produce blocks. + // All logs should be available to a validating node through eth_getLogs + NoPruneContracts map[common.Address]bool `json:"noPruneContracts,omitempty"` } type BorConfig interface { @@ -97,7 +102,7 @@ type BorConfig interface { func (c *Config) String() string { engine := c.getEngine() - return fmt.Sprintf("{ChainID: %v, Homestead: %v, DAO: %v, Tangerine Whistle: %v, Spurious Dragon: %v, Byzantium: %v, Constantinople: %v, Petersburg: %v, Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, Gray Glacier: %v, Terminal Total Difficulty: %v, Merge Netsplit: %v, Shanghai: %v, Cancun: %v, Prague: %v, Engine: %v}", + return fmt.Sprintf("{ChainID: %v, Homestead: %v, DAO: %v, Tangerine Whistle: %v, Spurious Dragon: %v, Byzantium: %v, Constantinople: %v, Petersburg: %v, Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, Gray Glacier: %v, Terminal Total Difficulty: %v, Merge Netsplit: %v, Shanghai: %v, Cancun: %v, Prague: %v, Engine: %v, NoPruneContracts: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -118,6 +123,7 @@ func (c *Config) String() string { c.CancunTime, c.PragueTime, engine, + c.NoPruneContracts, ) } diff --git a/eth/stagedsync/stage_execute.go b/eth/stagedsync/stage_execute.go index 072de8e5b5d..269f0566b27 100644 --- a/eth/stagedsync/stage_execute.go +++ b/eth/stagedsync/stage_execute.go @@ -178,7 +178,8 @@ func executeBlock( receipts = execRs.Receipts stateSyncReceipt = execRs.StateSyncReceipt - if writeReceipts { + // If writeReceipts is false here, append the not to be pruned receipts anyways + if writeReceipts || gatherNoPruneReceipts(&receipts, cfg.chainConfig) { if err = rawdb.AppendReceipts(tx, blockNum, receipts); err != nil { return err } @@ -201,6 +202,25 @@ func executeBlock( return nil } +// Filters out and keeps receipts of contracts that may be needed by CL, such as deposit contrac, +// The list of contracts to filter is config-specified +func gatherNoPruneReceipts(receipts *types.Receipts, chainCfg *chain.Config) bool { + cr := types.Receipts{} + for _, r := range *receipts { + for _, l := range r.Logs { + if chainCfg.NoPruneContracts[l.Address] { + cr = append(cr, r) + break + } + } + } + receipts = &cr + if receipts.Len() > 0 { + return true + } + return false +} + func newStateReaderWriter( batch kv.StatelessRwTx, tx kv.RwTx, @@ -922,10 +942,11 @@ func PruneExecutionStage(s *PruneState, tx kv.RwTx, cfg ExecuteBlockCfg, ctx con if err = rawdb.PruneTable(tx, kv.BorReceipts, cfg.prune.Receipts.PruneTo(s.ForwardProgress), ctx, math.MaxUint32); err != nil { return err } + // EDIT: Don't prune yet, let LogIndex stage take care of it // LogIndex.Prune will read everything what not pruned here - if err = rawdb.PruneTable(tx, kv.Log, cfg.prune.Receipts.PruneTo(s.ForwardProgress), ctx, math.MaxInt32); err != nil { - return err - } + // if err = rawdb.PruneTable(tx, kv.Log, cfg.prune.Receipts.PruneTo(s.ForwardProgress), ctx, math.MaxInt32); err != nil { + // return err + // } } if cfg.prune.CallTraces.Enabled() { if err = rawdb.PruneTableDupSort(tx, kv.CallTraceSet, logPrefix, cfg.prune.CallTraces.PruneTo(s.ForwardProgress), logEvery, ctx); err != nil { diff --git a/eth/stagedsync/stage_log_index.go b/eth/stagedsync/stage_log_index.go index 1c7e3f01208..2740e969529 100644 --- a/eth/stagedsync/stage_log_index.go +++ b/eth/stagedsync/stage_log_index.go @@ -31,20 +31,22 @@ const ( ) type LogIndexCfg struct { - tmpdir string - db kv.RwDB - prune prune.Mode - bufLimit datasize.ByteSize - flushEvery time.Duration + tmpdir string + db kv.RwDB + prune prune.Mode + bufLimit datasize.ByteSize + flushEvery time.Duration + noPruneContracts map[libcommon.Address]bool } -func StageLogIndexCfg(db kv.RwDB, prune prune.Mode, tmpDir string) LogIndexCfg { +func StageLogIndexCfg(db kv.RwDB, prune prune.Mode, tmpDir string, noPruneContracts map[libcommon.Address]bool) LogIndexCfg { return LogIndexCfg{ - db: db, - prune: prune, - bufLimit: bitmapsBufLimit, - flushEvery: bitmapsFlushEvery, - tmpdir: tmpDir, + db: db, + prune: prune, + bufLimit: bitmapsBufLimit, + flushEvery: bitmapsFlushEvery, + tmpdir: tmpDir, + noPruneContracts: noPruneContracts, } } @@ -80,14 +82,14 @@ func SpawnLogIndex(s *StageState, tx kv.RwTx, cfg LogIndexCfg, ctx context.Conte } startBlock := s.BlockNumber - pruneTo := cfg.prune.Receipts.PruneTo(endBlock) - if startBlock < pruneTo { - startBlock = pruneTo - } + pruneTo := cfg.prune.Receipts.PruneTo(endBlock) //endBlock - prune.r.older + // if startBlock < pruneTo { + // startBlock = pruneTo + // } if startBlock > 0 { startBlock++ } - if err = promoteLogIndex(logPrefix, tx, startBlock, endBlock, cfg, ctx, logger); err != nil { + if err = promoteLogIndex(logPrefix, tx, startBlock, endBlock, pruneTo, cfg, ctx, logger); err != nil { return err } if err = s.Update(tx, endBlock); err != nil { @@ -103,7 +105,7 @@ func SpawnLogIndex(s *StageState, tx kv.RwTx, cfg LogIndexCfg, ctx context.Conte return nil } -func promoteLogIndex(logPrefix string, tx kv.RwTx, start uint64, endBlock uint64, cfg LogIndexCfg, ctx context.Context, logger log.Logger) error { +func promoteLogIndex(logPrefix string, tx kv.RwTx, start uint64, endBlock uint64, pruneBlock uint64, cfg LogIndexCfg, ctx context.Context, logger log.Logger) error { quit := ctx.Done() logEvery := time.NewTicker(30 * time.Second) defer logEvery.Stop() @@ -175,6 +177,9 @@ func promoteLogIndex(logPrefix string, tx kv.RwTx, start uint64, endBlock uint64 } for _, l := range ll { + if l.BlockNumber < pruneBlock && cfg.noPruneContracts != nil && !cfg.noPruneContracts[l.Address] { + continue + } for _, topic := range l.Topics { topicStr := string(topic.Bytes()) m, ok := topics[topicStr] @@ -405,7 +410,7 @@ func PruneLogIndex(s *PruneState, tx kv.RwTx, cfg LogIndexCfg, ctx context.Conte } pruneTo := cfg.prune.Receipts.PruneTo(s.ForwardProgress) - if err = pruneLogIndex(logPrefix, tx, cfg.tmpdir, pruneTo, ctx, logger); err != nil { + if err = pruneLogIndex(logPrefix, tx, cfg.tmpdir, pruneTo, ctx, logger, cfg.noPruneContracts); err != nil { return err } if err = s.Done(tx); err != nil { @@ -420,7 +425,7 @@ func PruneLogIndex(s *PruneState, tx kv.RwTx, cfg LogIndexCfg, ctx context.Conte return nil } -func pruneLogIndex(logPrefix string, tx kv.RwTx, tmpDir string, pruneTo uint64, ctx context.Context, logger log.Logger) error { +func pruneLogIndex(logPrefix string, tx kv.RwTx, tmpDir string, pruneTo uint64, ctx context.Context, logger log.Logger, noPruneContracts map[libcommon.Address]bool) error { logEvery := time.NewTicker(logInterval) defer logEvery.Stop() @@ -461,6 +466,9 @@ func pruneLogIndex(logPrefix string, tx kv.RwTx, tmpDir string, pruneTo uint64, } for _, l := range logs { + if noPruneContracts != nil && noPruneContracts[l.Address] { + continue + } for _, topic := range l.Topics { if err := topics.Collect(topic.Bytes(), nil); err != nil { return err diff --git a/eth/stagedsync/stage_log_index_test.go b/eth/stagedsync/stage_log_index_test.go index cde5ae2c307..b3a4180cf09 100644 --- a/eth/stagedsync/stage_log_index_test.go +++ b/eth/stagedsync/stage_log_index_test.go @@ -99,12 +99,12 @@ func TestPromoteLogIndex(t *testing.T) { expectAddrs, expectTopics := genReceipts(t, tx, 100) - cfg := StageLogIndexCfg(nil, prune.DefaultMode, "") + cfg := StageLogIndexCfg(nil, prune.DefaultMode, "", nil) cfgCopy := cfg cfgCopy.bufLimit = 10 cfgCopy.flushEvery = time.Nanosecond - err := promoteLogIndex("logPrefix", tx, 0, 0, cfgCopy, ctx, logger) + err := promoteLogIndex("logPrefix", tx, 0, 0, 0, cfgCopy, ctx, logger) require.NoError(err) // Check indices GetCardinality (in how many blocks they meet) @@ -127,15 +127,15 @@ func TestPruneLogIndex(t *testing.T) { _, _ = genReceipts(t, tx, 100) - cfg := StageLogIndexCfg(nil, prune.DefaultMode, "") + cfg := StageLogIndexCfg(nil, prune.DefaultMode, "", nil) cfgCopy := cfg cfgCopy.bufLimit = 10 cfgCopy.flushEvery = time.Nanosecond - err := promoteLogIndex("logPrefix", tx, 0, 0, cfgCopy, ctx, logger) + err := promoteLogIndex("logPrefix", tx, 0, 0, 0, cfgCopy, ctx, logger) require.NoError(err) // Mode test - err = pruneLogIndex("", tx, tmpDir, 50, ctx, logger) + err = pruneLogIndex("", tx, tmpDir, 50, ctx, logger, nil) require.NoError(err) { @@ -167,15 +167,15 @@ func TestUnwindLogIndex(t *testing.T) { expectAddrs, expectTopics := genReceipts(t, tx, 100) - cfg := StageLogIndexCfg(nil, prune.DefaultMode, "") + cfg := StageLogIndexCfg(nil, prune.DefaultMode, "", nil) cfgCopy := cfg cfgCopy.bufLimit = 10 cfgCopy.flushEvery = time.Nanosecond - err := promoteLogIndex("logPrefix", tx, 0, 0, cfgCopy, ctx, logger) + err := promoteLogIndex("logPrefix", tx, 0, 0, 0, cfgCopy, ctx, logger) require.NoError(err) // Mode test - err = pruneLogIndex("", tx, tmpDir, 50, ctx, logger) + err = pruneLogIndex("", tx, tmpDir, 50, ctx, logger, nil) require.NoError(err) // Unwind test diff --git a/ethdb/prune/storage_mode.go b/ethdb/prune/storage_mode.go index cde01f6cdac..4864f6e3367 100644 --- a/ethdb/prune/storage_mode.go +++ b/ethdb/prune/storage_mode.go @@ -23,14 +23,6 @@ var DefaultMode = Mode{ Experiments: Experiments{}, // all off } -var ( - mainnetDepositContractBlock uint64 = 11052984 - sepoliaDepositContractBlock uint64 = 1273020 - goerliDepositContractBlock uint64 = 4367322 - gnosisDepositContractBlock uint64 = 19475089 - chiadoDepositContractBlock uint64 = 155530 -) - type Experiments struct { } @@ -55,8 +47,6 @@ func FromCli(chainId uint64, flags string, exactHistory, exactReceipts, exactTxI } } - pruneBlockBefore := pruneBlockDefault(chainId) - if exactHistory > 0 { mode.History = Distance(exactHistory) } @@ -74,16 +64,7 @@ func FromCli(chainId uint64, flags string, exactHistory, exactReceipts, exactTxI mode.History = Before(beforeH) } if beforeR > 0 { - if pruneBlockBefore != 0 { - log.Warn("specifying prune.before.r might break CL compatibility") - if beforeR > pruneBlockBefore { - log.Warn("the specified prune.before.r block number is higher than the deposit contract contract block number", "highest block number", pruneBlockBefore) - } - } mode.Receipts = Before(beforeR) - } else if exactReceipts == 0 && mode.Receipts.Enabled() && pruneBlockBefore != 0 { - // Default --prune=r to pruning receipts before the Beacon Chain genesis - mode.Receipts = Before(pruneBlockBefore) } if beforeT > 0 { mode.TxIndex = Before(beforeT) @@ -103,23 +84,6 @@ func FromCli(chainId uint64, flags string, exactHistory, exactReceipts, exactTxI return mode, nil } -func pruneBlockDefault(chainId uint64) uint64 { - switch chainId { - case 1 /* mainnet */ : - return mainnetDepositContractBlock - case 11155111 /* sepolia */ : - return sepoliaDepositContractBlock - case 5 /* goerli */ : - return goerliDepositContractBlock - case 10200 /* chiado */ : - return chiadoDepositContractBlock - case 100 /* gnosis */ : - return gnosisDepositContractBlock - } - - return 0 -} - func Get(db kv.Getter) (Mode, error) { prune := DefaultMode prune.Initialised = true diff --git a/params/chainspecs/chiado.json b/params/chainspecs/chiado.json index cc642d266f2..7ed187fc4d4 100644 --- a/params/chainspecs/chiado.json +++ b/params/chainspecs/chiado.json @@ -22,6 +22,9 @@ "maxBlobGasPerBlock": 262144, "targetBlobGasPerBlock": 131072, "blobGasPriceUpdateFraction": 1112826, + "noPruneContracts": { + "0xb97036A26259B7147018913bD58a774cf91acf25": true + }, "aura": { "stepDuration": 5, "blockReward": 0, diff --git a/params/chainspecs/gnosis.json b/params/chainspecs/gnosis.json index 84f2ac67909..8a4756514d5 100644 --- a/params/chainspecs/gnosis.json +++ b/params/chainspecs/gnosis.json @@ -22,6 +22,9 @@ "maxBlobGasPerBlock": 262144, "targetBlobGasPerBlock": 131072, "blobGasPriceUpdateFraction": 1112826, + "noPruneContracts": { + "0x0B98057eA310F4d31F2a452B414647007d1645d9": true + }, "aura": { "stepDuration": 5, "blockReward": 0, diff --git a/params/chainspecs/goerli.json b/params/chainspecs/goerli.json index 4bf04baa184..b419c2c2ad2 100644 --- a/params/chainspecs/goerli.json +++ b/params/chainspecs/goerli.json @@ -18,5 +18,8 @@ "clique": { "period": 15, "epoch": 30000 + }, + "noPruneContracts": { + "0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b": true } } diff --git a/params/chainspecs/holesky.json b/params/chainspecs/holesky.json index e582e406008..de44186a888 100644 --- a/params/chainspecs/holesky.json +++ b/params/chainspecs/holesky.json @@ -14,5 +14,8 @@ "terminalTotalDifficulty": 0, "terminalTotalDifficultyPassed": true, "shanghaiTime": 1696000704, - "cancunTime": 1707305664 + "cancunTime": 1707305664, + "noPruneContracts": { + "0x4242424242424242424242424242424242424242": true + } } \ No newline at end of file diff --git a/params/chainspecs/mainnet.json b/params/chainspecs/mainnet.json index a47bf7f418c..91ef377d35d 100644 --- a/params/chainspecs/mainnet.json +++ b/params/chainspecs/mainnet.json @@ -19,5 +19,8 @@ "terminalTotalDifficultyPassed": true, "shanghaiTime": 1681338455, "cancunTime": 1710338135, - "ethash": {} + "ethash": {}, + "noPruneContracts": { + "0x00000000219ab540356cBB839Cbe05303d7705Fa": true + } } diff --git a/params/chainspecs/sepolia.json b/params/chainspecs/sepolia.json index 9fe80a63fbe..c328e70b6e0 100644 --- a/params/chainspecs/sepolia.json +++ b/params/chainspecs/sepolia.json @@ -17,5 +17,8 @@ "mergeNetsplitBlock": 1735371, "shanghaiTime": 1677557088, "cancunTime": 1706655072, - "ethash": {} + "ethash": {}, + "noPruneContracts": { + "0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D": true + } } diff --git a/turbo/jsonrpc/eth_receipts.go b/turbo/jsonrpc/eth_receipts.go index e0f14b790f0..6d5f60c356f 100644 --- a/turbo/jsonrpc/eth_receipts.go +++ b/turbo/jsonrpc/eth_receipts.go @@ -162,7 +162,6 @@ func (api *APIImpl) GetLogs(ctx context.Context, crit filters.FilterCriteria) (t if api.historyV3(tx) { return api.getLogsV3(ctx, tx.(kv.TemporalTx), begin, end, crit) } - blockNumbers := bitmapdb.NewBitmap() defer bitmapdb.ReturnToPool(blockNumbers) if err := applyFilters(blockNumbers, tx, begin, end, crit); err != nil { diff --git a/turbo/stages/mock/mock_sentry.go b/turbo/stages/mock/mock_sentry.go index ff3a25ac4aa..6b24f6e798e 100644 --- a/turbo/stages/mock/mock_sentry.go +++ b/turbo/stages/mock/mock_sentry.go @@ -463,7 +463,7 @@ func MockWithEverything(tb testing.TB, gspec *types.Genesis, key *ecdsa.PrivateK stagedsync.StageHashStateCfg(mock.DB, mock.Dirs, cfg.HistoryV3), stagedsync.StageTrieCfg(mock.DB, checkStateRoot, true, false, dirs.Tmp, mock.BlockReader, mock.sentriesClient.Hd, cfg.HistoryV3, mock.agg), stagedsync.StageHistoryCfg(mock.DB, prune, dirs.Tmp), - stagedsync.StageLogIndexCfg(mock.DB, prune, dirs.Tmp), + stagedsync.StageLogIndexCfg(mock.DB, prune, dirs.Tmp, nil), stagedsync.StageCallTracesCfg(mock.DB, prune, 0, dirs.Tmp), stagedsync.StageTxLookupCfg(mock.DB, prune, dirs.Tmp, mock.ChainConfig.Bor, mock.BlockReader), stagedsync.StageFinishCfg(mock.DB, dirs.Tmp, forkValidator), diff --git a/turbo/stages/stageloop.go b/turbo/stages/stageloop.go index 35aab454bcd..ca7e3afb6f6 100644 --- a/turbo/stages/stageloop.go +++ b/turbo/stages/stageloop.go @@ -537,7 +537,7 @@ func NewDefaultStages(ctx context.Context, stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3), stagedsync.StageTrieCfg(db, true, true, false, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg), stagedsync.StageHistoryCfg(db, cfg.Prune, dirs.Tmp), - stagedsync.StageLogIndexCfg(db, cfg.Prune, dirs.Tmp), + stagedsync.StageLogIndexCfg(db, cfg.Prune, dirs.Tmp, cfg.Genesis.Config.NoPruneContracts), stagedsync.StageCallTracesCfg(db, cfg.Prune, 0, dirs.Tmp), stagedsync.StageTxLookupCfg(db, cfg.Prune, dirs.Tmp, controlServer.ChainConfig.Bor, blockReader), stagedsync.StageFinishCfg(db, dirs.Tmp, forkValidator), @@ -611,7 +611,7 @@ func NewPipelineStages(ctx context.Context, stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3), stagedsync.StageTrieCfg(db, checkStateRoot, true, false, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg), stagedsync.StageHistoryCfg(db, cfg.Prune, dirs.Tmp), - stagedsync.StageLogIndexCfg(db, cfg.Prune, dirs.Tmp), + stagedsync.StageLogIndexCfg(db, cfg.Prune, dirs.Tmp, cfg.Genesis.Config.NoPruneContracts), stagedsync.StageCallTracesCfg(db, cfg.Prune, 0, dirs.Tmp), stagedsync.StageTxLookupCfg(db, cfg.Prune, dirs.Tmp, controlServer.ChainConfig.Bor, blockReader), stagedsync.StageFinishCfg(db, dirs.Tmp, forkValidator), @@ -647,7 +647,7 @@ func NewPipelineStages(ctx context.Context, stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3), stagedsync.StageTrieCfg(db, checkStateRoot, true, false, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg), stagedsync.StageHistoryCfg(db, cfg.Prune, dirs.Tmp), - stagedsync.StageLogIndexCfg(db, cfg.Prune, dirs.Tmp), + stagedsync.StageLogIndexCfg(db, cfg.Prune, dirs.Tmp, cfg.Genesis.Config.NoPruneContracts), stagedsync.StageCallTracesCfg(db, cfg.Prune, 0, dirs.Tmp), stagedsync.StageTxLookupCfg(db, cfg.Prune, dirs.Tmp, controlServer.ChainConfig.Bor, blockReader), stagedsync.StageFinishCfg(db, dirs.Tmp, forkValidator),