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