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 }