diff --git a/cmd/ronin/main.go b/cmd/ronin/main.go index 89d9383b09..4e4e983f5f 100644 --- a/cmd/ronin/main.go +++ b/cmd/ronin/main.go @@ -101,6 +101,7 @@ var ( utils.GCModeFlag, utils.SnapshotFlag, utils.TxLookupLimitFlag, + utils.TriesInMemoryFlag, utils.LightServeFlag, utils.LightIngressFlag, utils.LightEgressFlag, diff --git a/cmd/ronin/usage.go b/cmd/ronin/usage.go index 4449d64bc8..23dfa0ce55 100644 --- a/cmd/ronin/usage.go +++ b/cmd/ronin/usage.go @@ -50,6 +50,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.ExitWhenSyncedFlag, utils.GCModeFlag, utils.TxLookupLimitFlag, + utils.TriesInMemoryFlag, utils.EthStatsURLFlag, utils.IdentityFlag, utils.LightKDFFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 4e47dc47bc..76d024256b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -231,6 +231,11 @@ var ( Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain)", Value: ethconfig.Defaults.TxLookupLimit, } + TriesInMemoryFlag = cli.IntFlag{ + Name: "triesinmemory", + Usage: "The number of tries is kept in memory before pruning (default = 128)", + Value: 128, + } MonitorDoubleSign = cli.BoolFlag{ Name: "monitor.doublesign", Usage: "Enable double sign monitoring", @@ -1730,6 +1735,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheSnapshotFlag.Name) { cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100 } + cfg.TriesInMemory = ctx.GlobalInt(TriesInMemoryFlag.Name) if !ctx.GlobalBool(SnapshotFlag.Name) { // If snap-sync is requested, this flag is also required if cfg.SyncMode == downloader.SnapSync { diff --git a/core/blockchain.go b/core/blockchain.go index b51cef2489..3a9861850d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -94,7 +94,7 @@ const ( txLookupCacheLimit = 1024 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 - TriesInMemory = 128 + DefaultTriesInMemory = 128 dirtyAccountsCacheLimit = 32 internalTxsCacheLimit = 32 @@ -136,6 +136,7 @@ type CacheConfig struct { TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory Preimages bool // Whether to store preimage of trie key to the disk + TriesInMemory int // The number of tries is kept in memory before pruning SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it } @@ -235,6 +236,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par if cacheConfig == nil { cacheConfig = defaultCacheConfig } + if cacheConfig.TriesInMemory == 0 { + cacheConfig.TriesInMemory = DefaultTriesInMemory + } bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) receiptsCache, _ := lru.New(receiptsCacheLimit) @@ -933,7 +937,7 @@ func (bc *BlockChain) Stop() { if !bc.cacheConfig.TrieDirtyDisabled { triedb := bc.stateCache.TrieDB() - for _, offset := range []uint64{0, 1, TriesInMemory - 1} { + for _, offset := range []uint64{0, 1, uint64(bc.cacheConfig.TriesInMemory) - 1} { if number := bc.CurrentBlock().NumberU64(); number > offset { recent := bc.GetBlockByNumber(number - offset) @@ -1456,7 +1460,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive bc.triegc.Push(root, -int64(block.NumberU64())) - if current := block.NumberU64(); current > TriesInMemory { + triesInMemory := uint64(bc.cacheConfig.TriesInMemory) + if current := block.NumberU64(); current > triesInMemory { // If we exceeded our memory allowance, flush matured singleton nodes to disk var ( nodes, imgs = triedb.Size() @@ -1466,7 +1471,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. triedb.Cap(limit - ethdb.IdealBatchSize) } // Find the next state trie we need to commit - chosen := current - TriesInMemory + chosen := current - triesInMemory // If we exceeded out time allowance, flush an entire trie to disk if bc.gcproc > bc.cacheConfig.TrieTimeLimit { @@ -1478,8 +1483,13 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } else { // If we're exceeding limits but haven't reached a large enough memory gap, // warn the user that the system is becoming unstable. - if chosen < lastWrite+TriesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit { - log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/TriesInMemory) + if chosen < lastWrite+triesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit { + log.Info( + "State in memory for too long, committing", + "time", bc.gcproc, + "allowance", bc.cacheConfig.TrieTimeLimit, + "optimum", float64(chosen-lastWrite)/float64(triesInMemory), + ) } // Flush an entire trie and restart the counters triedb.Commit(header.Root, true, nil) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 04a6cac889..0b3244da6e 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1518,7 +1518,7 @@ func TestTrieForkGC(t *testing.T) { db := rawdb.NewMemoryDatabase() genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }, true) + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*DefaultTriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }, true) // Generate a bunch of fork blocks, each side forking from the canonical chain forks := make([]*types.Block, len(blocks)) @@ -1547,7 +1547,7 @@ func TestTrieForkGC(t *testing.T) { } } // Dereference all the recent tries and ensure no past trie is left in - for i := 0; i < TriesInMemory; i++ { + for i := 0; i < DefaultTriesInMemory; i++ { chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root()) chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root()) } @@ -1566,8 +1566,8 @@ func TestLargeReorgTrieGC(t *testing.T) { genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }, true) - original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }, true) - competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }, true) + original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*DefaultTriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }, true) + competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*DefaultTriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }, true) // Import the shared chain and the original canonical one diskdb := rawdb.NewMemoryDatabase() @@ -1602,7 +1602,7 @@ func TestLargeReorgTrieGC(t *testing.T) { if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil { t.Fatalf("failed to finalize competitor chain: %v", err) } - for i, block := range competitor[:len(competitor)-TriesInMemory] { + for i, block := range competitor[:len(competitor)-DefaultTriesInMemory] { if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil { t.Fatalf("competitor %d: competing chain state missing", i) } @@ -1758,7 +1758,7 @@ func TestLowDiffLongChain(t *testing.T) { // We must use a pretty long chain to ensure that the fork doesn't overtake us // until after at least 128 blocks post tip - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*TriesInMemory, func(i int, b *BlockGen) { + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*DefaultTriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) b.OffsetTime(-9) }, true) @@ -1776,7 +1776,7 @@ func TestLowDiffLongChain(t *testing.T) { } // Generate fork chain, starting from an early block parent := blocks[10] - fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*TriesInMemory, func(i int, b *BlockGen) { + fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*DefaultTriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }, true) @@ -1811,7 +1811,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) // Generate and import the canonical chain - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil, true) + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*DefaultTriesInMemory, nil, true) diskdb := rawdb.NewMemoryDatabase() (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) @@ -1822,9 +1822,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon t.Fatalf("block %d: failed to insert into chain: %v", n, err) } - lastPrunedIndex := len(blocks) - TriesInMemory - 1 + lastPrunedIndex := len(blocks) - DefaultTriesInMemory - 1 lastPrunedBlock := blocks[lastPrunedIndex] - firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory] + firstNonPrunedBlock := blocks[len(blocks)-DefaultTriesInMemory] // Verify pruning of lastPrunedBlock if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) { @@ -1841,7 +1841,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon // Generate fork chain, make it longer than canon parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock parent := blocks[parentIndex] - fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { + fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*DefaultTriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }, true) // Prepend the parent(s) @@ -2483,7 +2483,7 @@ func TestSideImportPrunedBlocks(t *testing.T) { genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db) // Generate and import the canonical chain - blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil, true) + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*DefaultTriesInMemory, nil, true) diskdb := rawdb.NewMemoryDatabase() (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil) @@ -2494,14 +2494,14 @@ func TestSideImportPrunedBlocks(t *testing.T) { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } - lastPrunedIndex := len(blocks) - TriesInMemory - 1 + lastPrunedIndex := len(blocks) - DefaultTriesInMemory - 1 lastPrunedBlock := blocks[lastPrunedIndex] // Verify pruning of lastPrunedBlock if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) { t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64()) } - firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory] + firstNonPrunedBlock := blocks[len(blocks)-DefaultTriesInMemory] // Verify firstNonPrunedBlock is not pruned if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) { t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64()) diff --git a/eth/backend.go b/eth/backend.go index 75e837941b..ed1132bcc2 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -196,6 +196,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { TrieTimeLimit: config.TrieTimeout, SnapshotLimit: config.SnapshotCache, Preimages: config.Preimages, + TriesInMemory: config.TriesInMemory, } ) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index e1d605afc2..b9a999e62b 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -92,11 +92,11 @@ var Defaults = Config{ BlockProduceLeftOver: 200 * time.Millisecond, BlockSizeReserve: 500000, }, - TxPool: core.DefaultTxPoolConfig, - RPCGasCap: 50000000, - RPCEVMTimeout: 5 * time.Second, - GPO: FullNodeGPO, - RPCTxFeeCap: 1, // 1 ether + TxPool: core.DefaultTxPoolConfig, + RPCGasCap: 50000000, + RPCEVMTimeout: 5 * time.Second, + GPO: FullNodeGPO, + RPCTxFeeCap: 1, // 1 ether } func init() { @@ -172,6 +172,7 @@ type Config struct { TrieTimeout time.Duration SnapshotCache int Preimages bool + TriesInMemory int // Mining options Miner miner.Config diff --git a/les/handler_test.go b/les/handler_test.go index 6b8849585b..ed4c527f40 100644 --- a/les/handler_test.go +++ b/les/handler_test.go @@ -316,7 +316,7 @@ func TestGetStaleCodeLes4(t *testing.T) { testGetStaleCode(t, 4) } func testGetStaleCode(t *testing.T, protocol int) { netconfig := testnetConfig{ - blocks: core.TriesInMemory + 4, + blocks: core.DefaultTriesInMemory + 4, protocol: protocol, nopruning: true, } @@ -430,7 +430,7 @@ func TestGetStaleProofLes4(t *testing.T) { testGetStaleProof(t, 4) } func testGetStaleProof(t *testing.T, protocol int) { netconfig := testnetConfig{ - blocks: core.TriesInMemory + 4, + blocks: core.DefaultTriesInMemory + 4, protocol: protocol, nopruning: true, } diff --git a/les/peer.go b/les/peer.go index c6c672942b..fe68b3eb71 100644 --- a/les/peer.go +++ b/les/peer.go @@ -1058,7 +1058,7 @@ func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, ge // If local ethereum node is running in archive mode, advertise ourselves we have // all version state data. Otherwise only recent state is available. - stateRecent := uint64(core.TriesInMemory - blockSafetyMargin) + stateRecent := uint64(core.DefaultTriesInMemory - blockSafetyMargin) if server.archiveMode { stateRecent = 0 } diff --git a/les/server_requests.go b/les/server_requests.go index bab5f733d5..121bd48ef9 100644 --- a/les/server_requests.go +++ b/les/server_requests.go @@ -297,7 +297,7 @@ func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) { // Refuse to search stale state data in the database since looking for // a non-exist key is kind of expensive. local := bc.CurrentHeader().Number.Uint64() - if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local { + if !backend.ArchiveMode() && header.Number.Uint64()+core.DefaultTriesInMemory <= local { p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) p.bumpInvalid() continue @@ -396,7 +396,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) { // Refuse to search stale state data in the database since looking for // a non-exist key is kind of expensive. local := bc.CurrentHeader().Number.Uint64() - if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local { + if !backend.ArchiveMode() && header.Number.Uint64()+core.DefaultTriesInMemory <= local { p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) p.bumpInvalid() continue diff --git a/les/test_helper.go b/les/test_helper.go index 21d0f191c9..3aafb9e055 100644 --- a/les/test_helper.go +++ b/les/test_helper.go @@ -375,7 +375,7 @@ func (p *testPeer) handshakeWithClient(t *testing.T, td *big.Int, head common.Ha sendList = sendList.add("serveHeaders", nil) sendList = sendList.add("serveChainSince", uint64(0)) sendList = sendList.add("serveStateSince", uint64(0)) - sendList = sendList.add("serveRecentState", uint64(core.TriesInMemory-4)) + sendList = sendList.add("serveRecentState", uint64(core.DefaultTriesInMemory-4)) sendList = sendList.add("txRelay", nil) sendList = sendList.add("flowControl/BL", testBufLimit) sendList = sendList.add("flowControl/MRR", testBufRecharge)