github.com/aquanetwork/aquachain@v1.7.8/core/chain_indexer.go (about) 1 // Copyright 2017 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain 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 aquachain 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 aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "encoding/binary" 21 "fmt" 22 "sync" 23 "sync/atomic" 24 "time" 25 26 "gitlab.com/aquachain/aquachain/aqua/event" 27 "gitlab.com/aquachain/aquachain/aquadb" 28 "gitlab.com/aquachain/aquachain/common" 29 "gitlab.com/aquachain/aquachain/common/log" 30 "gitlab.com/aquachain/aquachain/core/types" 31 "gitlab.com/aquachain/aquachain/params" 32 ) 33 34 // ChainIndexerBackend defines the methods needed to process chain segments in 35 // the background and write the segment results into the database. These can be 36 // used to create filter blooms or CHTs. 37 type ChainIndexerBackend interface { 38 // Reset initiates the processing of a new chain segment, potentially terminating 39 // any partially completed operations (in case of a reorg). 40 Reset(section uint64, prevHead common.Hash) error 41 42 // Process crunches through the next header in the chain segment. The caller 43 // will ensure a sequential order of headers. 44 Process(header *types.Header) 45 46 // Commit finalizes the section metadata and stores it into the database. 47 Commit() error 48 } 49 50 // ChainIndexerChain interface is used for connecting the indexer to a blockchain 51 type ChainIndexerChain interface { 52 // CurrentHeader retrieves the latest locally known header complete with version. 53 CurrentHeader() *types.Header 54 // SubscribeChainEvent subscribes to new head header notifications. 55 SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription 56 } 57 58 // ChainIndexer does a post-processing job for equally sized sections of the 59 // canonical chain (like BlooomBits and CHT structures). A ChainIndexer is 60 // connected to the blockchain through the event system by starting a 61 // ChainEventLoop in a goroutine. 62 // 63 // Further child ChainIndexers can be added which use the output of the parent 64 // section indexer. These child indexers receive new head notifications only 65 // after an entire section has been finished or in case of rollbacks that might 66 // affect already finished sections. 67 type ChainIndexer struct { 68 chainDb aquadb.Database // Chain database to index the data from 69 indexDb aquadb.Database // Prefixed table-view of the db to write index metadata into 70 backend ChainIndexerBackend // Background processor generating the index data content 71 children []*ChainIndexer // Child indexers to cascade chain updates to 72 config *params.ChainConfig 73 active uint32 // Flag whether the event loop was started 74 update chan struct{} // Notification channel that headers should be processed 75 quit chan chan error // Quit channel to tear down running goroutines 76 77 sectionSize uint64 // Number of blocks in a single chain segment to process 78 confirmsReq uint64 // Number of confirmations before processing a completed segment 79 80 storedSections uint64 // Number of sections successfully indexed into the database 81 knownSections uint64 // Number of sections known to be complete (block wise) 82 cascadedHead uint64 // Block number of the last completed section cascaded to subindexers 83 84 throttling time.Duration // Disk throttling to prevent a heavy upgrade from hogging resources 85 86 log log.Logger 87 lock sync.RWMutex 88 } 89 90 // NewChainIndexer creates a new chain indexer to do background processing on 91 // chain segments of a given size after certain number of confirmations passed. 92 // The throttling parameter might be used to prevent database thrashing. 93 func NewChainIndexer(config *params.ChainConfig, chainDb, indexDb aquadb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer { 94 c := &ChainIndexer{ 95 chainDb: chainDb, 96 config: config, 97 indexDb: indexDb, 98 backend: backend, 99 update: make(chan struct{}, 1), 100 quit: make(chan chan error), 101 sectionSize: section, 102 confirmsReq: confirm, 103 throttling: throttling, 104 log: log.New("type", kind), 105 } 106 // Initialize database dependent fields and start the updater 107 c.loadValidSections() 108 go c.updateLoop() 109 110 return c 111 } 112 113 // AddKnownSectionHead marks a new section head as known/processed if it is newer 114 // than the already known best section head 115 func (c *ChainIndexer) AddKnownSectionHead(section uint64, shead common.Hash) { 116 c.lock.Lock() 117 defer c.lock.Unlock() 118 119 if section < c.storedSections { 120 return 121 } 122 c.setSectionHead(section, shead) 123 c.setValidSections(section + 1) 124 } 125 126 // Start creates a goroutine to feed chain head events into the indexer for 127 // cascading background processing. Children do not need to be started, they 128 // are notified about new events by their parents. 129 func (c *ChainIndexer) Start(chain ChainIndexerChain) { 130 events := make(chan ChainEvent, 10) 131 sub := chain.SubscribeChainEvent(events) 132 133 go c.eventLoop(chain.CurrentHeader(), events, sub) 134 } 135 136 // Close tears down all goroutines belonging to the indexer and returns any error 137 // that might have occurred internally. 138 func (c *ChainIndexer) Close() error { 139 var errs []error 140 141 // Tear down the primary update loop 142 errc := make(chan error) 143 c.quit <- errc 144 if err := <-errc; err != nil { 145 errs = append(errs, err) 146 } 147 // If needed, tear down the secondary event loop 148 if atomic.LoadUint32(&c.active) != 0 { 149 c.quit <- errc 150 if err := <-errc; err != nil { 151 errs = append(errs, err) 152 } 153 } 154 // Close all children 155 for _, child := range c.children { 156 if err := child.Close(); err != nil { 157 errs = append(errs, err) 158 } 159 } 160 // Return any failures 161 switch { 162 case len(errs) == 0: 163 return nil 164 165 case len(errs) == 1: 166 return errs[0] 167 168 default: 169 return fmt.Errorf("%v", errs) 170 } 171 } 172 173 // eventLoop is a secondary - optional - event loop of the indexer which is only 174 // started for the outermost indexer to push chain head events into a processing 175 // queue. 176 func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainEvent, sub event.Subscription) { 177 // Mark the chain indexer as active, requiring an additional teardown 178 atomic.StoreUint32(&c.active, 1) 179 180 defer sub.Unsubscribe() 181 currentHeader.Version = c.config.GetBlockVersion(currentHeader.Number) 182 183 // Fire the initial new head event to start any outstanding processing 184 c.newHead(currentHeader.Number.Uint64(), false) 185 186 var ( 187 prevHeader = currentHeader 188 prevHash = currentHeader.Hash() 189 ) 190 for { 191 select { 192 case errc := <-c.quit: 193 // Chain indexer terminating, report no failure and abort 194 errc <- nil 195 return 196 197 case ev, ok := <-events: 198 // Received a new event, ensure it's not nil (closing) and update 199 if !ok { 200 errc := <-c.quit 201 errc <- nil 202 return 203 } 204 header := ev.Block.Header() 205 header.Version = c.config.GetBlockVersion(header.Number) 206 if header.ParentHash != prevHash { 207 // Reorg to the common ancestor (might not exist in light sync mode, skip reorg then) 208 // TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly? 209 210 // TODO(karalabe): This operation is expensive and might block, causing the event system to 211 // potentially also lock up. We need to do with on a different thread somehow. 212 if h := FindCommonAncestor(c.chainDb, prevHeader, header, c.config.GetBlockVersion); h != nil { 213 c.newHead(h.Number.Uint64(), true) 214 } 215 } 216 c.newHead(header.Number.Uint64(), false) 217 prevHeader, prevHash = header, header.SetVersion(byte(c.config.GetBlockVersion(header.Number))) 218 } 219 } 220 } 221 222 // newHead notifies the indexer about new chain heads and/or reorgs. 223 func (c *ChainIndexer) newHead(head uint64, reorg bool) { 224 c.lock.Lock() 225 defer c.lock.Unlock() 226 227 // If a reorg happened, invalidate all sections until that point 228 if reorg { 229 // Revert the known section number to the reorg point 230 changed := head / c.sectionSize 231 if changed < c.knownSections { 232 c.knownSections = changed 233 } 234 // Revert the stored sections from the database to the reorg point 235 if changed < c.storedSections { 236 c.setValidSections(changed) 237 } 238 // Update the new head number to the finalized section end and notify children 239 head = changed * c.sectionSize 240 241 if head < c.cascadedHead { 242 c.cascadedHead = head 243 for _, child := range c.children { 244 child.newHead(c.cascadedHead, true) 245 } 246 } 247 return 248 } 249 // No reorg, calculate the number of newly known sections and update if high enough 250 var sections uint64 251 if head >= c.confirmsReq { 252 sections = (head + 1 - c.confirmsReq) / c.sectionSize 253 if sections > c.knownSections { 254 c.knownSections = sections 255 256 select { 257 case c.update <- struct{}{}: 258 default: 259 } 260 } 261 } 262 } 263 264 // updateLoop is the main event loop of the indexer which pushes chain segments 265 // down into the processing backend. 266 func (c *ChainIndexer) updateLoop() { 267 var ( 268 updating bool 269 updated time.Time 270 ) 271 272 for { 273 select { 274 case errc := <-c.quit: 275 // Chain indexer terminating, report no failure and abort 276 errc <- nil 277 return 278 279 case <-c.update: 280 // Section headers completed (or rolled back), update the index 281 c.lock.Lock() 282 if c.knownSections > c.storedSections { 283 // Periodically print an upgrade log message to the user 284 if time.Since(updated) > 8*time.Second { 285 if c.knownSections > c.storedSections+1 { 286 updating = true 287 c.log.Info("Upgrading chain index", "percentage", c.storedSections*100/c.knownSections) 288 } 289 updated = time.Now() 290 } 291 // Cache the current section count and head to allow unlocking the mutex 292 section := c.storedSections 293 var oldHead common.Hash 294 if section > 0 { 295 oldHead = c.SectionHead(section - 1) 296 } 297 // Process the newly defined section in the background 298 c.lock.Unlock() 299 newHead, err := c.processSection(section, oldHead) 300 if err != nil { 301 c.log.Error("Section processing failed", "error", err) 302 } 303 c.lock.Lock() 304 305 // If processing succeeded and no reorgs occcurred, mark the section completed 306 if err == nil && oldHead == c.SectionHead(section-1) { 307 c.setSectionHead(section, newHead) 308 c.setValidSections(section + 1) 309 if c.storedSections == c.knownSections && updating { 310 updating = false 311 c.log.Info("Finished upgrading chain index") 312 } 313 314 c.cascadedHead = c.storedSections*c.sectionSize - 1 315 for _, child := range c.children { 316 c.log.Trace("Cascading chain index update", "head", c.cascadedHead) 317 child.newHead(c.cascadedHead, false) 318 } 319 } else { 320 // If processing failed, don't retry until further notification 321 c.log.Debug("Chain index processing failed", "section", section, "err", err) 322 c.knownSections = c.storedSections 323 } 324 } 325 // If there are still further sections to process, reschedule 326 if c.knownSections > c.storedSections { 327 time.AfterFunc(c.throttling, func() { 328 select { 329 case c.update <- struct{}{}: 330 default: 331 } 332 }) 333 } 334 c.lock.Unlock() 335 } 336 } 337 } 338 339 // processSection processes an entire section by calling backend functions while 340 // ensuring the continuity of the passed headers. Since the chain mutex is not 341 // held while processing, the continuity can be broken by a long reorg, in which 342 // case the function returns with an error. 343 func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (common.Hash, error) { 344 c.log.Trace("Processing new chain section", "section", section) 345 346 // Reset and partial processing 347 348 if err := c.backend.Reset(section, lastHead); err != nil { 349 c.setValidSections(0) 350 return common.Hash{}, err 351 } 352 353 for number := section * c.sectionSize; number < (section+1)*c.sectionSize; number++ { 354 hash := GetCanonicalHash(c.chainDb, number) 355 if hash == (common.Hash{}) { 356 return common.Hash{}, fmt.Errorf("canonical block #%d unknown", number) 357 } 358 header := GetHeaderNoVersion(c.chainDb, hash, number) 359 if header == nil { 360 return common.Hash{}, fmt.Errorf("block #%d [%x…] not found", number, hash[:4]) 361 } else if header.ParentHash != lastHead { 362 return common.Hash{}, fmt.Errorf("chain reorged during section processing") 363 } 364 header.Version = c.config.GetBlockVersion(header.Number) 365 c.backend.Process(header) 366 lastHead = header.Hash() 367 } 368 if err := c.backend.Commit(); err != nil { 369 c.log.Error("Section commit failed", "error", err) 370 return common.Hash{}, err 371 } 372 return lastHead, nil 373 } 374 375 // Sections returns the number of processed sections maintained by the indexer 376 // and also the information about the last header indexed for potential canonical 377 // verifications. 378 func (c *ChainIndexer) Sections() (uint64, uint64, common.Hash) { 379 c.lock.Lock() 380 defer c.lock.Unlock() 381 382 return c.storedSections, c.storedSections*c.sectionSize - 1, c.SectionHead(c.storedSections - 1) 383 } 384 385 // AddChildIndexer adds a child ChainIndexer that can use the output of this one 386 func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) { 387 c.lock.Lock() 388 defer c.lock.Unlock() 389 390 c.children = append(c.children, indexer) 391 392 // Cascade any pending updates to new children too 393 if c.storedSections > 0 { 394 indexer.newHead(c.storedSections*c.sectionSize-1, false) 395 } 396 } 397 398 // loadValidSections reads the number of valid sections from the index database 399 // and caches is into the local state. 400 func (c *ChainIndexer) loadValidSections() { 401 data, _ := c.indexDb.Get([]byte("count")) 402 if len(data) == 8 { 403 c.storedSections = binary.BigEndian.Uint64(data[:]) 404 } 405 } 406 407 // setValidSections writes the number of valid sections to the index database 408 func (c *ChainIndexer) setValidSections(sections uint64) { 409 // Set the current number of valid sections in the database 410 var data [8]byte 411 binary.BigEndian.PutUint64(data[:], sections) 412 c.indexDb.Put([]byte("count"), data[:]) 413 414 // Remove any reorged sections, caching the valids in the mean time 415 for c.storedSections > sections { 416 c.storedSections-- 417 c.removeSectionHead(c.storedSections) 418 } 419 c.storedSections = sections // needed if new > old 420 } 421 422 // SectionHead retrieves the last block hash of a processed section from the 423 // index database. 424 func (c *ChainIndexer) SectionHead(section uint64) common.Hash { 425 var data [8]byte 426 binary.BigEndian.PutUint64(data[:], section) 427 428 hash, _ := c.indexDb.Get(append([]byte("shead"), data[:]...)) 429 if len(hash) == len(common.Hash{}) { 430 return common.BytesToHash(hash) 431 } 432 return common.Hash{} 433 } 434 435 // setSectionHead writes the last block hash of a processed section to the index 436 // database. 437 func (c *ChainIndexer) setSectionHead(section uint64, hash common.Hash) { 438 var data [8]byte 439 binary.BigEndian.PutUint64(data[:], section) 440 441 c.indexDb.Put(append([]byte("shead"), data[:]...), hash.Bytes()) 442 } 443 444 // removeSectionHead removes the reference to a processed section from the index 445 // database. 446 func (c *ChainIndexer) removeSectionHead(section uint64) { 447 var data [8]byte 448 binary.BigEndian.PutUint64(data[:], section) 449 450 c.indexDb.Delete(append([]byte("shead"), data[:]...)) 451 }