github.com/ImPedro29/bor@v0.2.7/consensus/bor/rest.go (about)

     1  package bor
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  	"sort"
    10  	"time"
    11  
    12  	"github.com/ethereum/go-ethereum/log"
    13  )
    14  
    15  var (
    16  	stateFetchLimit = 50
    17  )
    18  
    19  // ResponseWithHeight defines a response object type that wraps an original
    20  // response with a height.
    21  type ResponseWithHeight struct {
    22  	Height string          `json:"height"`
    23  	Result json.RawMessage `json:"result"`
    24  }
    25  
    26  type IHeimdallClient interface {
    27  	Fetch(path string, query string) (*ResponseWithHeight, error)
    28  	FetchWithRetry(path string, query string) (*ResponseWithHeight, error)
    29  	FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error)
    30  }
    31  
    32  type HeimdallClient struct {
    33  	urlString string
    34  	client    http.Client
    35  }
    36  
    37  func NewHeimdallClient(urlString string) (*HeimdallClient, error) {
    38  	h := &HeimdallClient{
    39  		urlString: urlString,
    40  		client: http.Client{
    41  			Timeout: time.Duration(5 * time.Second),
    42  		},
    43  	}
    44  	return h, nil
    45  }
    46  
    47  func (h *HeimdallClient) FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error) {
    48  	eventRecords := make([]*EventRecordWithTime, 0)
    49  	for {
    50  		queryParams := fmt.Sprintf("from-id=%d&to-time=%d&limit=%d", fromID, to, stateFetchLimit)
    51  		log.Info("Fetching state sync events", "queryParams", queryParams)
    52  		response, err := h.FetchWithRetry("clerk/event-record/list", queryParams)
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  		var _eventRecords []*EventRecordWithTime
    57  		if response.Result == nil { // status 204
    58  			break
    59  		}
    60  		if err := json.Unmarshal(response.Result, &_eventRecords); err != nil {
    61  			return nil, err
    62  		}
    63  		eventRecords = append(eventRecords, _eventRecords...)
    64  		if len(_eventRecords) < stateFetchLimit {
    65  			break
    66  		}
    67  		fromID += uint64(stateFetchLimit)
    68  	}
    69  
    70  	sort.SliceStable(eventRecords, func(i, j int) bool {
    71  		return eventRecords[i].ID < eventRecords[j].ID
    72  	})
    73  	return eventRecords, nil
    74  }
    75  
    76  // Fetch fetches response from heimdall
    77  func (h *HeimdallClient) Fetch(rawPath string, rawQuery string) (*ResponseWithHeight, error) {
    78  	u, err := url.Parse(h.urlString)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	u.Path = rawPath
    84  	u.RawQuery = rawQuery
    85  
    86  	return h.internalFetch(u)
    87  }
    88  
    89  // FetchWithRetry returns data from heimdall with retry
    90  func (h *HeimdallClient) FetchWithRetry(rawPath string, rawQuery string) (*ResponseWithHeight, error) {
    91  	u, err := url.Parse(h.urlString)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	u.Path = rawPath
    97  	u.RawQuery = rawQuery
    98  
    99  	for {
   100  		res, err := h.internalFetch(u)
   101  		if err == nil && res != nil {
   102  			return res, nil
   103  		}
   104  		log.Info("Retrying again in 5 seconds for next Heimdall span", "path", u.Path)
   105  		time.Sleep(5 * time.Second)
   106  	}
   107  }
   108  
   109  // internal fetch method
   110  func (h *HeimdallClient) internalFetch(u *url.URL) (*ResponseWithHeight, error) {
   111  	res, err := h.client.Get(u.String())
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	defer res.Body.Close()
   116  
   117  	// check status code
   118  	if res.StatusCode != 200 && res.StatusCode != 204 {
   119  		return nil, fmt.Errorf("Error while fetching data from Heimdall")
   120  	}
   121  
   122  	// unmarshall data from buffer
   123  	var response ResponseWithHeight
   124  	if res.StatusCode == 204 {
   125  		return &response, nil
   126  	}
   127  
   128  	// get response
   129  	body, err := ioutil.ReadAll(res.Body)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	if err := json.Unmarshal(body, &response); err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	return &response, nil
   139  }