github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/blockchain-scrapers/block-scrapers/ethereum.go (about)

     1  package blockscrapers
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"time"
     7  
     8  	"github.com/diadata-org/diadata/pkg/dia"
     9  	"github.com/diadata-org/diadata/pkg/dia/helpers/ethhelper"
    10  	"github.com/diadata-org/diadata/pkg/utils"
    11  	"github.com/ethereum/go-ethereum/ethclient"
    12  
    13  	models "github.com/diadata-org/diadata/pkg/model"
    14  )
    15  
    16  const (
    17  	followDist = 2
    18  	restDial   = ""
    19  )
    20  
    21  type EthereumScraper struct {
    22  	blockscraper    BlockScraper
    23  	client          *ethclient.Client
    24  	ticker          *time.Ticker
    25  	lastBlockNumber int64
    26  }
    27  
    28  func NewEthereumScraper(rdb *models.RelDB) *EthereumScraper {
    29  	connection, err := ethclient.Dial(utils.Getenv("URI_REST_ETH", restDial))
    30  	if err != nil {
    31  		log.Fatal("init rest client: ", err)
    32  	}
    33  
    34  	blockScraper := BlockScraper{
    35  		shutdown:     make(chan nothing),
    36  		shutdownDone: make(chan nothing),
    37  		error:        nil,
    38  		relDB:        rdb,
    39  		chanData:     make(chan dia.BlockData),
    40  	}
    41  	s := &EthereumScraper{
    42  		blockscraper: blockScraper,
    43  		client:       connection,
    44  		ticker:       time.NewTicker(refreshDelay),
    45  	}
    46  
    47  	go s.mainLoop()
    48  	return s
    49  }
    50  
    51  // mainLoop runs in a goroutine until channel s is closed.
    52  func (scraper *EthereumScraper) mainLoop() {
    53  	err := scraper.FetchData()
    54  	if err != nil {
    55  		log.Error("error updating block: ", err)
    56  	}
    57  	for {
    58  		select {
    59  		case <-scraper.ticker.C:
    60  			err := scraper.FetchData()
    61  			if err != nil {
    62  				log.Error("error updating block: ", err)
    63  			}
    64  		case <-scraper.blockscraper.shutdown: // user requested shutdown
    65  			log.Printf("Block scraper shutting down")
    66  			err := scraper.Close()
    67  			scraper.cleanup(err)
    68  			return
    69  		}
    70  	}
    71  }
    72  
    73  func (scraper *EthereumScraper) FetchData() error {
    74  
    75  	// Fetch last scraped block number from db upon initialization.
    76  	if scraper.lastBlockNumber == 0 {
    77  		var err error
    78  		scraper.lastBlockNumber, err = scraper.blockscraper.relDB.GetLastBlockBlockscraper(dia.ETHEREUM)
    79  		if err != nil {
    80  			log.Errorf("could not find last scraped block: %v. Start from block 0.", err)
    81  		}
    82  	}
    83  
    84  	block, err := scraper.client.BlockByNumber(context.Background(), nil)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	currentBlockNumber := block.NumberU64()
    89  	for i := int(scraper.lastBlockNumber); i < int(currentBlockNumber)-followDist; i++ {
    90  		blockdata, err := ethhelper.GetBlockDataOnChain(int64(i), scraper.client)
    91  		if err != nil {
    92  			return err
    93  		}
    94  		scraper.GetDataChannel() <- blockdata
    95  		scraper.lastBlockNumber = int64(i)
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  func (scraper *EthereumScraper) GetDataChannel() chan dia.BlockData {
   102  	return scraper.blockscraper.chanData
   103  }
   104  
   105  // closes all connected Scrapers. Must only be called from mainLoop
   106  func (scraper *EthereumScraper) cleanup(err error) {
   107  	scraper.blockscraper.errorLock.Lock()
   108  	defer scraper.blockscraper.errorLock.Unlock()
   109  	scraper.ticker.Stop()
   110  	if err != nil {
   111  		scraper.blockscraper.error = err
   112  	}
   113  	scraper.blockscraper.closed = true
   114  	close(scraper.blockscraper.shutdownDone) // signal that shutdown is complete
   115  }
   116  
   117  // Close closes any existing API connections
   118  func (scraper *EthereumScraper) Close() error {
   119  	if scraper.blockscraper.closed {
   120  		return errors.New("scraper already closed")
   121  	}
   122  	close(scraper.blockscraper.shutdown)
   123  	<-scraper.blockscraper.shutdownDone
   124  	scraper.blockscraper.errorLock.RLock()
   125  	defer scraper.blockscraper.errorLock.RUnlock()
   126  	return scraper.blockscraper.error
   127  }