github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/chain/core/chain_indexer.go (about) 1 package core 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "sync" 7 "sync/atomic" 8 "time" 9 10 "github.com/neatio-net/neatio/chain/core/rawdb" 11 12 "github.com/neatio-net/neatio/chain/core/types" 13 "github.com/neatio-net/neatio/chain/log" 14 "github.com/neatio-net/neatio/neatdb" 15 "github.com/neatio-net/neatio/utilities/common" 16 "github.com/neatio-net/neatio/utilities/event" 17 ) 18 19 type ChainIndexerBackend interface { 20 Reset(section uint64, prevHead common.Hash) error 21 22 Process(header *types.Header) 23 24 Commit() error 25 } 26 27 type ChainIndexerChain interface { 28 CurrentHeader() *types.Header 29 30 SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription 31 } 32 33 type ChainIndexer struct { 34 chainDb neatdb.Database 35 indexDb neatdb.Database 36 backend ChainIndexerBackend 37 sideren []*ChainIndexer 38 39 active uint32 40 update chan struct{} 41 quit chan chan error 42 43 sectionSize uint64 44 confirmsReq uint64 45 46 storedSections uint64 47 knownSections uint64 48 cascadedHead uint64 49 50 throttling time.Duration 51 52 log log.Logger 53 lock sync.RWMutex 54 } 55 56 func NewChainIndexer(chainDb, indexDb neatdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer { 57 c := &ChainIndexer{ 58 chainDb: chainDb, 59 indexDb: indexDb, 60 backend: backend, 61 update: make(chan struct{}, 1), 62 quit: make(chan chan error), 63 sectionSize: section, 64 confirmsReq: confirm, 65 throttling: throttling, 66 log: log.New("type", kind), 67 } 68 69 c.loadValidSections() 70 go c.updateLoop() 71 72 return c 73 } 74 75 func (c *ChainIndexer) AddKnownSectionHead(section uint64, shead common.Hash) { 76 c.lock.Lock() 77 defer c.lock.Unlock() 78 79 if section < c.storedSections { 80 return 81 } 82 c.setSectionHead(section, shead) 83 c.setValidSections(section + 1) 84 } 85 86 func (c *ChainIndexer) Start(chain ChainIndexerChain) { 87 events := make(chan ChainEvent, 10) 88 sub := chain.SubscribeChainEvent(events) 89 90 go c.eventLoop(chain.CurrentHeader(), events, sub) 91 } 92 93 func (c *ChainIndexer) Close() error { 94 var errs []error 95 96 errc := make(chan error) 97 c.quit <- errc 98 if err := <-errc; err != nil { 99 errs = append(errs, err) 100 } 101 102 if atomic.LoadUint32(&c.active) != 0 { 103 c.quit <- errc 104 if err := <-errc; err != nil { 105 errs = append(errs, err) 106 } 107 } 108 109 for _, side := range c.sideren { 110 if err := side.Close(); err != nil { 111 errs = append(errs, err) 112 } 113 } 114 115 switch { 116 case len(errs) == 0: 117 return nil 118 119 case len(errs) == 1: 120 return errs[0] 121 122 default: 123 return fmt.Errorf("%v", errs) 124 } 125 } 126 127 func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainEvent, sub event.Subscription) { 128 129 atomic.StoreUint32(&c.active, 1) 130 131 defer sub.Unsubscribe() 132 133 c.newHead(currentHeader.Number.Uint64(), false) 134 135 var ( 136 prevHeader = currentHeader 137 prevHash = currentHeader.Hash() 138 ) 139 for { 140 select { 141 case errc := <-c.quit: 142 143 errc <- nil 144 return 145 146 case ev, ok := <-events: 147 148 if !ok { 149 errc := <-c.quit 150 errc <- nil 151 return 152 } 153 header := ev.Block.Header() 154 if header.ParentHash != prevHash { 155 156 if rawdb.ReadCanonicalHash(c.chainDb, prevHeader.Number.Uint64()) != prevHash { 157 if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, header); h != nil { 158 c.newHead(h.Number.Uint64(), true) 159 } 160 } 161 } 162 c.newHead(header.Number.Uint64(), false) 163 164 prevHeader, prevHash = header, header.Hash() 165 } 166 } 167 } 168 169 func (c *ChainIndexer) newHead(head uint64, reorg bool) { 170 c.lock.Lock() 171 defer c.lock.Unlock() 172 173 if reorg { 174 175 changed := head / c.sectionSize 176 if changed < c.knownSections { 177 c.knownSections = changed 178 } 179 180 if changed < c.storedSections { 181 c.setValidSections(changed) 182 } 183 184 head = changed * c.sectionSize 185 186 if head < c.cascadedHead { 187 c.cascadedHead = head 188 for _, side := range c.sideren { 189 side.newHead(c.cascadedHead, true) 190 } 191 } 192 return 193 } 194 195 var sections uint64 196 if head >= c.confirmsReq { 197 sections = (head + 1 - c.confirmsReq) / c.sectionSize 198 if sections > c.knownSections { 199 c.knownSections = sections 200 201 select { 202 case c.update <- struct{}{}: 203 default: 204 } 205 } 206 } 207 } 208 209 func (c *ChainIndexer) updateLoop() { 210 var ( 211 updating bool 212 updated time.Time 213 ) 214 215 for { 216 select { 217 case errc := <-c.quit: 218 219 errc <- nil 220 return 221 222 case <-c.update: 223 224 c.lock.Lock() 225 if c.knownSections > c.storedSections { 226 227 if time.Since(updated) > 8*time.Second { 228 if c.knownSections > c.storedSections+1 { 229 updating = true 230 c.log.Info("Upgrading chain index", "percentage", c.storedSections*100/c.knownSections) 231 } 232 updated = time.Now() 233 } 234 235 section := c.storedSections 236 var oldHead common.Hash 237 if section > 0 { 238 oldHead = c.SectionHead(section - 1) 239 } 240 241 c.lock.Unlock() 242 newHead, err := c.processSection(section, oldHead) 243 if err != nil { 244 c.log.Error("Section processing failed", "error", err) 245 } 246 c.lock.Lock() 247 248 if err == nil && oldHead == c.SectionHead(section-1) { 249 c.setSectionHead(section, newHead) 250 c.setValidSections(section + 1) 251 if c.storedSections == c.knownSections && updating { 252 updating = false 253 c.log.Info("Finished upgrading chain index") 254 } 255 256 c.cascadedHead = c.storedSections*c.sectionSize - 1 257 for _, side := range c.sideren { 258 c.log.Trace("Cascading chain index update", "head", c.cascadedHead) 259 side.newHead(c.cascadedHead, false) 260 } 261 } else { 262 263 c.log.Debug("Chain index processing failed", "section", section, "err", err) 264 c.knownSections = c.storedSections 265 } 266 } 267 268 if c.knownSections > c.storedSections { 269 time.AfterFunc(c.throttling, func() { 270 select { 271 case c.update <- struct{}{}: 272 default: 273 } 274 }) 275 } 276 c.lock.Unlock() 277 } 278 } 279 } 280 281 func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (common.Hash, error) { 282 c.log.Trace("Processing new chain section", "section", section) 283 284 if err := c.backend.Reset(section, lastHead); err != nil { 285 c.setValidSections(0) 286 return common.Hash{}, err 287 } 288 289 for number := section * c.sectionSize; number < (section+1)*c.sectionSize; number++ { 290 hash := rawdb.ReadCanonicalHash(c.chainDb, number) 291 if hash == (common.Hash{}) { 292 return common.Hash{}, fmt.Errorf("canonical block #%d unknown", number) 293 } 294 header := rawdb.ReadHeader(c.chainDb, hash, number) 295 if header == nil { 296 return common.Hash{}, fmt.Errorf("block #%d [%x…] not found", number, hash[:4]) 297 } else if header.ParentHash != lastHead { 298 return common.Hash{}, fmt.Errorf("chain reorged during section processing") 299 } 300 c.backend.Process(header) 301 lastHead = header.Hash() 302 } 303 if err := c.backend.Commit(); err != nil { 304 c.log.Error("Section commit failed", "error", err) 305 return common.Hash{}, err 306 } 307 return lastHead, nil 308 } 309 310 func (c *ChainIndexer) Sections() (uint64, uint64, common.Hash) { 311 c.lock.Lock() 312 defer c.lock.Unlock() 313 314 return c.storedSections, c.storedSections*c.sectionSize - 1, c.SectionHead(c.storedSections - 1) 315 } 316 317 func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) { 318 c.lock.Lock() 319 defer c.lock.Unlock() 320 321 c.sideren = append(c.sideren, indexer) 322 323 if c.storedSections > 0 { 324 indexer.newHead(c.storedSections*c.sectionSize-1, false) 325 } 326 } 327 328 func (c *ChainIndexer) loadValidSections() { 329 data, _ := c.indexDb.Get([]byte("count")) 330 if len(data) == 8 { 331 c.storedSections = binary.BigEndian.Uint64(data[:]) 332 } 333 } 334 335 func (c *ChainIndexer) setValidSections(sections uint64) { 336 337 var data [8]byte 338 binary.BigEndian.PutUint64(data[:], sections) 339 c.indexDb.Put([]byte("count"), data[:]) 340 341 for c.storedSections > sections { 342 c.storedSections-- 343 c.removeSectionHead(c.storedSections) 344 } 345 c.storedSections = sections 346 } 347 348 func (c *ChainIndexer) SectionHead(section uint64) common.Hash { 349 var data [8]byte 350 binary.BigEndian.PutUint64(data[:], section) 351 352 hash, _ := c.indexDb.Get(append([]byte("shead"), data[:]...)) 353 if len(hash) == len(common.Hash{}) { 354 return common.BytesToHash(hash) 355 } 356 return common.Hash{} 357 } 358 359 func (c *ChainIndexer) setSectionHead(section uint64, hash common.Hash) { 360 var data [8]byte 361 binary.BigEndian.PutUint64(data[:], section) 362 363 c.indexDb.Put(append([]byte("shead"), data[:]...), hash.Bytes()) 364 } 365 366 func (c *ChainIndexer) removeSectionHead(section uint64) { 367 var data [8]byte 368 binary.BigEndian.PutUint64(data[:], section) 369 370 c.indexDb.Delete(append([]byte("shead"), data[:]...)) 371 }