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  }