diff --git a/CHANGELOG.md b/CHANGELOG.md index f4cc9861323..955f194b398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ See [RELEASE](./RELEASE.md) for workflow instructions. ### Features +* [#4969](https://github.com/spacemeshos/go-spacemesh/pull/4969) Nodes will also fetch from PoET 111 for round 3 if they were able to register to PoET 110. + ### Improvements * [#4965](https://github.com/spacemeshos/go-spacemesh/pull/4965) Updates to PoST: @@ -28,13 +30,13 @@ See [RELEASE](./RELEASE.md) for workflow instructions. Replacement for original version of hare. Won't be enabled on mainnet for now. Otherwise protocol uses significantly less traffic (atlest x20), and will allow -to set lower expected latency in the network, eventually reducing layer time. +to set lower expected latency in the network, eventually reducing layer time. ### Improvements * [#4879](https://github.com/spacemeshos/go-spacemesh/pull/4795) Makes majority calculation weighted for optimistic filtering. The network will start using the new algorithm at layer 18_000 (2023-09-14 20:00:00 +0000 UTC) -* [#4923](https://github.com/spacemeshos/go-spacemesh/pull/4923) Faster ballot eligibility validation. Improves sync speed. +* [#4923](https://github.com/spacemeshos/go-spacemesh/pull/4923) Faster ballot eligibility validation. Improves sync speed. * [#4934](https://github.com/spacemeshos/go-spacemesh/pull/4934) Ensure state is synced before participating in tortoise consensus. * [#4939](https://github.com/spacemeshos/go-spacemesh/pull/4939) Make sure to fetch data from peers that are already connected. * [#4936](https://github.com/spacemeshos/go-spacemesh/pull/4936) Use correct hare active set after node was synced. Otherwise applied layer may lag slightly behind the rest. diff --git a/activation/nipost.go b/activation/nipost.go index 8a77c49cc89..4f2aa71a1d1 100644 --- a/activation/nipost.go +++ b/activation/nipost.go @@ -13,6 +13,7 @@ import ( "github.com/spacemeshos/poet/shared" "github.com/spacemeshos/post/proving" "github.com/spacemeshos/post/verifying" + "golang.org/x/exp/slices" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/activation/metrics" @@ -47,6 +48,8 @@ const ( // proving clients, and thus enormously reduce the cost-per-proof for PoET since each additional proof adds only // a small number of hash evaluations to the total cost. type PoetProvingServiceClient interface { + Address() string + PowParams(ctx context.Context) (*PoetPowParams, error) // Submit registers a challenge in the proving service current open round. @@ -84,7 +87,7 @@ type NIPostBuilder struct { nodeID types.NodeID dataDir string postSetupProvider postSetupProvider - poetProvers []PoetProvingServiceClient + poetProvers map[string]PoetProvingServiceClient poetDB poetDbAPI state *types.NIPostBuilderState log log.Log @@ -105,7 +108,10 @@ func WithNipostValidator(v nipostValidator) NIPostBuilderOption { // withPoetClients allows to pass in clients directly (for testing purposes). func withPoetClients(clients []PoetProvingServiceClient) NIPostBuilderOption { return func(nb *NIPostBuilder) { - nb.poetProvers = clients + nb.poetProvers = make(map[string]PoetProvingServiceClient, len(clients)) + for _, client := range clients { + nb.poetProvers[client.Address()] = client + } } } @@ -127,13 +133,13 @@ func NewNIPostBuilder( layerClock layerClock, opts ...NIPostBuilderOption, ) (*NIPostBuilder, error) { - poetClients := make([]PoetProvingServiceClient, 0, len(poetServers)) + poetClients := make(map[string]PoetProvingServiceClient, len(poetServers)) for _, address := range poetServers { client, err := NewHTTPPoetClient(address, poetCfg) if err != nil { return nil, fmt.Errorf("cannot create poet client: %w", err) } - poetClients = append(poetClients, client) + poetClients[client.Address()] = client } b := &NIPostBuilder{ @@ -168,7 +174,10 @@ func (nb *NIPostBuilder) UpdatePoETProvers(poetProvers []PoetProvingServiceClien nb.state = &types.NIPostBuilderState{ NIPost: &types.NIPost{}, } - nb.poetProvers = poetProvers + nb.poetProvers = make(map[string]PoetProvingServiceClient, len(poetProvers)) + for _, poetProver := range poetProvers { + nb.poetProvers[poetProver.Address()] = poetProver + } nb.log.With().Info("updated poet proof service clients", log.Int("count", len(nb.poetProvers))) } @@ -249,7 +258,7 @@ func (nb *NIPostBuilder) BuildNIPost(ctx context.Context, challenge *types.NIPos } events.EmitPoetWaitProof(challenge.PublishEpoch, challenge.TargetEpoch(), time.Until(poetRoundEnd)) - poetProofRef, membership, err := nb.getBestProof(ctx, nb.state.Challenge) + poetProofRef, membership, err := nb.getBestProof(ctx, nb.state.Challenge, challenge.PublishEpoch) if err != nil { return nil, &PoetSvcUnstableError{msg: "getBestProof failed", source: err} } @@ -397,7 +406,59 @@ func membersContainChallenge(members []types.Member, challenge types.Hash32) (ui return 0, fmt.Errorf("challenge is not a member of the proof") } -func (nb *NIPostBuilder) getBestProof(ctx context.Context, challenge types.Hash32) (types.PoetProofRef, *types.MerkleProof, error) { +// TODO(mafa): remove after epoch 4 ends; https://github.com/spacemeshos/go-spacemesh/issues/4968 +func (nb *NIPostBuilder) addPoet111ForPubEpoch4(ctx context.Context) error { + // because PoET 111 had a hardware issue when challenges for round 3 were submitted, no node could submit to it + // 111 was recovered with the PoET 110 DB, so all successful submissions to 110 should be able to be fetched from there as well + + client111, ok := nb.poetProvers["https://poet-111.spacemesh.network"] + if !ok { + // poet 111 is not in the list, no action necessary + return nil + } + + nb.log.Info("pub epoch 4 mitigation: PoET 111 is in the list of PoETs, adding it to the state as well") + client110 := nb.poetProvers["https://poet-110.spacemesh.network"] + + ID110, err := client110.PoetServiceID(ctx) + if err != nil { + return fmt.Errorf("failed to get PoET 110 id: %w", err) + } + + ID111, err := client111.PoetServiceID(ctx) + if err != nil { + return fmt.Errorf("failed to get PoET 111 id: %w", err) + } + + if slices.IndexFunc(nb.state.PoetRequests, func(r types.PoetRequest) bool { return bytes.Equal(r.PoetServiceID.ServiceID, ID111.ServiceID) }) != -1 { + nb.log.Info("PoET 111 is already in the state, no action necessary") + return nil + } + + poet110 := slices.IndexFunc(nb.state.PoetRequests, func(r types.PoetRequest) bool { + return bytes.Equal(r.PoetServiceID.ServiceID, ID110.ServiceID) + }) + if poet110 == -1 { + return fmt.Errorf("poet 110 is not in the state, cannot add poet 111") + } + + poet111 := nb.state.PoetRequests[poet110] + poet111.PoetServiceID.ServiceID = ID111.ServiceID + nb.state.PoetRequests = append(nb.state.PoetRequests, poet111) + nb.persistState() + nb.log.Info("pub epoch 4 mitigation: PoET 111 added to the state") + return nil +} + +func (nb *NIPostBuilder) getBestProof(ctx context.Context, challenge types.Hash32, publishEpoch types.EpochID) (types.PoetProofRef, *types.MerkleProof, error) { + // TODO(mafa): remove after next PoET round; https://github.com/spacemeshos/go-spacemesh/issues/4968 + if publishEpoch == 4 { + err := nb.addPoet111ForPubEpoch4(ctx) + if err != nil { + nb.log.With().Error("pub epoch 4 mitigation: failed to add PoET 111 to state for pub epoch 4", log.Err(err)) + } + } + type poetProof struct { poet *types.PoetProofMessage membership *types.MerkleProof diff --git a/activation/nipost_mocks.go b/activation/nipost_mocks.go index cac8c8b2f33..cdfce1f4200 100644 --- a/activation/nipost_mocks.go +++ b/activation/nipost_mocks.go @@ -35,6 +35,44 @@ func (m *MockPoetProvingServiceClient) EXPECT() *MockPoetProvingServiceClientMoc return m.recorder } +// Address mocks base method. +func (m *MockPoetProvingServiceClient) Address() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Address") + ret0, _ := ret[0].(string) + return ret0 +} + +// Address indicates an expected call of Address. +func (mr *MockPoetProvingServiceClientMockRecorder) Address() *PoetProvingServiceClientAddressCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Address", reflect.TypeOf((*MockPoetProvingServiceClient)(nil).Address)) + return &PoetProvingServiceClientAddressCall{Call: call} +} + +// PoetProvingServiceClientAddressCall wrap *gomock.Call +type PoetProvingServiceClientAddressCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *PoetProvingServiceClientAddressCall) Return(arg0 string) *PoetProvingServiceClientAddressCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *PoetProvingServiceClientAddressCall) Do(f func() string) *PoetProvingServiceClientAddressCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *PoetProvingServiceClientAddressCall) DoAndReturn(f func() string) *PoetProvingServiceClientAddressCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // PoetServiceID mocks base method. func (m *MockPoetProvingServiceClient) PoetServiceID(arg0 context.Context) (types.PoetServiceID, error) { m.ctrl.T.Helper() diff --git a/activation/nipost_test.go b/activation/nipost_test.go index b7c4bd3835f..fd8ed5a677f 100644 --- a/activation/nipost_test.go +++ b/activation/nipost_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "go.uber.org/zap/zapcore" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/common/types" @@ -21,12 +22,13 @@ import ( "github.com/spacemeshos/go-spacemesh/sql" ) -func defaultPoetServiceMock(tb testing.TB, id []byte) *MockPoetProvingServiceClient { +func defaultPoetServiceMock(tb testing.TB, id []byte, address string) *MockPoetProvingServiceClient { tb.Helper() poetClient := NewMockPoetProvingServiceClient(gomock.NewController(tb)) poetClient.EXPECT().Submit(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&types.PoetRound{}, nil) poetClient.EXPECT().PoetServiceID(gomock.Any()).AnyTimes().Return(types.PoetServiceID{ServiceID: id}, nil) poetClient.EXPECT().PowParams(gomock.Any()).AnyTimes().Return(&PoetPowParams{}, nil) + poetClient.EXPECT().Address().AnyTimes().Return(address).AnyTimes() return poetClient } @@ -59,7 +61,7 @@ func TestNIPostBuilderWithMocks(t *testing.T) { nipostValidator := NewMocknipostValidator(ctrl) nipostValidator.EXPECT().Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) - poetProvider := defaultPoetServiceMock(t, []byte("poet")) + poetProvider := defaultPoetServiceMock(t, []byte("poet"), "http://localhost:9999") poetProvider.EXPECT().Proof(gomock.Any(), "").Return(&types.PoetProofMessage{ PoetProof: types.PoetProof{}, }, []types.Member{types.Member(challenge.Hash())}, nil) @@ -99,7 +101,7 @@ func TestPostSetup(t *testing.T) { PublishEpoch: postGenesisEpoch + 2, } - poetProvider := defaultPoetServiceMock(t, []byte("poet")) + poetProvider := defaultPoetServiceMock(t, []byte("poet"), "http://localhost:9999") poetProvider.EXPECT().Proof(gomock.Any(), "").Return(&types.PoetProofMessage{ PoetProof: types.PoetProof{}, }, []types.Member{types.Member(challenge.Hash())}, nil) @@ -328,7 +330,7 @@ func TestNIPostBuilder_BuildNIPost(t *testing.T) { Sequence: 2, } - poetProver := defaultPoetServiceMock(t, []byte("poet")) + poetProver := defaultPoetServiceMock(t, []byte("poet"), "http://localhost:9999") poetProver.EXPECT().Proof(gomock.Any(), "").AnyTimes().Return( &types.PoetProofMessage{ PoetProof: types.PoetProof{}, @@ -454,6 +456,7 @@ func TestNIPostBuilder_ManyPoETs_SubmittingChallenge_DeadlineReached(t *testing. }, ) poet.EXPECT().PowParams(gomock.Any()).Return(&PoetPowParams{}, nil) + poet.EXPECT().Address().Return("http://localhost:9999") poets = append(poets, poet) } { @@ -462,6 +465,7 @@ func TestNIPostBuilder_ManyPoETs_SubmittingChallenge_DeadlineReached(t *testing. poet.EXPECT().Submit(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.PoetRound{}, nil) poet.EXPECT().Proof(gomock.Any(), gomock.Any()).Return(proof, []types.Member{types.Member(challenge.Hash())}, nil) poet.EXPECT().PowParams(gomock.Any()).Return(&PoetPowParams{}, nil) + poet.EXPECT().Address().Return("http://localhost:9998") poets = append(poets, poet) } @@ -534,12 +538,12 @@ func TestNIPostBuilder_ManyPoETs_AllFinished(t *testing.T) { poets := make([]PoetProvingServiceClient, 0, 2) { - poet := defaultPoetServiceMock(t, []byte("poet0")) + poet := defaultPoetServiceMock(t, []byte("poet0"), "http://localhost:9999") poet.EXPECT().Proof(gomock.Any(), "").Return(proofWorse, []types.Member{types.Member(challenge.Hash())}, nil) poets = append(poets, poet) } { - poet := defaultPoetServiceMock(t, []byte("poet1")) + poet := defaultPoetServiceMock(t, []byte("poet1"), "http://localhost:9998") poet.EXPECT().Proof(gomock.Any(), "").Return(proofBetter, []types.Member{types.Member(challenge.Hash())}, nil) poets = append(poets, poet) } @@ -641,6 +645,7 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { postProver.EXPECT().Status().Return(&PostSetupStatus{State: PostSetupStateComplete}) poetProver := NewMockPoetProvingServiceClient(ctrl) poetProver.EXPECT().PoetServiceID(gomock.Any()).AnyTimes().Return(types.PoetServiceID{}, errors.New("test")) + poetProver.EXPECT().Address().Return("http://localhost:9999") nb, err := NewNIPostBuilder( nodeID, @@ -670,6 +675,7 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { poetProver.EXPECT().PoetServiceID(gomock.Any()).AnyTimes().Return(types.PoetServiceID{ServiceID: []byte{}}, nil) poetProver.EXPECT().Submit(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test")) poetProver.EXPECT().PowParams(gomock.Any()).Return(&PoetPowParams{}, nil) + poetProver.EXPECT().Address().Return("http://localhost:9999") nb, err := NewNIPostBuilder( nodeID, @@ -705,6 +711,7 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { }, ) poetProver.EXPECT().PowParams(gomock.Any()).Return(&PoetPowParams{}, nil) + poetProver.EXPECT().Address().Return("http://localhost:9999") nb, err := NewNIPostBuilder( nodeID, @@ -730,7 +737,7 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { mclock := defaultLayerClockMock(t) postProver := NewMockpostSetupProvider(ctrl) postProver.EXPECT().Status().Return(&PostSetupStatus{State: PostSetupStateComplete}) - poetProver := defaultPoetServiceMock(t, []byte("poet")) + poetProver := defaultPoetServiceMock(t, []byte("poet"), "http://localhost:9999") poetProver.EXPECT().Proof(gomock.Any(), "").Return(nil, nil, errors.New("failed")) nb, err := NewNIPostBuilder( @@ -758,7 +765,7 @@ func TestNIPSTBuilder_PoetUnstable(t *testing.T) { mclock := defaultLayerClockMock(t) postProver := NewMockpostSetupProvider(ctrl) postProver.EXPECT().Status().Return(&PostSetupStatus{State: PostSetupStateComplete}) - poetProver := defaultPoetServiceMock(t, []byte("poet")) + poetProver := defaultPoetServiceMock(t, []byte("poet"), "http://localhost:9999") poetProver.EXPECT().Proof(gomock.Any(), "").Return(&types.PoetProofMessage{PoetProof: types.PoetProof{}}, []types.Member{}, nil) nb, err := NewNIPostBuilder( @@ -797,6 +804,7 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { poetDb := NewMockpoetDbAPI(ctrl) mclock := NewMocklayerClock(ctrl) poetProver := NewMockPoetProvingServiceClient(ctrl) + poetProver.EXPECT().Address().Return("http://localhost:9999") postProver := NewMockpostSetupProvider(ctrl) postProver.EXPECT().Status().Return(&PostSetupStatus{State: PostSetupStateComplete}) mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( @@ -829,6 +837,7 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { poetDb := NewMockpoetDbAPI(ctrl) mclock := NewMocklayerClock(ctrl) poetProver := NewMockPoetProvingServiceClient(ctrl) + poetProver.EXPECT().Address().Return("http://localhost:9999") postProver := NewMockpostSetupProvider(ctrl) postProver.EXPECT().Status().Return(&PostSetupStatus{State: PostSetupStateComplete}) mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( @@ -867,6 +876,7 @@ func TestNIPoSTBuilder_StaleChallenge(t *testing.T) { poetDb := NewMockpoetDbAPI(ctrl) mclock := NewMocklayerClock(ctrl) poetProver := NewMockPoetProvingServiceClient(ctrl) + poetProver.EXPECT().Address().Return("http://localhost:9999") postProver := NewMockpostSetupProvider(ctrl) postProver.EXPECT().Status().Return(&PostSetupStatus{State: PostSetupStateComplete}) mclock.EXPECT().LayerToTime(gomock.Any()).DoAndReturn( @@ -937,6 +947,7 @@ func TestNIPoSTBuilder_Continues_After_Interrupted(t *testing.T) { ) poet.EXPECT().PowParams(gomock.Any()).Times(2).Return(&PoetPowParams{}, nil) poet.EXPECT().Proof(gomock.Any(), "").Return(proof, []types.Member{types.Member(challenge.Hash())}, nil) + poet.EXPECT().Address().Return("http://localhost:9999") sig, err := signing.NewEdSigner() req.NoError(err) @@ -1032,6 +1043,90 @@ func TestConstructingMerkleProof(t *testing.T) { }) } +// TODO(mafa): remove after epoch 4 end; https://github.com/spacemeshos/go-spacemesh/issues/4968 +func TestNIPostBuilder_Mainnet_PoetRound3_Workaround(t *testing.T) { + t.Parallel() + + challenge := types.NIPostChallenge{ + PublishEpoch: 4, + } + + ctrl := gomock.NewController(t) + postProvider := NewMockpostSetupProvider(ctrl) + postProvider.EXPECT().Status().Return(&PostSetupStatus{State: PostSetupStateComplete}) + postProvider.EXPECT().CommitmentAtx().Return(types.EmptyATXID, nil).AnyTimes() + postProvider.EXPECT().LastOpts().Return(&PostSetupOpts{}).AnyTimes() + postProvider.EXPECT().GenerateProof(gomock.Any(), gomock.Any(), gomock.Any()) + + nipostValidator := NewMocknipostValidator(ctrl) + nipostValidator.EXPECT().Post(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + + poets := make([]PoetProvingServiceClient, 0, 2) + { + poetProvider := NewMockPoetProvingServiceClient(ctrl) + poetProvider.EXPECT().Address().Return("https://poet-110.spacemesh.network") + poetProvider.EXPECT().PoetServiceID(gomock.Any()).Return(types.PoetServiceID{ServiceID: []byte("poet-110")}, nil).AnyTimes() + poetProvider.EXPECT().PowParams(gomock.Any()).AnyTimes().Return(&PoetPowParams{}, nil) + + // PoET 110 succeeds to submit + poetProvider.EXPECT().Submit(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.PoetRound{}, nil) + + // proof is fetched from PoET 110 + poetProvider.EXPECT().Proof(gomock.Any(), "").Return(&types.PoetProofMessage{ + PoetProof: types.PoetProof{}, + }, []types.Member{types.Member(challenge.Hash())}, nil) + poets = append(poets, poetProvider) + } + + { + // PoET 111 fails submission + poetProvider := NewMockPoetProvingServiceClient(ctrl) + poetProvider.EXPECT().Address().Return("https://poet-111.spacemesh.network") + poetProvider.EXPECT().PoetServiceID(gomock.Any()).Return(types.PoetServiceID{}, errors.New("failed submission")) + + // proof is still fetched from PoET 111 + poetProvider.EXPECT().PoetServiceID(gomock.Any()).Return(types.PoetServiceID{ServiceID: []byte("poet-111")}, nil).AnyTimes() + poetProvider.EXPECT().Proof(gomock.Any(), "").Return(&types.PoetProofMessage{ + PoetProof: types.PoetProof{}, + }, []types.Member{types.Member(challenge.Hash())}, nil) + + poets = append(poets, poetProvider) + } + + poetDb := NewMockpoetDbAPI(ctrl) + poetDb.EXPECT().ValidateAndStore(gomock.Any(), gomock.Any()).Return(nil).Times(2) + mclock := NewMocklayerClock(ctrl) + genesis := time.Now().Add(-time.Duration(1) * layerDuration) + mclock.EXPECT().LayerToTime(gomock.Any()).AnyTimes().DoAndReturn( + func(got types.LayerID) time.Time { + return genesis.Add(layerDuration * time.Duration(got)) + }, + ) + + sig, err := signing.NewEdSigner() + require.NoError(t, err) + poetCfg := PoetConfig{ + PhaseShift: layerDuration * layersPerEpoch / 2, + } + nb, err := NewNIPostBuilder( + types.NodeID{1}, + postProvider, + poetDb, + []string{}, + t.TempDir(), + logtest.New(t, zapcore.DebugLevel), + sig, + poetCfg, + mclock, + WithNipostValidator(nipostValidator), + withPoetClients(poets), + ) + require.NoError(t, err) + nipost, err := nb.BuildNIPost(context.Background(), &challenge) + require.NoError(t, err) + require.NotNil(t, nipost) +} + func FuzzBuilderStateConsistency(f *testing.F) { tester.FuzzConsistency[types.NIPostBuilderState](f) } diff --git a/activation/poet.go b/activation/poet.go index bc4824dbe92..6e2376fd4e0 100644 --- a/activation/poet.go +++ b/activation/poet.go @@ -93,6 +93,10 @@ func NewHTTPPoetClient(baseUrl string, cfg PoetConfig, opts ...PoetClientOpts) ( return poetClient, nil } +func (c *HTTPPoetClient) Address() string { + return c.baseURL.String() +} + func (c *HTTPPoetClient) PowParams(ctx context.Context) (*PoetPowParams, error) { resBody := rpcapi.PowParamsResponse{} if err := c.req(ctx, http.MethodGet, "/v1/pow_params", nil, &resBody); err != nil { diff --git a/activation/poet_client_test.go b/activation/poet_client_test.go index c57067b71b1..fb58a43dffc 100644 --- a/activation/poet_client_test.go +++ b/activation/poet_client_test.go @@ -60,6 +60,52 @@ func Test_HTTPPoetClient_Submit(t *testing.T) { require.NoError(t, err) } +func Test_HTTPPoetClient_Address(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, http.MethodGet, r.Method) + + w.WriteHeader(http.StatusOK) + resp, err := protojson.Marshal(&rpcapi.InfoResponse{}) + require.NoError(t, err) + w.Write(resp) + + require.Equal(t, "/v1/info", r.URL.Path) + })) + defer ts.Close() + + cfg := config.DefaultConfig() + client, err := NewHTTPPoetClient(ts.URL, PoetConfig{ + PhaseShift: cfg.Service.PhaseShift, + CycleGap: cfg.Service.CycleGap, + }, withCustomHttpClient(ts.Client())) + require.NoError(t, err) + + require.Equal(t, ts.URL, client.Address()) +} + +func Test_HTTPPoetClient_Address_Mainnet(t *testing.T) { + poetCfg := config.DefaultConfig() + + poETServers := []string{ + "https://mainnet-poet-0.spacemesh.network", + "https://mainnet-poet-1.spacemesh.network", + "https://mainnet-poet-2.spacemesh.network", + "https://poet-110.spacemesh.network", + "https://poet-111.spacemesh.network", + } + + for _, url := range poETServers { + t.Run(url, func(t *testing.T) { + client, err := NewHTTPPoetClient(url, PoetConfig{ + PhaseShift: poetCfg.Service.PhaseShift, + CycleGap: poetCfg.Service.CycleGap, + }) + require.NoError(t, err) + require.Equal(t, url, client.Address()) + }) + } +} + func Test_HTTPPoetClient_Proof(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, http.MethodGet, r.Method)