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  }