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