diff --git a/activation/activation_errors.go b/activation/activation_errors.go index 601541e83c..e8503fc128 100644 --- a/activation/activation_errors.go +++ b/activation/activation_errors.go @@ -12,6 +12,8 @@ var ( ErrPoetServiceUnstable = &PoetSvcUnstableError{} // ErrPoetProofNotReceived is returned when no poet proof was received. ErrPoetProofNotReceived = errors.New("builder: didn't receive any poet proof") + // ErrNoRegistrationForGivenPoetFound is returned when configured poets do not match existing registrations in db at all + ErrNoRegistrationForGivenPoetFound = errors.New("builder: none of configured poets matches the existing registrations") ) // PoetSvcUnstableError means there was a problem communicating diff --git a/activation/nipost.go b/activation/nipost.go index 399e07c9e5..2e467b3714 100644 --- a/activation/nipost.go +++ b/activation/nipost.go @@ -237,6 +237,11 @@ func (nb *NIPostBuilder) BuildNIPost( poetProofDeadline, poetRoundStart, challenge.Bytes()) if err != nil { + if errors.Is(err, ErrNoRegistrationForGivenPoetFound) { + // misconfiguration of poet services detected, user has to fix config + logger.Fatal("submitting to poets", zap.Error(err)) + } + return nil, fmt.Errorf("submitting to poets: %w", err) } @@ -440,13 +445,14 @@ func (nb *NIPostBuilder) submitPoetChallenges( ) case len(existingRegistrations) == 0: // no existing registration for given poets set - nb.logger.Panic( + nb.logger.Warn( "None of the poets listed in the config matches the existing registrations. "+ "Verify your config and local database state.", zap.Strings("registrations", maps.Keys(registrationsMap)), zap.Strings("configured_poets", maps.Keys(nb.poetProvers)), log.ZShortStringer("smesherID", nodeID), ) + return nil, ErrNoRegistrationForGivenPoetFound default: return existingRegistrations, nil } diff --git a/activation/nipost_test.go b/activation/nipost_test.go index cc368c9c44..fe59df97a0 100644 --- a/activation/nipost_test.go +++ b/activation/nipost_test.go @@ -1089,18 +1089,13 @@ func TestNIPoSTBuilder_PoETConfigChange(t *testing.T) { ) require.NoError(t, err) - defer func() { - if r := recover(); r == nil { - t.Errorf("Panic is expected") - } - }() - - nb.submitPoetChallenges( + _, err = nb.submitPoetChallenges( context.Background(), sig, time.Now().Add(10*time.Second), time.Now().Add(-5*time.Second), // poet round started challengeHash.Bytes()) + require.ErrorIs(t, err, ErrNoRegistrationForGivenPoetFound) }) } diff --git a/activation/poet.go b/activation/poet.go index f7b8a3cc31..f9914de804 100644 --- a/activation/poet.go +++ b/activation/poet.go @@ -68,10 +68,12 @@ type PoetClient interface { // HTTPPoetClient implements PoetProvingServiceClient interface. type HTTPPoetClient struct { - id []byte - baseURL *url.URL - client *retryablehttp.Client - logger *zap.Logger + id []byte + baseURL *url.URL + client *retryablehttp.Client + logger *zap.Logger + expectedPhaseShift time.Duration + fetchedPhaseShift time.Duration } func checkRetry(ctx context.Context, resp *http.Response, err error) (bool, error) { @@ -144,24 +146,18 @@ func NewHTTPPoetClient(server types.PoetServer, cfg PoetConfig, opts ...PoetClie } poetClient := &HTTPPoetClient{ - id: server.Pubkey.Bytes(), - baseURL: baseURL, - client: client, - logger: zap.NewNop(), + id: server.Pubkey.Bytes(), + baseURL: baseURL, + client: client, + logger: zap.NewNop(), + expectedPhaseShift: cfg.PhaseShift, } for _, opt := range opts { opt(poetClient) } - resp, err := poetClient.info(context.Background()) - if err != nil { + if err := poetClient.verifyPhaseShiftConfiguration(context.Background()); err != nil { poetClient.logger.Error("getting info about poet service configuration", zap.Error(err)) - } else if resp.PhaseShift.AsDuration() != cfg.PhaseShift { - poetClient.logger.Warn("getting info about poet service configuration", - zap.Duration("server: phase shift", resp.PhaseShift.AsDuration()), - zap.Duration("config: cycle gap", cfg.PhaseShift), - ) - return nil, ErrPhaseShiftMismatch } poetClient.logger.Info( @@ -212,6 +208,25 @@ func (c *HTTPPoetClient) CertifierInfo(ctx context.Context) (*url.URL, []byte, e return url, certifierInfo.Pubkey, nil } +func (c *HTTPPoetClient) verifyPhaseShiftConfiguration(ctx context.Context) error { + if c.fetchedPhaseShift != 0 { + return nil + } + + resp, err := c.info(ctx) + if err != nil { + return err + } else if resp.PhaseShift.AsDuration() != c.expectedPhaseShift { + c.logger.Panic("verifying poet service configuration", + zap.Duration("server: phase shift", resp.PhaseShift.AsDuration()), + zap.Duration("config: phase shift", c.expectedPhaseShift), + ) + } + + c.fetchedPhaseShift = resp.PhaseShift.AsDuration() + return nil +} + // Submit registers a challenge in the proving service current open round. func (c *HTTPPoetClient) Submit( ctx context.Context, @@ -221,6 +236,10 @@ func (c *HTTPPoetClient) Submit( nodeID types.NodeID, auth PoetAuth, ) (*types.PoetRound, error) { + if err := c.verifyPhaseShiftConfiguration(context.Background()); err != nil { + return nil, err + } + request := rpcapi.SubmitRequest{ Prefix: prefix, Challenge: challenge,