From 086d046592511b74316dfa4631f07268bc1d574d Mon Sep 17 00:00:00 2001 From: eastorski Date: Thu, 13 Feb 2025 16:07:38 +0000 Subject: [PATCH] Implemented wait if heimdall is not synced to the chain --- cmd/devnet/services/polygon/heimdall.go | 4 ++ .../polygon/heimdallsim/heimdall_simulator.go | 4 ++ polygon/bor/bor_test.go | 4 ++ polygon/heimdall/client.go | 2 + polygon/heimdall/client_http.go | 22 +++++++++++ polygon/heimdall/client_mock.go | 39 +++++++++++++++++++ polygon/heimdall/metrics.go | 1 + polygon/heimdall/service.go | 11 ++++++ polygon/heimdall/status.go | 30 ++++++++++++++ polygon/sync/sync.go | 19 +++++++++ 10 files changed, 136 insertions(+) create mode 100644 polygon/heimdall/status.go diff --git a/cmd/devnet/services/polygon/heimdall.go b/cmd/devnet/services/polygon/heimdall.go index 077db74c2c5..8eaebe924c8 100644 --- a/cmd/devnet/services/polygon/heimdall.go +++ b/cmd/devnet/services/polygon/heimdall.go @@ -221,6 +221,10 @@ func (h *Heimdall) getSpanOverrideHeight() uint64 { //MainChain: 8664000 } +func (h *Heimdall) FetchStatus(ctx context.Context) (*heimdall.Status, error) { + return nil, errors.New("TODO") +} + func (h *Heimdall) FetchCheckpoint(ctx context.Context, number int64) (*heimdall.Checkpoint, error) { return nil, errors.New("TODO") } diff --git a/cmd/devnet/services/polygon/heimdallsim/heimdall_simulator.go b/cmd/devnet/services/polygon/heimdallsim/heimdall_simulator.go index cd6a23a9866..4b30d3811ef 100644 --- a/cmd/devnet/services/polygon/heimdallsim/heimdall_simulator.go +++ b/cmd/devnet/services/polygon/heimdallsim/heimdall_simulator.go @@ -228,6 +228,10 @@ func (h *HeimdallSimulator) FetchStateSyncEvent(ctx context.Context, id uint64) return nil, errors.New("method FetchStateSyncEvent not implemented") } +func (h *HeimdallSimulator) FetchStatus(ctx context.Context) (*heimdall.Status, error) { + return nil, errors.New("method FetchStatus not implemented") +} + func (h *HeimdallSimulator) FetchCheckpoint(ctx context.Context, number int64) (*heimdall.Checkpoint, error) { return nil, errors.New("method FetchCheckpoint not implemented") } diff --git a/polygon/bor/bor_test.go b/polygon/bor/bor_test.go index ac8221ac6fc..68787bf189d 100644 --- a/polygon/bor/bor_test.go +++ b/polygon/bor/bor_test.go @@ -79,6 +79,10 @@ func (h *test_heimdall) FetchStateSyncEvent(ctx context.Context, id uint64) (*he return nil, nil } +func (h *test_heimdall) FetchStatus(ctx context.Context) (*heimdall.Status, error) { + return nil, nil +} + func (h *test_heimdall) FetchSpan(ctx context.Context, spanID uint64) (*heimdall.Span, error) { if span, ok := h.spans[heimdall.SpanId(spanID)]; ok { diff --git a/polygon/heimdall/client.go b/polygon/heimdall/client.go index 1c68e412e7d..bc71f8b6b8c 100644 --- a/polygon/heimdall/client.go +++ b/polygon/heimdall/client.go @@ -30,6 +30,8 @@ type Client interface { FetchSpan(ctx context.Context, spanID uint64) (*Span, error) FetchSpans(ctx context.Context, page uint64, limit uint64) ([]*Span, error) + FetchStatus(ctx context.Context) (*Status, error) + FetchCheckpoint(ctx context.Context, number int64) (*Checkpoint, error) FetchCheckpointCount(ctx context.Context) (int64, error) FetchCheckpoints(ctx context.Context, page uint64, limit uint64) ([]*Checkpoint, error) diff --git a/polygon/heimdall/client_http.go b/polygon/heimdall/client_http.go index 852c83519f4..877a80a7187 100644 --- a/polygon/heimdall/client_http.go +++ b/polygon/heimdall/client_http.go @@ -125,6 +125,8 @@ const ( fetchStateSyncEventsPath = "clerk/event-record/list" fetchStateSyncEvent = "clerk/event-record/%s" + fetchStatus = "/status" + fetchCheckpoint = "/checkpoints/%s" fetchCheckpointCount = "/checkpoints/count" fetchCheckpointList = "/checkpoints/list" @@ -349,6 +351,22 @@ func (c *HttpClient) FetchMilestone(ctx context.Context, number int64) (*Milesto return &response.Result, nil } +func (c *HttpClient) FetchStatus(ctx context.Context) (*Status, error) { + url, err := statusURL(c.urlString) + if err != nil { + return nil, err + } + + ctx = withRequestType(ctx, statusRequest) + + response, err := FetchWithRetry[StatusResponse](ctx, c, url, c.logger) + if err != nil { + return nil, err + } + + return &response.Result, nil +} + // FetchCheckpointCount fetches the checkpoint count from heimdall func (c *HttpClient) FetchCheckpointCount(ctx context.Context) (int64, error) { url, err := checkpointCountURL(c.urlString) @@ -587,6 +605,10 @@ func checkpointCountURL(urlString string) (*url.URL, error) { return makeURL(urlString, fetchCheckpointCount, "") } +func statusURL(urlString string) (*url.URL, error) { + return makeURL(urlString, fetchStatus, "") +} + func checkpointListURL(urlString string, page uint64, limit uint64) (*url.URL, error) { return makeURL(urlString, fetchCheckpointList, fmt.Sprintf(fetchCheckpointListQueryFormat, page, limit)) } diff --git a/polygon/heimdall/client_mock.go b/polygon/heimdall/client_mock.go index 7710eff7509..a8f3baf9089 100644 --- a/polygon/heimdall/client_mock.go +++ b/polygon/heimdall/client_mock.go @@ -620,3 +620,42 @@ func (c *MockClientFetchStateSyncEventsCall) DoAndReturn(f func(context.Context, c.Call = c.Call.DoAndReturn(f) return c } + +// FetchStatus mocks base method. +func (m *MockClient) FetchStatus(ctx context.Context) (*Status, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchStatus", ctx) + ret0, _ := ret[0].(*Status) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchStatus indicates an expected call of FetchStatus. +func (mr *MockClientMockRecorder) FetchStatus(ctx any) *MockClientFetchStatusCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchStatus", reflect.TypeOf((*MockClient)(nil).FetchStatus), ctx) + return &MockClientFetchStatusCall{Call: call} +} + +// MockClientFetchStatusCall wrap *gomock.Call +type MockClientFetchStatusCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockClientFetchStatusCall) Return(arg0 *Status, arg1 error) *MockClientFetchStatusCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockClientFetchStatusCall) Do(f func(context.Context) (*Status, error)) *MockClientFetchStatusCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockClientFetchStatusCall) DoAndReturn(f func(context.Context) (*Status, error)) *MockClientFetchStatusCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/polygon/heimdall/metrics.go b/polygon/heimdall/metrics.go index 2fb58a3fcc6..7bfc2f9e4b0 100644 --- a/polygon/heimdall/metrics.go +++ b/polygon/heimdall/metrics.go @@ -34,6 +34,7 @@ type ( ) const ( + statusRequest requestType = "status" stateSyncRequest requestType = "state-sync" spanRequest requestType = "span" checkpointRequest requestType = "checkpoint" diff --git a/polygon/heimdall/service.go b/polygon/heimdall/service.go index dd60dc69bbf..5c3133e073d 100644 --- a/polygon/heimdall/service.go +++ b/polygon/heimdall/service.go @@ -47,6 +47,7 @@ type Service struct { milestoneScraper *Scraper[*Milestone] spanScraper *Scraper[*Span] spanBlockProducersTracker *spanBlockProducersTracker + client Client ready ready } @@ -100,6 +101,7 @@ func NewService(config ServiceConfig) *Service { milestoneScraper: milestoneScraper, spanScraper: spanScraper, spanBlockProducersTracker: newSpanBlockProducersTracker(logger, borConfig, store.SpanBlockProducerSelections()), + client: client, } } @@ -397,3 +399,12 @@ func (s *Service) replayUntrackedSpans(ctx context.Context) error { return nil } + +func (s *Service) IsCatchingUp(ctx context.Context) (bool, error) { + status, err := s.client.FetchStatus(ctx) + if err != nil { + return false, err + } + + return status.CatchingUp, nil +} diff --git a/polygon/heimdall/status.go b/polygon/heimdall/status.go new file mode 100644 index 00000000000..6e63fb5abca --- /dev/null +++ b/polygon/heimdall/status.go @@ -0,0 +1,30 @@ +// Copyright 2024 The Erigon Authors +// This file is part of Erigon. +// +// Erigon is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Erigon is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Erigon. If not, see . + +package heimdall + +type Status struct { + LatestBlockHash string `json:"latest_block_hash"` + LatestAppHash string `json:"latest_app_hash"` + LatestBlockHeight string `json:"latest_block_height"` + LatestBlockTime string `json:"latest_block_time"` + CatchingUp bool `json:"catching_up"` +} + +type StatusResponse struct { + Height string `json:"height"` + Result Status `json:"result"` +} diff --git a/polygon/sync/sync.go b/polygon/sync/sync.go index 5ac8ba471b2..9ee9428bb02 100644 --- a/polygon/sync/sync.go +++ b/polygon/sync/sync.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/golang-lru/v2/simplelru" "github.com/erigontech/erigon-lib/common" + libcommon "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/log/v3" "github.com/erigontech/erigon/core/types" "github.com/erigontech/erigon/eth/ethconfig" @@ -34,6 +35,7 @@ import ( ) type heimdallSynchronizer interface { + IsCatchingUp(ctx context.Context) (bool, error) SynchronizeCheckpoints(ctx context.Context) (latest *heimdall.Checkpoint, err error) SynchronizeMilestones(ctx context.Context) (latest *heimdall.Milestone, err error) SynchronizeSpans(ctx context.Context, blockNum uint64) error @@ -668,6 +670,23 @@ func (s *Sync) Run(ctx context.Context) error { s.logger.Info(syncLogPrefix("running sync component")) + for { + // we have to check if the heimdall we are connected to is synchonised with the chain + // to prevent getting empty list of checkpoints/milestones during the sync + + catchingUp, err := s.heimdallSync.IsCatchingUp(ctx) + if err != nil { + return err + } + + if !catchingUp { + break + } + + s.logger.Warn(syncLogPrefix("your heimdalld process is behind, please check its logs and localhost:1317/status api")) + libcommon.Sleep(ctx, 30*time.Second) + } + result, err := s.syncToTip(ctx) if err != nil { return err