github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/trie/sync_bloom.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package trie 18 19 import ( 20 "encoding/binary" 21 "fmt" 22 "sync" 23 "sync/atomic" 24 "time" 25 26 bloomfilter "github.com/holiman/bloomfilter/v2" 27 28 "github.com/scroll-tech/go-ethereum/common" 29 "github.com/scroll-tech/go-ethereum/core/rawdb" 30 "github.com/scroll-tech/go-ethereum/ethdb" 31 "github.com/scroll-tech/go-ethereum/log" 32 "github.com/scroll-tech/go-ethereum/metrics" 33 ) 34 35 var ( 36 bloomAddMeter = metrics.NewRegisteredMeter("trie/bloom/add", nil) 37 bloomLoadMeter = metrics.NewRegisteredMeter("trie/bloom/load", nil) 38 bloomTestMeter = metrics.NewRegisteredMeter("trie/bloom/test", nil) 39 bloomMissMeter = metrics.NewRegisteredMeter("trie/bloom/miss", nil) 40 bloomFaultMeter = metrics.NewRegisteredMeter("trie/bloom/fault", nil) 41 bloomErrorGauge = metrics.NewRegisteredGauge("trie/bloom/error", nil) 42 ) 43 44 // SyncBloom is a bloom filter used during fast sync to quickly decide if a trie 45 // node or contract code already exists on disk or not. It self populates from the 46 // provided disk database on creation in a background thread and will only start 47 // returning live results once that's finished. 48 type SyncBloom struct { 49 bloom *bloomfilter.Filter 50 inited uint32 51 closer sync.Once 52 closed uint32 53 pend sync.WaitGroup 54 closeCh chan struct{} 55 } 56 57 // NewSyncBloom creates a new bloom filter of the given size (in megabytes) and 58 // initializes it from the database. The bloom is hard coded to use 3 filters. 59 func NewSyncBloom(memory uint64, database ethdb.Iteratee) *SyncBloom { 60 // Create the bloom filter to track known trie nodes 61 bloom, err := bloomfilter.New(memory*1024*1024*8, 4) 62 if err != nil { 63 panic(fmt.Sprintf("failed to create bloom: %v", err)) 64 } 65 log.Info("Allocated fast sync bloom", "size", common.StorageSize(memory*1024*1024)) 66 67 // Assemble the fast sync bloom and init it from previous sessions 68 b := &SyncBloom{ 69 bloom: bloom, 70 closeCh: make(chan struct{}), 71 } 72 b.pend.Add(2) 73 go func() { 74 defer b.pend.Done() 75 b.init(database) 76 }() 77 go func() { 78 defer b.pend.Done() 79 b.meter() 80 }() 81 return b 82 } 83 84 // init iterates over the database, pushing every trie hash into the bloom filter. 85 func (b *SyncBloom) init(database ethdb.Iteratee) { 86 // Iterate over the database, but restart every now and again to avoid holding 87 // a persistent snapshot since fast sync can push a ton of data concurrently, 88 // bloating the disk. 89 // 90 // Note, this is fine, because everything inserted into leveldb by fast sync is 91 // also pushed into the bloom directly, so we're not missing anything when the 92 // iterator is swapped out for a new one. 93 it := database.NewIterator(nil, nil) 94 95 var ( 96 start = time.Now() 97 swap = time.Now() 98 ) 99 for it.Next() && atomic.LoadUint32(&b.closed) == 0 { 100 // If the database entry is a trie node, add it to the bloom 101 key := it.Key() 102 if len(key) == common.HashLength { 103 b.bloom.AddHash(binary.BigEndian.Uint64(key)) 104 bloomLoadMeter.Mark(1) 105 } else if ok, hash := rawdb.IsCodeKey(key); ok { 106 // If the database entry is a contract code, add it to the bloom 107 b.bloom.AddHash(binary.BigEndian.Uint64(hash)) 108 bloomLoadMeter.Mark(1) 109 } 110 // If enough time elapsed since the last iterator swap, restart 111 if time.Since(swap) > 8*time.Second { 112 key := common.CopyBytes(it.Key()) 113 114 it.Release() 115 it = database.NewIterator(nil, key) 116 117 log.Info("Initializing state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability(), "elapsed", common.PrettyDuration(time.Since(start))) 118 swap = time.Now() 119 } 120 } 121 it.Release() 122 123 // Mark the bloom filter inited and return 124 log.Info("Initialized state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability(), "elapsed", common.PrettyDuration(time.Since(start))) 125 atomic.StoreUint32(&b.inited, 1) 126 } 127 128 // meter periodically recalculates the false positive error rate of the bloom 129 // filter and reports it in a metric. 130 func (b *SyncBloom) meter() { 131 // check every second 132 tick := time.NewTicker(1 * time.Second) 133 defer tick.Stop() 134 135 for { 136 select { 137 case <-tick.C: 138 // Report the current error ration. No floats, lame, scale it up. 139 bloomErrorGauge.Update(int64(b.bloom.FalsePosititveProbability() * 100000)) 140 case <-b.closeCh: 141 return 142 } 143 } 144 } 145 146 // Close terminates any background initializer still running and releases all the 147 // memory allocated for the bloom. 148 func (b *SyncBloom) Close() error { 149 b.closer.Do(func() { 150 // Ensure the initializer is stopped 151 atomic.StoreUint32(&b.closed, 1) 152 close(b.closeCh) 153 b.pend.Wait() 154 155 // Wipe the bloom, but mark it "uninited" just in case someone attempts an access 156 log.Info("Deallocated state bloom", "items", b.bloom.N(), "errorrate", b.bloom.FalsePosititveProbability()) 157 158 atomic.StoreUint32(&b.inited, 0) 159 b.bloom = nil 160 }) 161 return nil 162 } 163 164 // Add inserts a new trie node hash into the bloom filter. 165 func (b *SyncBloom) Add(hash []byte) { 166 if atomic.LoadUint32(&b.closed) == 1 { 167 return 168 } 169 b.bloom.AddHash(binary.BigEndian.Uint64(hash)) 170 bloomAddMeter.Mark(1) 171 } 172 173 // Contains tests if the bloom filter contains the given hash: 174 // - false: the bloom definitely does not contain hash 175 // - true: the bloom maybe contains hash 176 // 177 // While the bloom is being initialized, any query will return true. 178 func (b *SyncBloom) Contains(hash []byte) bool { 179 bloomTestMeter.Mark(1) 180 if atomic.LoadUint32(&b.inited) == 0 { 181 // We didn't load all the trie nodes from the previous run of Geth yet. As 182 // such, we can't say for sure if a hash is not present for anything. Until 183 // the init is done, we're faking "possible presence" for everything. 184 return true 185 } 186 // Bloom initialized, check the real one and report any successful misses 187 maybe := b.bloom.ContainsHash(binary.BigEndian.Uint64(hash)) 188 if !maybe { 189 bloomMissMeter.Mark(1) 190 } 191 return maybe 192 }