2022-02-07 21:30:46 +00:00
|
|
|
package bor
|
|
|
|
|
|
|
|
import (
|
2022-06-28 03:49:51 +00:00
|
|
|
"context"
|
2022-02-07 21:30:46 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2022-04-23 14:43:00 +00:00
|
|
|
"io"
|
2022-02-07 21:30:46 +00:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ledgerwatch/log/v3"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
stateFetchLimit = 50
|
|
|
|
)
|
|
|
|
|
|
|
|
// ResponseWithHeight defines a response object type that wraps an original
|
|
|
|
// response with a height.
|
|
|
|
type ResponseWithHeight struct {
|
|
|
|
Height string `json:"height"`
|
|
|
|
Result json.RawMessage `json:"result"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type IHeimdallClient interface {
|
2022-06-28 03:49:51 +00:00
|
|
|
Fetch(ctx context.Context, path string, query string) (*ResponseWithHeight, error)
|
|
|
|
FetchWithRetry(ctx context.Context, path string, query string) (*ResponseWithHeight, error)
|
|
|
|
FetchStateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*EventRecordWithTime, error)
|
2022-02-07 21:30:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type HeimdallClient struct {
|
|
|
|
urlString string
|
|
|
|
client http.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewHeimdallClient(urlString string) (*HeimdallClient, error) {
|
|
|
|
h := &HeimdallClient{
|
|
|
|
urlString: urlString,
|
|
|
|
client: http.Client{
|
2022-08-13 11:51:25 +00:00
|
|
|
Timeout: 5 * time.Second,
|
2022-02-07 21:30:46 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
return h, nil
|
|
|
|
}
|
|
|
|
|
2022-06-28 03:49:51 +00:00
|
|
|
func (h *HeimdallClient) FetchStateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*EventRecordWithTime, error) {
|
2022-02-07 21:30:46 +00:00
|
|
|
eventRecords := make([]*EventRecordWithTime, 0)
|
|
|
|
for {
|
|
|
|
queryParams := fmt.Sprintf("from-id=%d&to-time=%d&limit=%d", fromID, to, stateFetchLimit)
|
2022-02-24 00:03:10 +00:00
|
|
|
log.Trace("Fetching state sync events", "queryParams", queryParams)
|
2022-06-28 03:49:51 +00:00
|
|
|
response, err := h.FetchWithRetry(ctx, "clerk/event-record/list", queryParams)
|
2022-02-07 21:30:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var _eventRecords []*EventRecordWithTime
|
|
|
|
if response.Result == nil { // status 204
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(response.Result, &_eventRecords); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
eventRecords = append(eventRecords, _eventRecords...)
|
|
|
|
if len(_eventRecords) < stateFetchLimit {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
fromID += uint64(stateFetchLimit)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.SliceStable(eventRecords, func(i, j int) bool {
|
|
|
|
return eventRecords[i].ID < eventRecords[j].ID
|
|
|
|
})
|
|
|
|
return eventRecords, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch fetches response from heimdall
|
2022-06-28 03:49:51 +00:00
|
|
|
func (h *HeimdallClient) Fetch(ctx context.Context, rawPath string, rawQuery string) (*ResponseWithHeight, error) {
|
2022-02-07 21:30:46 +00:00
|
|
|
u, err := url.Parse(h.urlString)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
u.Path = rawPath
|
|
|
|
u.RawQuery = rawQuery
|
|
|
|
|
2022-06-28 03:49:51 +00:00
|
|
|
return h.internalFetch(ctx, u)
|
2022-02-07 21:30:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FetchWithRetry returns data from heimdall with retry
|
2022-06-28 03:49:51 +00:00
|
|
|
func (h *HeimdallClient) FetchWithRetry(ctx context.Context, rawPath string, rawQuery string) (*ResponseWithHeight, error) {
|
2022-02-07 21:30:46 +00:00
|
|
|
u, err := url.Parse(h.urlString)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
u.Path = rawPath
|
|
|
|
u.RawQuery = rawQuery
|
|
|
|
|
|
|
|
for {
|
2022-06-28 03:49:51 +00:00
|
|
|
res, err := h.internalFetch(ctx, u)
|
2022-02-07 21:30:46 +00:00
|
|
|
if err == nil && res != nil {
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
log.Info("Retrying again in 5 seconds for next Heimdall span", "path", u.Path)
|
2022-06-28 03:49:51 +00:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
}
|
2022-02-07 21:30:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// internal fetch method
|
2022-06-28 03:49:51 +00:00
|
|
|
func (h *HeimdallClient) internalFetch(ctx context.Context, u *url.URL) (*ResponseWithHeight, error) {
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
res, err := h.client.Do(req)
|
2022-02-07 21:30:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
|
|
|
|
|
|
// check status code
|
|
|
|
if res.StatusCode != 200 && res.StatusCode != 204 {
|
|
|
|
return nil, fmt.Errorf("Error while fetching data from Heimdall")
|
|
|
|
}
|
|
|
|
|
|
|
|
// unmarshall data from buffer
|
|
|
|
var response ResponseWithHeight
|
|
|
|
if res.StatusCode == 204 {
|
|
|
|
return &response, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// get response
|
2022-04-23 14:43:00 +00:00
|
|
|
body, err := io.ReadAll(res.Body)
|
2022-02-07 21:30:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(body, &response); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &response, nil
|
|
|
|
}
|