From 9e13ad74a8d4c7f5c95f15549b12a42d5e1262c2 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:56:10 -0600 Subject: [PATCH 01/21] remote hare --- hare3/eligibility/oracle.go | 10 +- hare3/remote.go | 207 ++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 hare3/remote.go diff --git a/hare3/eligibility/oracle.go b/hare3/eligibility/oracle.go index c33cf0c50b..1880a0df73 100644 --- a/hare3/eligibility/oracle.go +++ b/hare3/eligibility/oracle.go @@ -94,6 +94,7 @@ type Oracle struct { beacons system.BeaconGetter atxsdata *atxsdata.Data + minerWeightFn func(ctx context.Context, layer types.LayerID, id types.NodeID) (uint64, error) db sql.Executor vrfVerifier vrfVerifier layersPerEpoch uint32 @@ -103,6 +104,12 @@ type Oracle struct { type Opt func(*Oracle) +func WithMinerWeightFunc(f func(ctx context.Context, layer types.LayerID, id types.NodeID) (uint64, error)) Opt { + return func(o *Oracle) { + o.minerWeightFn = f + } +} + func WithConfig(config Config) Opt { return func(o *Oracle) { o.cfg = config @@ -139,6 +146,7 @@ func New( cfg: DefaultConfig(), log: zap.NewNop(), } + oracle.minerWeightFn = oracle.minerWeight for _, opt := range opts { opt(oracle) } @@ -235,7 +243,7 @@ func (o *Oracle) prepareEligibilityCheck( // calc hash & check threshold // this is cheap in case the node is not eligible - minerWeight, err := o.minerWeight(ctx, layer, id) + minerWeight, err := o.minerWeightFn(ctx, layer, id) if err != nil { return 0, fixed.Fixed{}, fixed.Fixed{}, true, err } diff --git a/hare3/remote.go b/hare3/remote.go new file mode 100644 index 0000000000..49c64a7276 --- /dev/null +++ b/hare3/remote.go @@ -0,0 +1,207 @@ +package hare3 + +import ( + "errors" + "fmt" + "math" + "sync" + "time" + + "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/hare3/eligibility" + "github.com/spacemeshos/go-spacemesh/log" + "github.com/spacemeshos/go-spacemesh/signing" + "go.uber.org/zap" + "golang.org/x/exp/maps" +) + +type RemoteHare struct { + eligibility *eligibility.Oracle + nodeClock nodeClock + mu sync.Mutex + beacons map[types.EpochID]types.Beacon + signers map[string]*signing.EdSigner + oracle *legacyOracle + sessions map[types.LayerID]*protocol + + log *zap.Logger +} + +func NewRemoteHare() *RemoteHare { + return &RemoteHare{} +} + +func (h *RemoteHare) Register(sig *signing.EdSigner) { + h.mu.Lock() + defer h.mu.Unlock() + h.log.Info("registered signing key", log.ZShortStringer("id", sig.NodeID())) + h.signers[string(sig.NodeID().Bytes())] = sig +} + +func (h *RemoteHare) Start() { + current := h.nodeClock.CurrentLayer() + 1 + enableLayer = 0 + enabled := max(current, enableLayer /* h.config.EnableLayer*/, types.GetEffectiveGenesis()+1) + disabled := types.LayerID(math.MaxUint32) + if h.config.DisableLayer > 0 { + disabled = h.config.DisableLayer + } + h.log.Info("started", + // zap.Inline(&h.config), + zap.Uint32("enabled", enabled.Uint32()), + zap.Uint32("disabled", disabled.Uint32()), + ) + h.eg.Go(func() error { + for next := enabled; next < disabled; next++ { + select { + case <-h.nodeClock.AwaitLayer(next): + h.log.Debug("notified", zap.Uint32("layer", next.Uint32())) + h.onLayer(next) + h.cleanMessageCache(next - 1) + case <-h.ctx.Done(): + return nil + } + } + return nil + }) +} + +func (h *RemoteHare) beacon(e types.EpochID) types.Beacon { + h.mu.Lock() + defer h.mu.Unlock() + b, ok := h.beacons[e] + if !ok { + return types.EmptyBeacon + } + + return b +} + +func (h *RemoteHare) onLayer(layer types.LayerID) { + beacon, err := h.beacon(layer.GetEpoch()) + if err != nil || beacon == types.EmptyBeacon { + h.log.Debug("no beacon", + zap.Uint32("epoch", layer.GetEpoch().Uint32()), + zap.Uint32("lid", layer.Uint32()), + zap.Error(err), + ) + return + } + + h.mu.Lock() + // signer can't join mid session + s := &session{ + lid: layer, + beacon: beacon, + signers: maps.Values(h.signers), + vrfs: make([]*types.HareEligibility, len(h.signers)), + proto: newProtocol(h.config.CommitteeFor(layer)/2 + 1), + } + h.sessions[layer] = s.proto + h.mu.Unlock() + + sessionStart.Inc() + h.tracer.OnStart(layer) + h.log.Debug("registered layer", zap.Uint32("lid", layer.Uint32())) + h.eg.Go(func() error { + if err := h.run(s); err != nil { + h.log.Warn("failed", + zap.Uint32("lid", layer.Uint32()), + zap.Error(err), + ) + exitErrors.Inc() + // if terminated successfully it will notify block generator + // and it will have to CompleteHare + } else { + h.log.Debug("terminated", + zap.Uint32("lid", layer.Uint32()), + ) + } + h.mu.Lock() + delete(h.sessions, layer) + h.mu.Unlock() + sessionTerminated.Inc() + return nil + }) +} + +func (h *RemoteHare) selectProposals(session *session) error { +} + +func (h *RemoteHare) run(session *session) error { + var ( + current = IterRound{Round: preround} + start = time.Now() + active bool + ) + for i := range session.signers { + session.vrfs[i] = h.oracle.active(session.signers[i], session.beacon, session.lid, current) + active = active || session.vrfs[i] != nil + } + activeLatency.Observe(time.Since(start).Seconds()) + + walltime := h.nodeClock.LayerToTime(session.lid).Add(h.config.PreroundDelay) + if active { + h.log.Debug("active in preround. waiting for preround delay", zap.Uint32("lid", session.lid.Uint32())) + // initial set is not needed if node is not active in preround + select { + case <-h.wallClock.After(walltime.Sub(h.wallClock.Now())): + case <-h.ctx.Done(): + return h.ctx.Err() + } + start := time.Now() + session.proto.OnInitial(h.selectProposals(session)) + proposalsLatency.Observe(time.Since(start).Seconds()) + } + if err := h.onOutput(session, current, session.proto.Next()); err != nil { + return err + } + result := false + for { + walltime = walltime.Add(h.config.RoundDuration) + current = session.proto.IterRound + start = time.Now() + + for i := range session.signers { + if current.IsMessageRound() { + session.vrfs[i] = h.oracle.active(session.signers[i], session.beacon, session.lid, current) + } else { + session.vrfs[i] = nil + } + } + h.tracer.OnActive(session.vrfs) + activeLatency.Observe(time.Since(start).Seconds()) + + select { + case <-h.wallClock.After(walltime.Sub(h.wallClock.Now())): + h.log.Debug("execute round", + zap.Uint32("lid", session.lid.Uint32()), + zap.Uint8("iter", session.proto.Iter), zap.Stringer("round", session.proto.Round), + zap.Bool("active", active), + ) + out := session.proto.Next() + if out.result != nil { + result = true + } + if err := h.onOutput(session, current, out); err != nil { + return err + } + // we are logginng stats 1 network delay after new iteration start + // so that we can receive notify messages from previous iteration + if session.proto.Round == softlock && h.config.LogStats { + h.log.Debug("stats", zap.Uint32("lid", session.lid.Uint32()), zap.Inline(session.proto.Stats())) + } + if out.terminated { + if !result { + return errors.New("terminated without result") + } + return nil + } + if current.Iter == h.config.IterationsLimit { + return fmt.Errorf("hare failed to reach consensus in %d iterations", h.config.IterationsLimit) + } + case <-h.ctx.Done(): + return nil + } + } +} From 1c1989adca4e9f0d72bf605f6472d51f9dc0204f Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:17:47 -0600 Subject: [PATCH 02/21] wip wip change api schema and generate compiles builds again add publish, go gen wip --- api/node/client/client.gen.go | 200 ++++++++++++++++++++++++++++++++ api/node/client/client.go | 22 ++++ api/node/models/components.yaml | 6 + api/node/models/models.gen.go | 6 + api/node/node_service.yaml | 48 ++++++++ api/node/server/mocks.go | 62 ++++++++++ api/node/server/server.gen.go | 175 ++++++++++++++++++++++++++++ api/node/server/server.go | 32 +++++ hare3/hare.go | 49 +++++--- hare3/remote.go | 135 +++++++++++++++------ 10 files changed, 682 insertions(+), 53 deletions(-) diff --git a/api/node/client/client.gen.go b/api/node/client/client.gen.go index e528badc57..8af76d234c 100644 --- a/api/node/client/client.gen.go +++ b/api/node/client/client.gen.go @@ -115,6 +115,12 @@ type ClientInterface interface { // GetActivationPositioningAtxPublishEpoch request GetActivationPositioningAtxPublishEpoch(ctx context.Context, publishEpoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostHarePublishWithBody request with any body + PostHarePublishWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetHareRoundTemplateLayerRoundWithBody request with any body + GetHareRoundTemplateLayerRoundWithBody(ctx context.Context, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostPoetWithBody request with any body PostPoetWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -158,6 +164,30 @@ func (c *Client) GetActivationPositioningAtxPublishEpoch(ctx context.Context, pu return c.Client.Do(req) } +func (c *Client) PostHarePublishWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostHarePublishRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetHareRoundTemplateLayerRoundWithBody(ctx context.Context, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHareRoundTemplateLayerRoundRequestWithBody(c.Server, layer, round, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostPoetWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostPoetRequestWithBody(c.Server, contentType, body) if err != nil { @@ -284,6 +314,78 @@ func NewGetActivationPositioningAtxPublishEpochRequest(server string, publishEpo return req, nil } +// NewPostHarePublishRequestWithBody generates requests for PostHarePublish with any type of body +func NewPostHarePublishRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/hare/publish") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetHareRoundTemplateLayerRoundRequestWithBody generates requests for GetHareRoundTemplateLayerRound with any type of body +func NewGetHareRoundTemplateLayerRoundRequestWithBody(server string, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "layer", runtime.ParamLocationPath, layer) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "round", runtime.ParamLocationPath, round) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/hare/round_template/%s/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewPostPoetRequestWithBody generates requests for PostPoet with any type of body func NewPostPoetRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error @@ -401,6 +503,12 @@ type ClientWithResponsesInterface interface { // GetActivationPositioningAtxPublishEpochWithResponse request GetActivationPositioningAtxPublishEpochWithResponse(ctx context.Context, publishEpoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*GetActivationPositioningAtxPublishEpochResponse, error) + // PostHarePublishWithBodyWithResponse request with any body + PostHarePublishWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostHarePublishResponse, error) + + // GetHareRoundTemplateLayerRoundWithBodyWithResponse request with any body + GetHareRoundTemplateLayerRoundWithBodyWithResponse(ctx context.Context, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerRoundResponse, error) + // PostPoetWithBodyWithResponse request with any body PostPoetWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPoetResponse, error) @@ -476,6 +584,48 @@ func (r GetActivationPositioningAtxPublishEpochResponse) StatusCode() int { return 0 } +type PostHarePublishResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r PostHarePublishResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostHarePublishResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetHareRoundTemplateLayerRoundResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r GetHareRoundTemplateLayerRoundResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetHareRoundTemplateLayerRoundResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type PostPoetResponse struct { Body []byte HTTPResponse *http.Response @@ -545,6 +695,24 @@ func (c *ClientWithResponses) GetActivationPositioningAtxPublishEpochWithRespons return ParseGetActivationPositioningAtxPublishEpochResponse(rsp) } +// PostHarePublishWithBodyWithResponse request with arbitrary body returning *PostHarePublishResponse +func (c *ClientWithResponses) PostHarePublishWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostHarePublishResponse, error) { + rsp, err := c.PostHarePublishWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostHarePublishResponse(rsp) +} + +// GetHareRoundTemplateLayerRoundWithBodyWithResponse request with arbitrary body returning *GetHareRoundTemplateLayerRoundResponse +func (c *ClientWithResponses) GetHareRoundTemplateLayerRoundWithBodyWithResponse(ctx context.Context, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerRoundResponse, error) { + rsp, err := c.GetHareRoundTemplateLayerRoundWithBody(ctx, layer, round, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetHareRoundTemplateLayerRoundResponse(rsp) +} + // PostPoetWithBodyWithResponse request with arbitrary body returning *PostPoetResponse func (c *ClientWithResponses) PostPoetWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPoetResponse, error) { rsp, err := c.PostPoetWithBody(ctx, contentType, body, reqEditors...) @@ -643,6 +811,38 @@ func ParseGetActivationPositioningAtxPublishEpochResponse(rsp *http.Response) (* return response, nil } +// ParsePostHarePublishResponse parses an HTTP response from a PostHarePublishWithResponse call +func ParsePostHarePublishResponse(rsp *http.Response) (*PostHarePublishResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostHarePublishResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetHareRoundTemplateLayerRoundResponse parses an HTTP response from a GetHareRoundTemplateLayerRoundWithResponse call +func ParseGetHareRoundTemplateLayerRoundResponse(rsp *http.Response) (*GetHareRoundTemplateLayerRoundResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetHareRoundTemplateLayerRoundResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + // ParsePostPoetResponse parses an HTTP response from a PostPoetWithResponse call func ParsePostPoetResponse(rsp *http.Response) (*PostPoetResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/api/node/client/client.go b/api/node/client/client.go index dab49ef641..3d0a33b9db 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -5,6 +5,7 @@ import ( "context" "encoding/hex" "fmt" + "io" "net/http" "time" @@ -13,9 +14,11 @@ import ( "github.com/spacemeshos/go-spacemesh/activation" "github.com/spacemeshos/go-spacemesh/api/node/models" + externalRef0 "github.com/spacemeshos/go-spacemesh/api/node/models" "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common" "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/hare3" "github.com/spacemeshos/go-spacemesh/p2p/pubsub" ) @@ -124,3 +127,22 @@ func (s *NodeService) StorePoetProof(ctx context.Context, proof *types.PoetProof } return nil } + +func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, round hare3.Round) ([]byte, error) { + resp, err := s.client.GetHareRoundTemplateLayerRoundWithBody(ctx, externalRef0.LayerID(layer), externalRef0.HareRound(round), "application/octet-stream", nil) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status: %s", resp.Status) + } + bytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read all: %w", err) + } + return bytes, nil +} + +func (s *NodeService) PublishHareMessage(ctx context.Context, msg []byte) error { + return nil +} diff --git a/api/node/models/components.yaml b/api/node/models/components.yaml index 4e445f0e57..e0c9fd586c 100644 --- a/api/node/models/components.yaml +++ b/api/node/models/components.yaml @@ -42,3 +42,9 @@ components: EpochID: type: integer format: uint32 + LayerID: + type: integer + format: uint64 + HareRound: + type: integer + format: uint8 diff --git a/api/node/models/models.gen.go b/api/node/models/models.gen.go index ccae04b0c8..0973c97d91 100644 --- a/api/node/models/models.gen.go +++ b/api/node/models/models.gen.go @@ -20,5 +20,11 @@ type ActivationTx struct { // EpochID defines model for EpochID. type EpochID = uint32 +// HareRound defines model for HareRound. +type HareRound = uint8 + +// LayerID defines model for LayerID. +type LayerID = uint64 + // NodeID defines model for NodeID. type NodeID = string diff --git a/api/node/node_service.yaml b/api/node/node_service.yaml index 608a7ee398..8ca6c0d687 100644 --- a/api/node/node_service.yaml +++ b/api/node/node_service.yaml @@ -122,3 +122,51 @@ paths: plain/text: schema: type: string + /hare/round_template/{layer}/{round}: + get: + summary: Get a hare message to sign + tags: + - "hare" + parameters: + - in: path + name: layer + required: true + schema: + $ref: "models/components.yaml#/components/schemas/LayerID" + - in: path + name: round + required: true + schema: + $ref: "models/components.yaml#/components/schemas/HareRound" + requestBody: + required: false + responses: + "200": + description: successfully found the message to return to client + content: + application/octet-stream: + schema: + type: string + format: binary + "204": + description: did not find a message to retrieve + /hare/publish: + post: + summary: Publish a signed hare message + tags: + - "hare" + requestBody: + required: true + description: hare message to publish serialize in binary form + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + "202": + description: successfully accepted the message and queued it to be published over pubsub + "500": + description: could not process request + + diff --git a/api/node/server/mocks.go b/api/node/server/mocks.go index e6885b33ae..fdcbace72f 100644 --- a/api/node/server/mocks.go +++ b/api/node/server/mocks.go @@ -14,6 +14,7 @@ import ( reflect "reflect" types "github.com/spacemeshos/go-spacemesh/common/types" + hare3 "github.com/spacemeshos/go-spacemesh/hare3" gomock "go.uber.org/mock/gomock" ) @@ -77,3 +78,64 @@ func (c *MockpoetDBValidateAndStoreCall) DoAndReturn(f func(context.Context, *ty c.Call = c.Call.DoAndReturn(f) return c } + +// Mockhare is a mock of hare interface. +type Mockhare struct { + ctrl *gomock.Controller + recorder *MockhareMockRecorder +} + +// MockhareMockRecorder is the mock recorder for Mockhare. +type MockhareMockRecorder struct { + mock *Mockhare +} + +// NewMockhare creates a new mock instance. +func NewMockhare(ctrl *gomock.Controller) *Mockhare { + mock := &Mockhare{ctrl: ctrl} + mock.recorder = &MockhareMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *Mockhare) EXPECT() *MockhareMockRecorder { + return m.recorder +} + +// RoundMessage mocks base method. +func (m *Mockhare) RoundMessage(layer types.LayerID, round hare3.Round) *hare3.Message { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RoundMessage", layer, round) + ret0, _ := ret[0].(*hare3.Message) + return ret0 +} + +// RoundMessage indicates an expected call of RoundMessage. +func (mr *MockhareMockRecorder) RoundMessage(layer, round any) *MockhareRoundMessageCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoundMessage", reflect.TypeOf((*Mockhare)(nil).RoundMessage), layer, round) + return &MockhareRoundMessageCall{Call: call} +} + +// MockhareRoundMessageCall wrap *gomock.Call +type MockhareRoundMessageCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockhareRoundMessageCall) Return(arg0 *hare3.Message) *MockhareRoundMessageCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockhareRoundMessageCall) Do(f func(types.LayerID, hare3.Round) *hare3.Message) *MockhareRoundMessageCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockhareRoundMessageCall) DoAndReturn(f func(types.LayerID, hare3.Round) *hare3.Message) *MockhareRoundMessageCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/api/node/server/server.gen.go b/api/node/server/server.gen.go index 64ed4825d0..74970448e9 100644 --- a/api/node/server/server.gen.go +++ b/api/node/server/server.gen.go @@ -45,6 +45,12 @@ type ServerInterface interface { // Get Positioning ATX ID with given maximum publish epoch // (GET /activation/positioning_atx/{publish_epoch}) GetActivationPositioningAtxPublishEpoch(w http.ResponseWriter, r *http.Request, publishEpoch externalRef0.EpochID) + // Publish a signed hare message + // (POST /hare/publish) + PostHarePublish(w http.ResponseWriter, r *http.Request) + // Get a hare message to sign + // (GET /hare/round_template/{layer}/{round}) + GetHareRoundTemplateLayerRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, round externalRef0.HareRound) // Store PoET proof // (POST /poet) PostPoet(w http.ResponseWriter, r *http.Request) @@ -137,6 +143,54 @@ func (siw *ServerInterfaceWrapper) GetActivationPositioningAtxPublishEpoch(w htt handler.ServeHTTP(w, r) } +// PostHarePublish operation middleware +func (siw *ServerInterfaceWrapper) PostHarePublish(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.PostHarePublish(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetHareRoundTemplateLayerRound operation middleware +func (siw *ServerInterfaceWrapper) GetHareRoundTemplateLayerRound(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "layer" ------------- + var layer externalRef0.LayerID + + err = runtime.BindStyledParameterWithOptions("simple", "layer", r.PathValue("layer"), &layer, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "layer", Err: err}) + return + } + + // ------------- Path parameter "round" ------------- + var round externalRef0.HareRound + + err = runtime.BindStyledParameterWithOptions("simple", "round", r.PathValue("round"), &round, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "round", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetHareRoundTemplateLayerRound(w, r, layer, round) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // PostPoet operation middleware func (siw *ServerInterfaceWrapper) PostPoet(w http.ResponseWriter, r *http.Request) { @@ -299,6 +353,8 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc("GET "+options.BaseURL+"/activation/atx/{atx_id}", wrapper.GetActivationAtxAtxId) m.HandleFunc("GET "+options.BaseURL+"/activation/last_atx/{node_id}", wrapper.GetActivationLastAtxNodeId) m.HandleFunc("GET "+options.BaseURL+"/activation/positioning_atx/{publish_epoch}", wrapper.GetActivationPositioningAtxPublishEpoch) + m.HandleFunc("POST "+options.BaseURL+"/hare/publish", wrapper.PostHarePublish) + m.HandleFunc("GET "+options.BaseURL+"/hare/round_template/{layer}/{round}", wrapper.GetHareRoundTemplateLayerRound) m.HandleFunc("POST "+options.BaseURL+"/poet", wrapper.PostPoet) m.HandleFunc("POST "+options.BaseURL+"/publish/{protocol}", wrapper.PostPublishProtocol) @@ -393,6 +449,66 @@ func (response GetActivationPositioningAtxPublishEpoch200JSONResponse) VisitGetA return json.NewEncoder(w).Encode(response) } +type PostHarePublishRequestObject struct { + Body io.Reader +} + +type PostHarePublishResponseObject interface { + VisitPostHarePublishResponse(w http.ResponseWriter) error +} + +type PostHarePublish202Response struct { +} + +func (response PostHarePublish202Response) VisitPostHarePublishResponse(w http.ResponseWriter) error { + w.WriteHeader(202) + return nil +} + +type PostHarePublish500Response struct { +} + +func (response PostHarePublish500Response) VisitPostHarePublishResponse(w http.ResponseWriter) error { + w.WriteHeader(500) + return nil +} + +type GetHareRoundTemplateLayerRoundRequestObject struct { + Layer externalRef0.LayerID `json:"layer"` + Round externalRef0.HareRound `json:"round"` +} + +type GetHareRoundTemplateLayerRoundResponseObject interface { + VisitGetHareRoundTemplateLayerRoundResponse(w http.ResponseWriter) error +} + +type GetHareRoundTemplateLayerRound200ApplicationoctetStreamResponse struct { + Body io.Reader + ContentLength int64 +} + +func (response GetHareRoundTemplateLayerRound200ApplicationoctetStreamResponse) VisitGetHareRoundTemplateLayerRoundResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/octet-stream") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type GetHareRoundTemplateLayerRound204Response struct { +} + +func (response GetHareRoundTemplateLayerRound204Response) VisitGetHareRoundTemplateLayerRoundResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + type PostPoetRequestObject struct { Body io.Reader } @@ -456,6 +572,12 @@ type StrictServerInterface interface { // Get Positioning ATX ID with given maximum publish epoch // (GET /activation/positioning_atx/{publish_epoch}) GetActivationPositioningAtxPublishEpoch(ctx context.Context, request GetActivationPositioningAtxPublishEpochRequestObject) (GetActivationPositioningAtxPublishEpochResponseObject, error) + // Publish a signed hare message + // (POST /hare/publish) + PostHarePublish(ctx context.Context, request PostHarePublishRequestObject) (PostHarePublishResponseObject, error) + // Get a hare message to sign + // (GET /hare/round_template/{layer}/{round}) + GetHareRoundTemplateLayerRound(ctx context.Context, request GetHareRoundTemplateLayerRoundRequestObject) (GetHareRoundTemplateLayerRoundResponseObject, error) // Store PoET proof // (POST /poet) PostPoet(ctx context.Context, request PostPoetRequestObject) (PostPoetResponseObject, error) @@ -571,6 +693,59 @@ func (sh *strictHandler) GetActivationPositioningAtxPublishEpoch(w http.Response } } +// PostHarePublish operation middleware +func (sh *strictHandler) PostHarePublish(w http.ResponseWriter, r *http.Request) { + var request PostHarePublishRequestObject + + request.Body = r.Body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.PostHarePublish(ctx, request.(PostHarePublishRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostHarePublish") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(PostHarePublishResponseObject); ok { + if err := validResponse.VisitPostHarePublishResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetHareRoundTemplateLayerRound operation middleware +func (sh *strictHandler) GetHareRoundTemplateLayerRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, round externalRef0.HareRound) { + var request GetHareRoundTemplateLayerRoundRequestObject + + request.Layer = layer + request.Round = round + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetHareRoundTemplateLayerRound(ctx, request.(GetHareRoundTemplateLayerRoundRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetHareRoundTemplateLayerRound") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetHareRoundTemplateLayerRoundResponseObject); ok { + if err := validResponse.VisitGetHareRoundTemplateLayerRoundResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // PostPoet operation middleware func (sh *strictHandler) PostPoet(w http.ResponseWriter, r *http.Request) { var request PostPoetRequestObject diff --git a/api/node/server/server.go b/api/node/server/server.go index 5a1c9589e8..ce6528a10e 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -17,6 +17,7 @@ import ( "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common" "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/hare3" "github.com/spacemeshos/go-spacemesh/p2p/pubsub" ) @@ -26,10 +27,15 @@ type poetDB interface { ValidateAndStore(ctx context.Context, proofMessage *types.PoetProofMessage) error } +type hare interface { + RoundMessage(layer types.LayerID, round hare3.Round) *hare3.Message +} + type Server struct { atxService activation.AtxService publisher pubsub.Publisher poetDB poetDB + hare hare logger *zap.Logger } @@ -197,3 +203,29 @@ func (s *Server) PostPoet(ctx context.Context, request PostPoetRequestObject) (P } return PostPoet200Response{}, nil } + +type hareResponse struct { + message []byte +} + +func (h *hareResponse) VisitGetHareRoundTemplateLayerRoundResponse(w http.ResponseWriter) error { + if h.message == nil { + w.WriteHeader(204) // no content + return nil + } + w.Header().Add("content-type", "application/octet-stream") + w.WriteHeader(200) + _, err := w.Write(h.message) + return err +} + +func (s *Server) GetHareRoundTemplateLayerRound(ctx context.Context, request GetHareRoundTemplateLayerRoundRequestObject) (GetHareRoundTemplateLayerRoundResponseObject, error) { + msg := s.hare.RoundMessage(types.LayerID(request.Layer), hare3.Round(request.Round)) + if msg == nil { + return &hareResponse{}, nil + } + + return &hareResponse{ + message: codec.MustEncode(msg), + }, nil +} diff --git a/hare3/hare.go b/hare3/hare.go index a4c3dd3326..8c366e94c3 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -183,12 +183,13 @@ func New( ) *Hare { ctx, cancel := context.WithCancel(context.Background()) hr := &Hare{ - ctx: ctx, - cancel: cancel, - results: make(chan hare4.ConsensusOutput, 32), - coins: make(chan hare4.WeakCoinOutput, 32), - signers: map[string]*signing.EdSigner{}, - sessions: map[types.LayerID]*protocol{}, + ctx: ctx, + cancel: cancel, + results: make(chan hare4.ConsensusOutput, 32), + coins: make(chan hare4.WeakCoinOutput, 32), + signers: map[string]*signing.EdSigner{}, + sessions: map[types.LayerID]*protocol{}, + layerResults: make(map[types.LayerID]map[Round]output), config: DefaultConfig(), log: zap.NewNop(), @@ -217,14 +218,15 @@ func New( type Hare struct { // state - ctx context.Context - cancel context.CancelFunc - eg errgroup.Group - results chan hare4.ConsensusOutput - coins chan hare4.WeakCoinOutput - mu sync.Mutex - signers map[string]*signing.EdSigner - sessions map[types.LayerID]*protocol + ctx context.Context + cancel context.CancelFunc + eg errgroup.Group + results chan hare4.ConsensusOutput + coins chan hare4.WeakCoinOutput + mu sync.Mutex + signers map[string]*signing.EdSigner + sessions map[types.LayerID]*protocol + layerResults map[types.LayerID]map[Round]output // options config Config @@ -410,6 +412,17 @@ func (h *Hare) onLayer(layer types.LayerID) { }) } +func (h *Hare) layerResult(layer types.LayerID, iter IterRound, out output) { + h.mu.Lock() + defer h.mu.Unlock() + + _, ok := h.layerResults[layer] + if !ok { + h.layerResults[layer] = make(map[Round]output) + } + h.layerResults[layer][iter.Round] = out +} + func (h *Hare) run(session *session) error { // oracle may load non-negligible amount of data from disk // we do it before preround starts, so that load can have some slack time @@ -439,7 +452,10 @@ func (h *Hare) run(session *session) error { session.proto.OnInitial(h.selectProposals(session)) proposalsLatency.Observe(time.Since(start).Seconds()) } - if err := h.onOutput(session, current, session.proto.Next()); err != nil { + + out := session.proto.Next() + h.layerResult(session.lid, current, out) + if err := h.onOutput(session, current, out); err != nil { return err } result := false @@ -469,6 +485,9 @@ func (h *Hare) run(session *session) error { if out.result != nil { result = true } + + h.layerResult(session.lid, current, out) + if err := h.onOutput(session, current, out); err != nil { return err } diff --git a/hare3/remote.go b/hare3/remote.go index 49c64a7276..cc7498eb58 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -1,34 +1,49 @@ package hare3 import ( - "errors" - "fmt" + "context" "math" "sync" "time" + "github.com/jonboulle/clockwork" + "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/hare3/eligibility" "github.com/spacemeshos/go-spacemesh/log" "github.com/spacemeshos/go-spacemesh/signing" "go.uber.org/zap" "golang.org/x/exp/maps" + "golang.org/x/sync/errgroup" ) +type nodeService interface { + GetHareMessage(ctx context.Context, layer types.LayerID, round Round) ([]byte, error) + PublishHareMessage(ctx context.Context, msg []byte) error +} + type RemoteHare struct { + config Config eligibility *eligibility.Oracle + wallClock clockwork.Clock nodeClock nodeClock mu sync.Mutex beacons map[types.EpochID]types.Beacon signers map[string]*signing.EdSigner oracle *legacyOracle sessions map[types.LayerID]*protocol + eg errgroup.Group + ctx context.Context + svc nodeService log *zap.Logger } +// type remote func NewRemoteHare() *RemoteHare { - return &RemoteHare{} + return &RemoteHare{ + wallClock: clockwork.NewRealClock(), + } } func (h *RemoteHare) Register(sig *signing.EdSigner) { @@ -40,12 +55,12 @@ func (h *RemoteHare) Register(sig *signing.EdSigner) { func (h *RemoteHare) Start() { current := h.nodeClock.CurrentLayer() + 1 - enableLayer = 0 + enableLayer := types.LayerID(0) enabled := max(current, enableLayer /* h.config.EnableLayer*/, types.GetEffectiveGenesis()+1) disabled := types.LayerID(math.MaxUint32) - if h.config.DisableLayer > 0 { - disabled = h.config.DisableLayer - } + //if h.config.DisableLayer > 0 { + //disabled = h.config.DisableLayer + //} h.log.Info("started", // zap.Inline(&h.config), zap.Uint32("enabled", enabled.Uint32()), @@ -57,7 +72,7 @@ func (h *RemoteHare) Start() { case <-h.nodeClock.AwaitLayer(next): h.log.Debug("notified", zap.Uint32("layer", next.Uint32())) h.onLayer(next) - h.cleanMessageCache(next - 1) + // h.cleanMessageCache(next - 1) case <-h.ctx.Done(): return nil } @@ -78,12 +93,12 @@ func (h *RemoteHare) beacon(e types.EpochID) types.Beacon { } func (h *RemoteHare) onLayer(layer types.LayerID) { - beacon, err := h.beacon(layer.GetEpoch()) - if err != nil || beacon == types.EmptyBeacon { + beacon := h.beacon(layer.GetEpoch()) + if beacon == types.EmptyBeacon { h.log.Debug("no beacon", zap.Uint32("epoch", layer.GetEpoch().Uint32()), zap.Uint32("lid", layer.Uint32()), - zap.Error(err), + // zap.Error(err), ) return } @@ -95,13 +110,14 @@ func (h *RemoteHare) onLayer(layer types.LayerID) { beacon: beacon, signers: maps.Values(h.signers), vrfs: make([]*types.HareEligibility, len(h.signers)), - proto: newProtocol(h.config.CommitteeFor(layer)/2 + 1), + // proto: newProtocol(h.config.CommitteeFor(layer)/2 + 1), + proto: newProtocol(123), } h.sessions[layer] = s.proto h.mu.Unlock() sessionStart.Inc() - h.tracer.OnStart(layer) + // h.tracer.OnStart(layer) h.log.Debug("registered layer", zap.Uint32("lid", layer.Uint32())) h.eg.Go(func() error { if err := h.run(s); err != nil { @@ -126,6 +142,7 @@ func (h *RemoteHare) onLayer(layer types.LayerID) { } func (h *RemoteHare) selectProposals(session *session) error { + return nil } func (h *RemoteHare) run(session *session) error { @@ -150,26 +167,30 @@ func (h *RemoteHare) run(session *session) error { return h.ctx.Err() } start := time.Now() - session.proto.OnInitial(h.selectProposals(session)) + // TODO this still has the prerequisite of handling the proposals construction correctly + // session.proto.OnInitial(h.selectProposals(session)) proposalsLatency.Observe(time.Since(start).Seconds()) } - if err := h.onOutput(session, current, session.proto.Next()); err != nil { - return err - } - result := false + + //if err := h.onOutput(session, current, session.proto.Next()); err != nil { + //return err + //} + //result := false for { walltime = walltime.Add(h.config.RoundDuration) current = session.proto.IterRound start = time.Now() + eligible := false for i := range session.signers { if current.IsMessageRound() { session.vrfs[i] = h.oracle.active(session.signers[i], session.beacon, session.lid, current) + eligible = eligible || (session.vrfs[i] != nil) } else { session.vrfs[i] = nil } } - h.tracer.OnActive(session.vrfs) + // h.tracer.OnActive(session.vrfs) activeLatency.Observe(time.Since(start).Seconds()) select { @@ -179,29 +200,67 @@ func (h *RemoteHare) run(session *session) error { zap.Uint8("iter", session.proto.Iter), zap.Stringer("round", session.proto.Round), zap.Bool("active", active), ) - out := session.proto.Next() - if out.result != nil { - result = true - } - if err := h.onOutput(session, current, out); err != nil { - return err - } - // we are logginng stats 1 network delay after new iteration start - // so that we can receive notify messages from previous iteration - if session.proto.Round == softlock && h.config.LogStats { - h.log.Debug("stats", zap.Uint32("lid", session.lid.Uint32()), zap.Inline(session.proto.Stats())) - } - if out.terminated { - if !result { - return errors.New("terminated without result") + + if eligible { + msgBytes, err := h.svc.GetHareMessage(context.Background(), session.lid, session.proto.IterRound.Round) + if err != nil { + h.log.Error("get hare message", zap.Error(err)) + continue } - return nil - } - if current.Iter == h.config.IterationsLimit { - return fmt.Errorf("hare failed to reach consensus in %d iterations", h.config.IterationsLimit) + msg := &Message{} + if err := codec.Decode(msgBytes, msg); err != nil { + h.log.Error("decode remote hare message", zap.Error(err)) + } + h.signPub(session, msg) } + //out := session.proto.Next() + //if out.result != nil { + //result = true + //} + //if err := h.onOutput(session, current, out); err != nil { + //return err + //} + //if out.terminated { + //if !result { + //return errors.New("terminated without result") + //} + //return nil + //} + //if current.Iter == h.config.IterationsLimit { + //return fmt.Errorf("hare failed to reach consensus in %d iterations", h.config.IterationsLimit) + //} case <-h.ctx.Done(): return nil } } } + +func (h *RemoteHare) signPub(session *session, message *Message) { + for i, vrf := range session.vrfs { + if vrf == nil { + continue + } + msg := *message + msg.Layer = session.lid + msg.Eligibility = *vrf + msg.Sender = session.signers[i].NodeID() + msg.Signature = session.signers[i].Sign(signing.HARE, msg.ToMetadata().ToBytes()) + if err := h.svc.PublishHareMessage(h.ctx, msg.ToBytes()); err != nil { + h.log.Error("failed to publish", zap.Inline(&msg), zap.Error(err)) + } + } +} + +func (h *RemoteHare) onRound(p *protocol) { + if p.Round == preround && p.Iter == 0 { + // skips hardlock unlike softlock in the paper. + // this makes no practical difference from correctness. + // but allows to simplify assignment in validValues + p.Round = softlock + } else if p.Round == notify { + p.Round = hardlock + p.Iter++ + } else { + p.Round++ + } +} From e7709aef2b715761b8cc311271c0a9ee077c4387 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:11:51 -0600 Subject: [PATCH 03/21] add publish --- api/node/client/client.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/api/node/client/client.go b/api/node/client/client.go index 3d0a33b9db..2e6aa5e528 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/hex" + "errors" "fmt" "io" "net/http" @@ -144,5 +145,18 @@ func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, r } func (s *NodeService) PublishHareMessage(ctx context.Context, msg []byte) error { - return nil + buf := bytes.NewBuffer(msg) + resp, err := s.client.PostHarePublishWithBody(ctx, "application/octet-stream", buf) + if err != nil { + return fmt.Errorf("publish hare: %w", err) + } + + switch resp.StatusCode { + case 202: + return nil + case 500: + return errors.New("error processing send") + default: + return fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } } From 0865b6eff997d1369a2290642fc518176a7d8fed Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:16:20 -0600 Subject: [PATCH 04/21] add haphazard protocol state transition --- hare3/remote.go | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/hare3/remote.go b/hare3/remote.go index cc7498eb58..bb58f5dc3e 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -168,14 +168,9 @@ func (h *RemoteHare) run(session *session) error { } start := time.Now() // TODO this still has the prerequisite of handling the proposals construction correctly - // session.proto.OnInitial(h.selectProposals(session)) proposalsLatency.Observe(time.Since(start).Seconds()) } - - //if err := h.onOutput(session, current, session.proto.Next()); err != nil { - //return err - //} - //result := false + onRound(session.proto) for { walltime = walltime.Add(h.config.RoundDuration) current = session.proto.IterRound @@ -190,7 +185,6 @@ func (h *RemoteHare) run(session *session) error { session.vrfs[i] = nil } } - // h.tracer.OnActive(session.vrfs) activeLatency.Observe(time.Since(start).Seconds()) select { @@ -205,6 +199,7 @@ func (h *RemoteHare) run(session *session) error { msgBytes, err := h.svc.GetHareMessage(context.Background(), session.lid, session.proto.IterRound.Round) if err != nil { h.log.Error("get hare message", zap.Error(err)) + onRound(session.proto) // advance the protocol state before continuing continue } msg := &Message{} @@ -212,23 +207,8 @@ func (h *RemoteHare) run(session *session) error { h.log.Error("decode remote hare message", zap.Error(err)) } h.signPub(session, msg) + onRound(session.proto) // advance the protocol state before continuing } - //out := session.proto.Next() - //if out.result != nil { - //result = true - //} - //if err := h.onOutput(session, current, out); err != nil { - //return err - //} - //if out.terminated { - //if !result { - //return errors.New("terminated without result") - //} - //return nil - //} - //if current.Iter == h.config.IterationsLimit { - //return fmt.Errorf("hare failed to reach consensus in %d iterations", h.config.IterationsLimit) - //} case <-h.ctx.Done(): return nil } @@ -251,7 +231,7 @@ func (h *RemoteHare) signPub(session *session, message *Message) { } } -func (h *RemoteHare) onRound(p *protocol) { +func onRound(p *protocol) { if p.Round == preround && p.Iter == 0 { // skips hardlock unlike softlock in the paper. // this makes no practical difference from correctness. From bba19d510603599b8c50fea733441e6e05dc283a Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 12:15:17 -0600 Subject: [PATCH 05/21] wip --- api/node/client/client.gen.go | 119 +++++++++++++------------ api/node/client/client.go | 28 +++--- api/node/models/components.yaml | 3 + api/node/models/models.gen.go | 3 + api/node/node_service.yaml | 37 ++++---- api/node/server/mocks.go | 6 +- api/node/server/server.gen.go | 153 ++++++++++++++++++-------------- api/node/server/server.go | 27 +++++- hare3/hare.go | 23 ++++- hare3/remote.go | 48 ++++++---- node/node.go | 134 ++++++++++++++++------------ 11 files changed, 338 insertions(+), 243 deletions(-) diff --git a/api/node/client/client.gen.go b/api/node/client/client.gen.go index 8af76d234c..83a4d203d1 100644 --- a/api/node/client/client.gen.go +++ b/api/node/client/client.gen.go @@ -115,11 +115,11 @@ type ClientInterface interface { // GetActivationPositioningAtxPublishEpoch request GetActivationPositioningAtxPublishEpoch(ctx context.Context, publishEpoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*http.Response, error) - // PostHarePublishWithBody request with any body - PostHarePublishWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetHareRoundTemplateLayerIterRound request + GetHareRoundTemplateLayerIterRound(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetHareRoundTemplateLayerRoundWithBody request with any body - GetHareRoundTemplateLayerRoundWithBody(ctx context.Context, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetHareTotalWeight request + GetHareTotalWeight(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) // PostPoetWithBody request with any body PostPoetWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -164,8 +164,8 @@ func (c *Client) GetActivationPositioningAtxPublishEpoch(ctx context.Context, pu return c.Client.Do(req) } -func (c *Client) PostHarePublishWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPostHarePublishRequestWithBody(c.Server, contentType, body) +func (c *Client) GetHareRoundTemplateLayerIterRound(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHareRoundTemplateLayerIterRoundRequest(c.Server, layer, iter, round) if err != nil { return nil, err } @@ -176,8 +176,8 @@ func (c *Client) PostHarePublishWithBody(ctx context.Context, contentType string return c.Client.Do(req) } -func (c *Client) GetHareRoundTemplateLayerRoundWithBody(ctx context.Context, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetHareRoundTemplateLayerRoundRequestWithBody(c.Server, layer, round, contentType, body) +func (c *Client) GetHareTotalWeight(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHareTotalWeightRequest(c.Server) if err != nil { return nil, err } @@ -314,59 +314,64 @@ func NewGetActivationPositioningAtxPublishEpochRequest(server string, publishEpo return req, nil } -// NewPostHarePublishRequestWithBody generates requests for PostHarePublish with any type of body -func NewPostHarePublishRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +// NewGetHareRoundTemplateLayerIterRoundRequest generates requests for GetHareRoundTemplateLayerIterRound +func NewGetHareRoundTemplateLayerIterRoundRequest(server string, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound) (*http.Request, error) { var err error - serverURL, err := url.Parse(server) + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "layer", runtime.ParamLocationPath, layer) if err != nil { return nil, err } - operationPath := fmt.Sprintf("/hare/publish") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } + var pathParam1 string - queryURL, err := serverURL.Parse(operationPath) + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "iter", runtime.ParamLocationPath, iter) if err != nil { return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + var pathParam2 string + + pathParam2, err = runtime.StyleParamWithLocation("simple", false, "round", runtime.ParamLocationPath, round) if err != nil { return nil, err } - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewGetHareRoundTemplateLayerRoundRequestWithBody generates requests for GetHareRoundTemplateLayerRound with any type of body -func NewGetHareRoundTemplateLayerRoundRequestWithBody(server string, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader) (*http.Request, error) { - var err error + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } - var pathParam0 string + operationPath := fmt.Sprintf("/hare/round_template/%s/%s/%s", pathParam0, pathParam1, pathParam2) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } - pathParam0, err = runtime.StyleParamWithLocation("simple", false, "layer", runtime.ParamLocationPath, layer) + queryURL, err := serverURL.Parse(operationPath) if err != nil { return nil, err } - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithLocation("simple", false, "round", runtime.ParamLocationPath, round) + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err } + return req, nil +} + +// NewGetHareTotalWeightRequest generates requests for GetHareTotalWeight +func NewGetHareTotalWeightRequest(server string) (*http.Request, error) { + var err error + serverURL, err := url.Parse(server) if err != nil { return nil, err } - operationPath := fmt.Sprintf("/hare/round_template/%s/%s", pathParam0, pathParam1) + operationPath := fmt.Sprintf("/hare/total_weight") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -376,13 +381,11 @@ func NewGetHareRoundTemplateLayerRoundRequestWithBody(server string, layer exter return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), body) + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err } - req.Header.Add("Content-Type", contentType) - return req, nil } @@ -503,11 +506,11 @@ type ClientWithResponsesInterface interface { // GetActivationPositioningAtxPublishEpochWithResponse request GetActivationPositioningAtxPublishEpochWithResponse(ctx context.Context, publishEpoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*GetActivationPositioningAtxPublishEpochResponse, error) - // PostHarePublishWithBodyWithResponse request with any body - PostHarePublishWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostHarePublishResponse, error) + // GetHareRoundTemplateLayerIterRoundWithResponse request + GetHareRoundTemplateLayerIterRoundWithResponse(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerIterRoundResponse, error) - // GetHareRoundTemplateLayerRoundWithBodyWithResponse request with any body - GetHareRoundTemplateLayerRoundWithBodyWithResponse(ctx context.Context, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerRoundResponse, error) + // GetHareTotalWeightWithResponse request + GetHareTotalWeightWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHareTotalWeightResponse, error) // PostPoetWithBodyWithResponse request with any body PostPoetWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPoetResponse, error) @@ -584,13 +587,13 @@ func (r GetActivationPositioningAtxPublishEpochResponse) StatusCode() int { return 0 } -type PostHarePublishResponse struct { +type GetHareRoundTemplateLayerIterRoundResponse struct { Body []byte HTTPResponse *http.Response } // Status returns HTTPResponse.Status -func (r PostHarePublishResponse) Status() string { +func (r GetHareRoundTemplateLayerIterRoundResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -598,20 +601,20 @@ func (r PostHarePublishResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r PostHarePublishResponse) StatusCode() int { +func (r GetHareRoundTemplateLayerIterRoundResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } return 0 } -type GetHareRoundTemplateLayerRoundResponse struct { +type GetHareTotalWeightResponse struct { Body []byte HTTPResponse *http.Response } // Status returns HTTPResponse.Status -func (r GetHareRoundTemplateLayerRoundResponse) Status() string { +func (r GetHareTotalWeightResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -619,7 +622,7 @@ func (r GetHareRoundTemplateLayerRoundResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r GetHareRoundTemplateLayerRoundResponse) StatusCode() int { +func (r GetHareTotalWeightResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } @@ -695,22 +698,22 @@ func (c *ClientWithResponses) GetActivationPositioningAtxPublishEpochWithRespons return ParseGetActivationPositioningAtxPublishEpochResponse(rsp) } -// PostHarePublishWithBodyWithResponse request with arbitrary body returning *PostHarePublishResponse -func (c *ClientWithResponses) PostHarePublishWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostHarePublishResponse, error) { - rsp, err := c.PostHarePublishWithBody(ctx, contentType, body, reqEditors...) +// GetHareRoundTemplateLayerIterRoundWithResponse request returning *GetHareRoundTemplateLayerIterRoundResponse +func (c *ClientWithResponses) GetHareRoundTemplateLayerIterRoundWithResponse(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerIterRoundResponse, error) { + rsp, err := c.GetHareRoundTemplateLayerIterRound(ctx, layer, iter, round, reqEditors...) if err != nil { return nil, err } - return ParsePostHarePublishResponse(rsp) + return ParseGetHareRoundTemplateLayerIterRoundResponse(rsp) } -// GetHareRoundTemplateLayerRoundWithBodyWithResponse request with arbitrary body returning *GetHareRoundTemplateLayerRoundResponse -func (c *ClientWithResponses) GetHareRoundTemplateLayerRoundWithBodyWithResponse(ctx context.Context, layer externalRef0.LayerID, round externalRef0.HareRound, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerRoundResponse, error) { - rsp, err := c.GetHareRoundTemplateLayerRoundWithBody(ctx, layer, round, contentType, body, reqEditors...) +// GetHareTotalWeightWithResponse request returning *GetHareTotalWeightResponse +func (c *ClientWithResponses) GetHareTotalWeightWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHareTotalWeightResponse, error) { + rsp, err := c.GetHareTotalWeight(ctx, reqEditors...) if err != nil { return nil, err } - return ParseGetHareRoundTemplateLayerRoundResponse(rsp) + return ParseGetHareTotalWeightResponse(rsp) } // PostPoetWithBodyWithResponse request with arbitrary body returning *PostPoetResponse @@ -811,15 +814,15 @@ func ParseGetActivationPositioningAtxPublishEpochResponse(rsp *http.Response) (* return response, nil } -// ParsePostHarePublishResponse parses an HTTP response from a PostHarePublishWithResponse call -func ParsePostHarePublishResponse(rsp *http.Response) (*PostHarePublishResponse, error) { +// ParseGetHareRoundTemplateLayerIterRoundResponse parses an HTTP response from a GetHareRoundTemplateLayerIterRoundWithResponse call +func ParseGetHareRoundTemplateLayerIterRoundResponse(rsp *http.Response) (*GetHareRoundTemplateLayerIterRoundResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &PostHarePublishResponse{ + response := &GetHareRoundTemplateLayerIterRoundResponse{ Body: bodyBytes, HTTPResponse: rsp, } @@ -827,15 +830,15 @@ func ParsePostHarePublishResponse(rsp *http.Response) (*PostHarePublishResponse, return response, nil } -// ParseGetHareRoundTemplateLayerRoundResponse parses an HTTP response from a GetHareRoundTemplateLayerRoundWithResponse call -func ParseGetHareRoundTemplateLayerRoundResponse(rsp *http.Response) (*GetHareRoundTemplateLayerRoundResponse, error) { +// ParseGetHareTotalWeightResponse parses an HTTP response from a GetHareTotalWeightWithResponse call +func ParseGetHareTotalWeightResponse(rsp *http.Response) (*GetHareTotalWeightResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetHareRoundTemplateLayerRoundResponse{ + response := &GetHareTotalWeightResponse{ Body: bodyBytes, HTTPResponse: rsp, } diff --git a/api/node/client/client.go b/api/node/client/client.go index 2e6aa5e528..1223b6e4db 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -4,10 +4,10 @@ import ( "bytes" "context" "encoding/hex" - "errors" "fmt" "io" "net/http" + "strconv" "time" "github.com/hashicorp/go-retryablehttp" @@ -129,8 +129,8 @@ func (s *NodeService) StorePoetProof(ctx context.Context, proof *types.PoetProof return nil } -func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, round hare3.Round) ([]byte, error) { - resp, err := s.client.GetHareRoundTemplateLayerRoundWithBody(ctx, externalRef0.LayerID(layer), externalRef0.HareRound(round), "application/octet-stream", nil) +func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, round hare3.IterRound) ([]byte, error) { + resp, err := s.client.GetHareRoundTemplateLayerIterRound(ctx, externalRef0.LayerID(layer), externalRef0.HareIter(round.Iter), externalRef0.HareRound(round.Round)) if err != nil { return nil, err } @@ -144,19 +144,17 @@ func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, r return bytes, nil } -func (s *NodeService) PublishHareMessage(ctx context.Context, msg []byte) error { - buf := bytes.NewBuffer(msg) - resp, err := s.client.PostHarePublishWithBody(ctx, "application/octet-stream", buf) +func (s *NodeService) TotalWeight(ctx context.Context) (uint64, error) { + resp, err := s.client.GetHareTotalWeight(ctx) if err != nil { - return fmt.Errorf("publish hare: %w", err) + return 0, err } - - switch resp.StatusCode { - case 202: - return nil - case 500: - return errors.New("error processing send") - default: - return fmt.Errorf("unexpected status code: %d", resp.StatusCode) + if resp.StatusCode != http.StatusOK { + return 0, fmt.Errorf("unexpected status: %s", resp.Status) + } + bytes, err := io.ReadAll(resp.Body) + if err != nil { + return 0, fmt.Errorf("read all: %w", err) } + return strconv.ParseUint(string(bytes), 10, 64) } diff --git a/api/node/models/components.yaml b/api/node/models/components.yaml index e0c9fd586c..35a4cba545 100644 --- a/api/node/models/components.yaml +++ b/api/node/models/components.yaml @@ -45,6 +45,9 @@ components: LayerID: type: integer format: uint64 + HareIter: + type: integer + format: uint8 HareRound: type: integer format: uint8 diff --git a/api/node/models/models.gen.go b/api/node/models/models.gen.go index 0973c97d91..595e682d37 100644 --- a/api/node/models/models.gen.go +++ b/api/node/models/models.gen.go @@ -20,6 +20,9 @@ type ActivationTx struct { // EpochID defines model for EpochID. type EpochID = uint32 +// HareIter defines model for HareIter. +type HareIter = uint8 + // HareRound defines model for HareRound. type HareRound = uint8 diff --git a/api/node/node_service.yaml b/api/node/node_service.yaml index 8ca6c0d687..d3d6f9962f 100644 --- a/api/node/node_service.yaml +++ b/api/node/node_service.yaml @@ -122,7 +122,7 @@ paths: plain/text: schema: type: string - /hare/round_template/{layer}/{round}: + /hare/round_template/{layer}/{iter}/{round}: get: summary: Get a hare message to sign tags: @@ -133,13 +133,16 @@ paths: required: true schema: $ref: "models/components.yaml#/components/schemas/LayerID" + - in: path + name: iter + required: true + schema: + $ref: "models/components.yaml#/components/schemas/HareIter" - in: path name: round required: true schema: $ref: "models/components.yaml#/components/schemas/HareRound" - requestBody: - required: false responses: "200": description: successfully found the message to return to client @@ -150,23 +153,19 @@ paths: format: binary "204": description: did not find a message to retrieve - /hare/publish: - post: - summary: Publish a signed hare message + /hare/total_weight: + get: + summary: Get the current total weight tags: - "hare" - requestBody: - required: true - description: hare message to publish serialize in binary form - content: - application/octet-stream: - schema: - type: string - format: binary responses: - "202": - description: successfully accepted the message and queued it to be published over pubsub - "500": - description: could not process request - + "200": + description: successfully found the message to return to client + content: + application/octet-stream: + schema: + type: string + format: binary + "204": + description: did not find a message to retrieve diff --git a/api/node/server/mocks.go b/api/node/server/mocks.go index fdcbace72f..d5e850f9bc 100644 --- a/api/node/server/mocks.go +++ b/api/node/server/mocks.go @@ -103,7 +103,7 @@ func (m *Mockhare) EXPECT() *MockhareMockRecorder { } // RoundMessage mocks base method. -func (m *Mockhare) RoundMessage(layer types.LayerID, round hare3.Round) *hare3.Message { +func (m *Mockhare) RoundMessage(layer types.LayerID, round hare3.IterRound) *hare3.Message { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RoundMessage", layer, round) ret0, _ := ret[0].(*hare3.Message) @@ -129,13 +129,13 @@ func (c *MockhareRoundMessageCall) Return(arg0 *hare3.Message) *MockhareRoundMes } // Do rewrite *gomock.Call.Do -func (c *MockhareRoundMessageCall) Do(f func(types.LayerID, hare3.Round) *hare3.Message) *MockhareRoundMessageCall { +func (c *MockhareRoundMessageCall) Do(f func(types.LayerID, hare3.IterRound) *hare3.Message) *MockhareRoundMessageCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockhareRoundMessageCall) DoAndReturn(f func(types.LayerID, hare3.Round) *hare3.Message) *MockhareRoundMessageCall { +func (c *MockhareRoundMessageCall) DoAndReturn(f func(types.LayerID, hare3.IterRound) *hare3.Message) *MockhareRoundMessageCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/api/node/server/server.gen.go b/api/node/server/server.gen.go index 74970448e9..c9f46f5133 100644 --- a/api/node/server/server.gen.go +++ b/api/node/server/server.gen.go @@ -45,12 +45,12 @@ type ServerInterface interface { // Get Positioning ATX ID with given maximum publish epoch // (GET /activation/positioning_atx/{publish_epoch}) GetActivationPositioningAtxPublishEpoch(w http.ResponseWriter, r *http.Request, publishEpoch externalRef0.EpochID) - // Publish a signed hare message - // (POST /hare/publish) - PostHarePublish(w http.ResponseWriter, r *http.Request) // Get a hare message to sign - // (GET /hare/round_template/{layer}/{round}) - GetHareRoundTemplateLayerRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, round externalRef0.HareRound) + // (GET /hare/round_template/{layer}/{iter}/{round}) + GetHareRoundTemplateLayerIterRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound) + // Get the current total weight + // (GET /hare/total_weight) + GetHareTotalWeight(w http.ResponseWriter, r *http.Request) // Store PoET proof // (POST /poet) PostPoet(w http.ResponseWriter, r *http.Request) @@ -143,22 +143,8 @@ func (siw *ServerInterfaceWrapper) GetActivationPositioningAtxPublishEpoch(w htt handler.ServeHTTP(w, r) } -// PostHarePublish operation middleware -func (siw *ServerInterfaceWrapper) PostHarePublish(w http.ResponseWriter, r *http.Request) { - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.PostHarePublish(w, r) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - -// GetHareRoundTemplateLayerRound operation middleware -func (siw *ServerInterfaceWrapper) GetHareRoundTemplateLayerRound(w http.ResponseWriter, r *http.Request) { +// GetHareRoundTemplateLayerIterRound operation middleware +func (siw *ServerInterfaceWrapper) GetHareRoundTemplateLayerIterRound(w http.ResponseWriter, r *http.Request) { var err error @@ -171,6 +157,15 @@ func (siw *ServerInterfaceWrapper) GetHareRoundTemplateLayerRound(w http.Respons return } + // ------------- Path parameter "iter" ------------- + var iter externalRef0.HareIter + + err = runtime.BindStyledParameterWithOptions("simple", "iter", r.PathValue("iter"), &iter, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "iter", Err: err}) + return + } + // ------------- Path parameter "round" ------------- var round externalRef0.HareRound @@ -181,7 +176,21 @@ func (siw *ServerInterfaceWrapper) GetHareRoundTemplateLayerRound(w http.Respons } handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.GetHareRoundTemplateLayerRound(w, r, layer, round) + siw.Handler.GetHareRoundTemplateLayerIterRound(w, r, layer, iter, round) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetHareTotalWeight operation middleware +func (siw *ServerInterfaceWrapper) GetHareTotalWeight(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetHareTotalWeight(w, r) })) for _, middleware := range siw.HandlerMiddlewares { @@ -353,8 +362,8 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc("GET "+options.BaseURL+"/activation/atx/{atx_id}", wrapper.GetActivationAtxAtxId) m.HandleFunc("GET "+options.BaseURL+"/activation/last_atx/{node_id}", wrapper.GetActivationLastAtxNodeId) m.HandleFunc("GET "+options.BaseURL+"/activation/positioning_atx/{publish_epoch}", wrapper.GetActivationPositioningAtxPublishEpoch) - m.HandleFunc("POST "+options.BaseURL+"/hare/publish", wrapper.PostHarePublish) - m.HandleFunc("GET "+options.BaseURL+"/hare/round_template/{layer}/{round}", wrapper.GetHareRoundTemplateLayerRound) + m.HandleFunc("GET "+options.BaseURL+"/hare/round_template/{layer}/{iter}/{round}", wrapper.GetHareRoundTemplateLayerIterRound) + m.HandleFunc("GET "+options.BaseURL+"/hare/total_weight", wrapper.GetHareTotalWeight) m.HandleFunc("POST "+options.BaseURL+"/poet", wrapper.PostPoet) m.HandleFunc("POST "+options.BaseURL+"/publish/{protocol}", wrapper.PostPublishProtocol) @@ -449,45 +458,56 @@ func (response GetActivationPositioningAtxPublishEpoch200JSONResponse) VisitGetA return json.NewEncoder(w).Encode(response) } -type PostHarePublishRequestObject struct { - Body io.Reader +type GetHareRoundTemplateLayerIterRoundRequestObject struct { + Layer externalRef0.LayerID `json:"layer"` + Iter externalRef0.HareIter `json:"iter"` + Round externalRef0.HareRound `json:"round"` } -type PostHarePublishResponseObject interface { - VisitPostHarePublishResponse(w http.ResponseWriter) error +type GetHareRoundTemplateLayerIterRoundResponseObject interface { + VisitGetHareRoundTemplateLayerIterRoundResponse(w http.ResponseWriter) error } -type PostHarePublish202Response struct { +type GetHareRoundTemplateLayerIterRound200ApplicationoctetStreamResponse struct { + Body io.Reader + ContentLength int64 } -func (response PostHarePublish202Response) VisitPostHarePublishResponse(w http.ResponseWriter) error { - w.WriteHeader(202) - return nil +func (response GetHareRoundTemplateLayerIterRound200ApplicationoctetStreamResponse) VisitGetHareRoundTemplateLayerIterRoundResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/octet-stream") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err } -type PostHarePublish500Response struct { +type GetHareRoundTemplateLayerIterRound204Response struct { } -func (response PostHarePublish500Response) VisitPostHarePublishResponse(w http.ResponseWriter) error { - w.WriteHeader(500) +func (response GetHareRoundTemplateLayerIterRound204Response) VisitGetHareRoundTemplateLayerIterRoundResponse(w http.ResponseWriter) error { + w.WriteHeader(204) return nil } -type GetHareRoundTemplateLayerRoundRequestObject struct { - Layer externalRef0.LayerID `json:"layer"` - Round externalRef0.HareRound `json:"round"` +type GetHareTotalWeightRequestObject struct { } -type GetHareRoundTemplateLayerRoundResponseObject interface { - VisitGetHareRoundTemplateLayerRoundResponse(w http.ResponseWriter) error +type GetHareTotalWeightResponseObject interface { + VisitGetHareTotalWeightResponse(w http.ResponseWriter) error } -type GetHareRoundTemplateLayerRound200ApplicationoctetStreamResponse struct { +type GetHareTotalWeight200ApplicationoctetStreamResponse struct { Body io.Reader ContentLength int64 } -func (response GetHareRoundTemplateLayerRound200ApplicationoctetStreamResponse) VisitGetHareRoundTemplateLayerRoundResponse(w http.ResponseWriter) error { +func (response GetHareTotalWeight200ApplicationoctetStreamResponse) VisitGetHareTotalWeightResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/octet-stream") if response.ContentLength != 0 { w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) @@ -501,10 +521,10 @@ func (response GetHareRoundTemplateLayerRound200ApplicationoctetStreamResponse) return err } -type GetHareRoundTemplateLayerRound204Response struct { +type GetHareTotalWeight204Response struct { } -func (response GetHareRoundTemplateLayerRound204Response) VisitGetHareRoundTemplateLayerRoundResponse(w http.ResponseWriter) error { +func (response GetHareTotalWeight204Response) VisitGetHareTotalWeightResponse(w http.ResponseWriter) error { w.WriteHeader(204) return nil } @@ -572,12 +592,12 @@ type StrictServerInterface interface { // Get Positioning ATX ID with given maximum publish epoch // (GET /activation/positioning_atx/{publish_epoch}) GetActivationPositioningAtxPublishEpoch(ctx context.Context, request GetActivationPositioningAtxPublishEpochRequestObject) (GetActivationPositioningAtxPublishEpochResponseObject, error) - // Publish a signed hare message - // (POST /hare/publish) - PostHarePublish(ctx context.Context, request PostHarePublishRequestObject) (PostHarePublishResponseObject, error) // Get a hare message to sign - // (GET /hare/round_template/{layer}/{round}) - GetHareRoundTemplateLayerRound(ctx context.Context, request GetHareRoundTemplateLayerRoundRequestObject) (GetHareRoundTemplateLayerRoundResponseObject, error) + // (GET /hare/round_template/{layer}/{iter}/{round}) + GetHareRoundTemplateLayerIterRound(ctx context.Context, request GetHareRoundTemplateLayerIterRoundRequestObject) (GetHareRoundTemplateLayerIterRoundResponseObject, error) + // Get the current total weight + // (GET /hare/total_weight) + GetHareTotalWeight(ctx context.Context, request GetHareTotalWeightRequestObject) (GetHareTotalWeightResponseObject, error) // Store PoET proof // (POST /poet) PostPoet(ctx context.Context, request PostPoetRequestObject) (PostPoetResponseObject, error) @@ -693,25 +713,27 @@ func (sh *strictHandler) GetActivationPositioningAtxPublishEpoch(w http.Response } } -// PostHarePublish operation middleware -func (sh *strictHandler) PostHarePublish(w http.ResponseWriter, r *http.Request) { - var request PostHarePublishRequestObject +// GetHareRoundTemplateLayerIterRound operation middleware +func (sh *strictHandler) GetHareRoundTemplateLayerIterRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound) { + var request GetHareRoundTemplateLayerIterRoundRequestObject - request.Body = r.Body + request.Layer = layer + request.Iter = iter + request.Round = round handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.PostHarePublish(ctx, request.(PostHarePublishRequestObject)) + return sh.ssi.GetHareRoundTemplateLayerIterRound(ctx, request.(GetHareRoundTemplateLayerIterRoundRequestObject)) } for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostHarePublish") + handler = middleware(handler, "GetHareRoundTemplateLayerIterRound") } response, err := handler(r.Context(), w, r, request) if err != nil { sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(PostHarePublishResponseObject); ok { - if err := validResponse.VisitPostHarePublishResponse(w); err != nil { + } else if validResponse, ok := response.(GetHareRoundTemplateLayerIterRoundResponseObject); ok { + if err := validResponse.VisitGetHareRoundTemplateLayerIterRoundResponse(w); err != nil { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { @@ -719,26 +741,23 @@ func (sh *strictHandler) PostHarePublish(w http.ResponseWriter, r *http.Request) } } -// GetHareRoundTemplateLayerRound operation middleware -func (sh *strictHandler) GetHareRoundTemplateLayerRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, round externalRef0.HareRound) { - var request GetHareRoundTemplateLayerRoundRequestObject - - request.Layer = layer - request.Round = round +// GetHareTotalWeight operation middleware +func (sh *strictHandler) GetHareTotalWeight(w http.ResponseWriter, r *http.Request) { + var request GetHareTotalWeightRequestObject handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.GetHareRoundTemplateLayerRound(ctx, request.(GetHareRoundTemplateLayerRoundRequestObject)) + return sh.ssi.GetHareTotalWeight(ctx, request.(GetHareTotalWeightRequestObject)) } for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetHareRoundTemplateLayerRound") + handler = middleware(handler, "GetHareTotalWeight") } response, err := handler(r.Context(), w, r, request) if err != nil { sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(GetHareRoundTemplateLayerRoundResponseObject); ok { - if err := validResponse.VisitGetHareRoundTemplateLayerRoundResponse(w); err != nil { + } else if validResponse, ok := response.(GetHareTotalWeightResponseObject); ok { + if err := validResponse.VisitGetHareTotalWeightResponse(w); err != nil { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { diff --git a/api/node/server/server.go b/api/node/server/server.go index ce6528a10e..2cd06191d0 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -5,6 +5,7 @@ import ( "context" "encoding/hex" "errors" + "fmt" "io" "net/http" @@ -28,7 +29,8 @@ type poetDB interface { } type hare interface { - RoundMessage(layer types.LayerID, round hare3.Round) *hare3.Message + RoundMessage(layer types.LayerID, round hare3.IterRound) *hare3.Message + TotalWeight() uint64 } type Server struct { @@ -45,12 +47,14 @@ func NewServer( atxService activation.AtxService, publisher pubsub.Publisher, poetDB poetDB, + hare hare, logger *zap.Logger, ) *Server { return &Server{ atxService: atxService, publisher: publisher, poetDB: poetDB, + hare: hare, logger: logger, } } @@ -208,7 +212,7 @@ type hareResponse struct { message []byte } -func (h *hareResponse) VisitGetHareRoundTemplateLayerRoundResponse(w http.ResponseWriter) error { +func (h *hareResponse) VisitGetHareRoundTemplateLayerIterRoundResponse(w http.ResponseWriter) error { if h.message == nil { w.WriteHeader(204) // no content return nil @@ -219,8 +223,8 @@ func (h *hareResponse) VisitGetHareRoundTemplateLayerRoundResponse(w http.Respon return err } -func (s *Server) GetHareRoundTemplateLayerRound(ctx context.Context, request GetHareRoundTemplateLayerRoundRequestObject) (GetHareRoundTemplateLayerRoundResponseObject, error) { - msg := s.hare.RoundMessage(types.LayerID(request.Layer), hare3.Round(request.Round)) +func (s *Server) GetHareRoundTemplateLayerIterRound(ctx context.Context, request GetHareRoundTemplateLayerIterRoundRequestObject) (GetHareRoundTemplateLayerIterRoundResponseObject, error) { + msg := s.hare.RoundMessage(types.LayerID(request.Layer), hare3.IterRound{Round: hare3.Round(request.Round), Iter: (request.Iter)}) if msg == nil { return &hareResponse{}, nil } @@ -229,3 +233,18 @@ func (s *Server) GetHareRoundTemplateLayerRound(ctx context.Context, request Get message: codec.MustEncode(msg), }, nil } + +type totalWeightResp struct { + w uint64 +} + +func (t *totalWeightResp) VisitGetHareTotalWeightResponse(w http.ResponseWriter) error { + w.Header().Add("content-type", "application/octet-stream") + w.WriteHeader(200) + _, err := w.Write([]byte(fmt.Sprintf("%d", t.w))) + return err +} + +func (s *Server) GetHareTotalWeight(ctx context.Context, _ GetHareTotalWeightRequestObject) (GetHareTotalWeightResponseObject, error) { + return &totalWeightResp{s.hare.TotalWeight()}, nil +} diff --git a/hare3/hare.go b/hare3/hare.go index 8c366e94c3..d473078230 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -189,7 +189,7 @@ func New( coins: make(chan hare4.WeakCoinOutput, 32), signers: map[string]*signing.EdSigner{}, sessions: map[types.LayerID]*protocol{}, - layerResults: make(map[types.LayerID]map[Round]output), + layerResults: make(map[types.LayerID]map[IterRound]output), config: DefaultConfig(), log: zap.NewNop(), @@ -226,7 +226,7 @@ type Hare struct { mu sync.Mutex signers map[string]*signing.EdSigner sessions map[types.LayerID]*protocol - layerResults map[types.LayerID]map[Round]output + layerResults map[types.LayerID]map[IterRound]output // options config Config @@ -418,9 +418,9 @@ func (h *Hare) layerResult(layer types.LayerID, iter IterRound, out output) { _, ok := h.layerResults[layer] if !ok { - h.layerResults[layer] = make(map[Round]output) + h.layerResults[layer] = make(map[IterRound]output) } - h.layerResults[layer][iter.Round] = out + h.layerResults[layer][iter] = out } func (h *Hare) run(session *session) error { @@ -656,3 +656,18 @@ type session struct { signers []*signing.EdSigner vrfs []*types.HareEligibility } + +func (h *Hare) RoundMessage(layer types.LayerID, round IterRound) *Message { + h.mu.Lock() + defer h.mu.Unlock() + + l, ok := h.layerResults[layer] + if !ok { + return nil + } + r, ok := l[round] + if !ok { + return nil + } + return r.message +} diff --git a/hare3/remote.go b/hare3/remote.go index bb58f5dc3e..f2a98fd89f 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -9,7 +9,6 @@ import ( "github.com/jonboulle/clockwork" "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/hare3/eligibility" "github.com/spacemeshos/go-spacemesh/log" "github.com/spacemeshos/go-spacemesh/signing" "go.uber.org/zap" @@ -18,30 +17,39 @@ import ( ) type nodeService interface { - GetHareMessage(ctx context.Context, layer types.LayerID, round Round) ([]byte, error) - PublishHareMessage(ctx context.Context, msg []byte) error + GetHareMessage(ctx context.Context, layer types.LayerID, round IterRound) ([]byte, error) + Publish(ctx context.Context, proto string, blob []byte) error } type RemoteHare struct { - config Config - eligibility *eligibility.Oracle - wallClock clockwork.Clock - nodeClock nodeClock - mu sync.Mutex - beacons map[types.EpochID]types.Beacon - signers map[string]*signing.EdSigner - oracle *legacyOracle - sessions map[types.LayerID]*protocol - eg errgroup.Group - ctx context.Context - svc nodeService + config Config + wallClock clockwork.Clock + nodeClock nodeClock + mu sync.Mutex + beacons map[types.EpochID]types.Beacon + signers map[string]*signing.EdSigner + oracle *legacyOracle + sessions map[types.LayerID]*protocol + eg errgroup.Group + ctx context.Context + svc nodeService log *zap.Logger } // type remote -func NewRemoteHare() *RemoteHare { +func NewRemoteHare(config Config, nodeClock nodeClock, nodeService nodeService) *RemoteHare { return &RemoteHare{ + config: config, + nodeClock: nodeClock, + beacons: make(map[types.EpochID]types.Beacon), + signers: make(map[string]*signing.EdSigner), + oracle: nil, + sessions: make(map[types.LayerID]*protocol), + eg: errgroup.Group{}, + ctx: context.Background(), + svc: nodeService, + wallClock: clockwork.NewRealClock(), } } @@ -172,6 +180,10 @@ func (h *RemoteHare) run(session *session) error { } onRound(session.proto) for { + if session.proto.IterRound.Iter > h.config.IterationsLimit { + return nil + } + walltime = walltime.Add(h.config.RoundDuration) current = session.proto.IterRound start = time.Now() @@ -196,7 +208,7 @@ func (h *RemoteHare) run(session *session) error { ) if eligible { - msgBytes, err := h.svc.GetHareMessage(context.Background(), session.lid, session.proto.IterRound.Round) + msgBytes, err := h.svc.GetHareMessage(context.Background(), session.lid, session.proto.IterRound) if err != nil { h.log.Error("get hare message", zap.Error(err)) onRound(session.proto) // advance the protocol state before continuing @@ -225,7 +237,7 @@ func (h *RemoteHare) signPub(session *session, message *Message) { msg.Eligibility = *vrf msg.Sender = session.signers[i].NodeID() msg.Signature = session.signers[i].Sign(signing.HARE, msg.ToMetadata().ToBytes()) - if err := h.svc.PublishHareMessage(h.ctx, msg.ToBytes()); err != nil { + if err := h.svc.Publish(h.ctx, h.config.ProtocolName, msg.ToBytes()); err != nil { h.log.Error("failed to publish", zap.Inline(&msg), zap.Error(err)) } } diff --git a/node/node.go b/node/node.go index 309c0b8a12..08aed68a45 100644 --- a/node/node.go +++ b/node/node.go @@ -397,6 +397,7 @@ type App struct { clock *timesync.NodeClock hare3 *hare3.Hare hare4 *hare4.Hare + remoteHare *hare3.RemoteHare hareResultsChan chan hare4.ConsensusOutput hOracle *eligibility.Oracle blockGen *blocks.Generator @@ -885,68 +886,91 @@ func (app *App) initServices(ctx context.Context) error { // should be removed after hare4 transition is complete app.hareResultsChan = make(chan hare4.ConsensusOutput, 32) - if app.Config.HARE3.Enable { - app.hare3 = hare3.New( + if nodeServiceClient != nil { + app.remoteHare = hare3.NewRemoteHare( + app.Config.HARE3, app.clock, - app.host, - app.db, - app.atxsdata, - proposalsStore, - app.edVerifier, - app.hOracle, - newSyncer, - patrol, - hare3.WithLogger(logger), - hare3.WithConfig(app.Config.HARE3), - hare3.WithResultsChan(app.hareResultsChan), + nodeServiceClient, + // app.host, + // app.db, + // app.atxsdata, + // proposalsStore, + // app.edVerifier, + // app.hOracle, + // newSyncer, + // patrol, + // hare3.WithLogger(logger), + // hare3.WithConfig(), + // hare3.WithResultsChan(app.hareResultsChan), ) for _, sig := range app.signers { - app.hare3.Register(sig) + app.remoteHare.Register(sig) } - app.hare3.Start() - app.eg.Go(func() error { - compat.ReportWeakcoin( - ctx, - logger, - app.hare3.Coins(), - tortoiseWeakCoin{db: app.cachedDB, tortoise: trtl}, - ) - return nil - }) - } + app.remoteHare.Start() + } else { - if app.Config.HARE4.Enable { - app.hare4 = hare4.New( - app.clock, - app.host, - app.db, - app.atxsdata, - proposalsStore, - app.edVerifier, - app.hOracle, - newSyncer, - patrol, - app.host, - hare4.WithLogger(logger), - hare4.WithConfig(app.Config.HARE4), - hare4.WithResultsChan(app.hareResultsChan), - ) - for _, sig := range app.signers { - app.hare4.Register(sig) + if app.Config.HARE3.Enable { + app.hare3 = hare3.New( + app.clock, + app.host, + app.db, + app.atxsdata, + proposalsStore, + app.edVerifier, + app.hOracle, + newSyncer, + patrol, + hare3.WithLogger(logger), + hare3.WithConfig(app.Config.HARE3), + hare3.WithResultsChan(app.hareResultsChan), + ) + for _, sig := range app.signers { + app.hare3.Register(sig) + } + app.hare3.Start() + app.eg.Go(func() error { + compat.ReportWeakcoin( + ctx, + logger, + app.hare3.Coins(), + tortoiseWeakCoin{db: app.cachedDB, tortoise: trtl}, + ) + return nil + }) } - app.hare4.Start() - app.eg.Go(func() error { - compat.ReportWeakcoin( - ctx, - logger, - app.hare4.Coins(), - tortoiseWeakCoin{db: app.cachedDB, tortoise: trtl}, + + if app.Config.HARE4.Enable { + app.hare4 = hare4.New( + app.clock, + app.host, + app.db, + app.atxsdata, + proposalsStore, + app.edVerifier, + app.hOracle, + newSyncer, + patrol, + app.host, + hare4.WithLogger(logger), + hare4.WithConfig(app.Config.HARE4), + hare4.WithResultsChan(app.hareResultsChan), ) - return nil - }) - panic("hare4 still not enabled") + for _, sig := range app.signers { + app.hare4.Register(sig) + } + app.hare4.Start() + app.eg.Go(func() error { + compat.ReportWeakcoin( + ctx, + logger, + app.hare4.Coins(), + tortoiseWeakCoin{db: app.cachedDB, tortoise: trtl}, + ) + return nil + }) + panic("hare4 still not enabled") + } } - propHare := &proposalConsumerHare{ hare3: app.hare3, h3DisableLayer: app.Config.HARE3.DisableLayer, @@ -1865,7 +1889,7 @@ func (app *App) startAPIServices(ctx context.Context) error { golden := types.ATXID(app.Config.Genesis.GoldenATX()) logger := app.log.Zap().Named("atx-service") actSvc := activation.NewDBAtxService(app.db, golden, app.atxsdata, app.validator, logger) - server := nodeserver.NewServer(actSvc, app.host, app.poetDb, logger) + server := nodeserver.NewServer(actSvc, app.host, app.poetDb, app.hare3, logger) app.nodeServiceServer = &http.Server{ Handler: server.IntoHandler(http.NewServeMux()), From 599abfd08ecd7e0014f5d00d3fa727fa929d1398 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:10:13 -0600 Subject: [PATCH 06/21] builds --- api/node/client/client.gen.go | 45 ++++++++++++++----------- api/node/client/client.go | 4 +-- api/node/node_service.yaml | 13 ++++++-- api/node/server/mocks.go | 38 +++++++++++++++++++++ api/node/server/server.gen.go | 62 +++++++++++++++++++++-------------- api/node/server/server.go | 8 ++--- hare3/eligibility/oracle.go | 18 +++++++++- hare3/hare.go | 4 +++ hare3/legacy_oracle.go | 1 + 9 files changed, 140 insertions(+), 53 deletions(-) diff --git a/api/node/client/client.gen.go b/api/node/client/client.gen.go index 83a4d203d1..874e054113 100644 --- a/api/node/client/client.gen.go +++ b/api/node/client/client.gen.go @@ -118,8 +118,8 @@ type ClientInterface interface { // GetHareRoundTemplateLayerIterRound request GetHareRoundTemplateLayerIterRound(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*http.Response, error) - // GetHareTotalWeight request - GetHareTotalWeight(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetHareTotalWeightLayer request + GetHareTotalWeightLayer(ctx context.Context, layer uint32, reqEditors ...RequestEditorFn) (*http.Response, error) // PostPoetWithBody request with any body PostPoetWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -176,8 +176,8 @@ func (c *Client) GetHareRoundTemplateLayerIterRound(ctx context.Context, layer e return c.Client.Do(req) } -func (c *Client) GetHareTotalWeight(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetHareTotalWeightRequest(c.Server) +func (c *Client) GetHareTotalWeightLayer(ctx context.Context, layer uint32, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHareTotalWeightLayerRequest(c.Server, layer) if err != nil { return nil, err } @@ -362,16 +362,23 @@ func NewGetHareRoundTemplateLayerIterRoundRequest(server string, layer externalR return req, nil } -// NewGetHareTotalWeightRequest generates requests for GetHareTotalWeight -func NewGetHareTotalWeightRequest(server string) (*http.Request, error) { +// NewGetHareTotalWeightLayerRequest generates requests for GetHareTotalWeightLayer +func NewGetHareTotalWeightLayerRequest(server string, layer uint32) (*http.Request, error) { var err error + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "layer", runtime.ParamLocationPath, layer) + if err != nil { + return nil, err + } + serverURL, err := url.Parse(server) if err != nil { return nil, err } - operationPath := fmt.Sprintf("/hare/total_weight") + operationPath := fmt.Sprintf("/hare/total_weight/%s", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -509,8 +516,8 @@ type ClientWithResponsesInterface interface { // GetHareRoundTemplateLayerIterRoundWithResponse request GetHareRoundTemplateLayerIterRoundWithResponse(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerIterRoundResponse, error) - // GetHareTotalWeightWithResponse request - GetHareTotalWeightWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHareTotalWeightResponse, error) + // GetHareTotalWeightLayerWithResponse request + GetHareTotalWeightLayerWithResponse(ctx context.Context, layer uint32, reqEditors ...RequestEditorFn) (*GetHareTotalWeightLayerResponse, error) // PostPoetWithBodyWithResponse request with any body PostPoetWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPoetResponse, error) @@ -608,13 +615,13 @@ func (r GetHareRoundTemplateLayerIterRoundResponse) StatusCode() int { return 0 } -type GetHareTotalWeightResponse struct { +type GetHareTotalWeightLayerResponse struct { Body []byte HTTPResponse *http.Response } // Status returns HTTPResponse.Status -func (r GetHareTotalWeightResponse) Status() string { +func (r GetHareTotalWeightLayerResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -622,7 +629,7 @@ func (r GetHareTotalWeightResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r GetHareTotalWeightResponse) StatusCode() int { +func (r GetHareTotalWeightLayerResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } @@ -707,13 +714,13 @@ func (c *ClientWithResponses) GetHareRoundTemplateLayerIterRoundWithResponse(ctx return ParseGetHareRoundTemplateLayerIterRoundResponse(rsp) } -// GetHareTotalWeightWithResponse request returning *GetHareTotalWeightResponse -func (c *ClientWithResponses) GetHareTotalWeightWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHareTotalWeightResponse, error) { - rsp, err := c.GetHareTotalWeight(ctx, reqEditors...) +// GetHareTotalWeightLayerWithResponse request returning *GetHareTotalWeightLayerResponse +func (c *ClientWithResponses) GetHareTotalWeightLayerWithResponse(ctx context.Context, layer uint32, reqEditors ...RequestEditorFn) (*GetHareTotalWeightLayerResponse, error) { + rsp, err := c.GetHareTotalWeightLayer(ctx, layer, reqEditors...) if err != nil { return nil, err } - return ParseGetHareTotalWeightResponse(rsp) + return ParseGetHareTotalWeightLayerResponse(rsp) } // PostPoetWithBodyWithResponse request with arbitrary body returning *PostPoetResponse @@ -830,15 +837,15 @@ func ParseGetHareRoundTemplateLayerIterRoundResponse(rsp *http.Response) (*GetHa return response, nil } -// ParseGetHareTotalWeightResponse parses an HTTP response from a GetHareTotalWeightWithResponse call -func ParseGetHareTotalWeightResponse(rsp *http.Response) (*GetHareTotalWeightResponse, error) { +// ParseGetHareTotalWeightLayerResponse parses an HTTP response from a GetHareTotalWeightLayerWithResponse call +func ParseGetHareTotalWeightLayerResponse(rsp *http.Response) (*GetHareTotalWeightLayerResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetHareTotalWeightResponse{ + response := &GetHareTotalWeightLayerResponse{ Body: bodyBytes, HTTPResponse: rsp, } diff --git a/api/node/client/client.go b/api/node/client/client.go index 1223b6e4db..496d7a7dc1 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -144,8 +144,8 @@ func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, r return bytes, nil } -func (s *NodeService) TotalWeight(ctx context.Context) (uint64, error) { - resp, err := s.client.GetHareTotalWeight(ctx) +func (s *NodeService) TotalWeight(ctx context.Context, layer types.LayerID) (uint64, error) { + resp, err := s.client.GetHareTotalWeightLayer(ctx, uint32(layer)) if err != nil { return 0, err } diff --git a/api/node/node_service.yaml b/api/node/node_service.yaml index d3d6f9962f..22be789a22 100644 --- a/api/node/node_service.yaml +++ b/api/node/node_service.yaml @@ -153,14 +153,21 @@ paths: format: binary "204": description: did not find a message to retrieve - /hare/total_weight: + /hare/total_weight/{layer}: get: - summary: Get the current total weight + summary: Get the total weight for layer tags: - "hare" + parameters: + - in: path + name: layer + required: true + schema: + type: integer + format: uint32 responses: "200": - description: successfully found the message to return to client + description: successfully found the total weight to return to client content: application/octet-stream: schema: diff --git a/api/node/server/mocks.go b/api/node/server/mocks.go index d5e850f9bc..e8965f4b3b 100644 --- a/api/node/server/mocks.go +++ b/api/node/server/mocks.go @@ -139,3 +139,41 @@ func (c *MockhareRoundMessageCall) DoAndReturn(f func(types.LayerID, hare3.IterR c.Call = c.Call.DoAndReturn(f) return c } + +// TotalWeight mocks base method. +func (m *Mockhare) TotalWeight() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TotalWeight") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// TotalWeight indicates an expected call of TotalWeight. +func (mr *MockhareMockRecorder) TotalWeight() *MockhareTotalWeightCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalWeight", reflect.TypeOf((*Mockhare)(nil).TotalWeight)) + return &MockhareTotalWeightCall{Call: call} +} + +// MockhareTotalWeightCall wrap *gomock.Call +type MockhareTotalWeightCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockhareTotalWeightCall) Return(arg0 uint64) *MockhareTotalWeightCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockhareTotalWeightCall) Do(f func() uint64) *MockhareTotalWeightCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockhareTotalWeightCall) DoAndReturn(f func() uint64) *MockhareTotalWeightCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/api/node/server/server.gen.go b/api/node/server/server.gen.go index c9f46f5133..951a574aae 100644 --- a/api/node/server/server.gen.go +++ b/api/node/server/server.gen.go @@ -48,9 +48,9 @@ type ServerInterface interface { // Get a hare message to sign // (GET /hare/round_template/{layer}/{iter}/{round}) GetHareRoundTemplateLayerIterRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound) - // Get the current total weight - // (GET /hare/total_weight) - GetHareTotalWeight(w http.ResponseWriter, r *http.Request) + // Get the total weight for layer + // (GET /hare/total_weight/{layer}) + GetHareTotalWeightLayer(w http.ResponseWriter, r *http.Request, layer uint32) // Store PoET proof // (POST /poet) PostPoet(w http.ResponseWriter, r *http.Request) @@ -186,11 +186,22 @@ func (siw *ServerInterfaceWrapper) GetHareRoundTemplateLayerIterRound(w http.Res handler.ServeHTTP(w, r) } -// GetHareTotalWeight operation middleware -func (siw *ServerInterfaceWrapper) GetHareTotalWeight(w http.ResponseWriter, r *http.Request) { +// GetHareTotalWeightLayer operation middleware +func (siw *ServerInterfaceWrapper) GetHareTotalWeightLayer(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "layer" ------------- + var layer uint32 + + err = runtime.BindStyledParameterWithOptions("simple", "layer", r.PathValue("layer"), &layer, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "layer", Err: err}) + return + } handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.GetHareTotalWeight(w, r) + siw.Handler.GetHareTotalWeightLayer(w, r, layer) })) for _, middleware := range siw.HandlerMiddlewares { @@ -363,7 +374,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc("GET "+options.BaseURL+"/activation/last_atx/{node_id}", wrapper.GetActivationLastAtxNodeId) m.HandleFunc("GET "+options.BaseURL+"/activation/positioning_atx/{publish_epoch}", wrapper.GetActivationPositioningAtxPublishEpoch) m.HandleFunc("GET "+options.BaseURL+"/hare/round_template/{layer}/{iter}/{round}", wrapper.GetHareRoundTemplateLayerIterRound) - m.HandleFunc("GET "+options.BaseURL+"/hare/total_weight", wrapper.GetHareTotalWeight) + m.HandleFunc("GET "+options.BaseURL+"/hare/total_weight/{layer}", wrapper.GetHareTotalWeightLayer) m.HandleFunc("POST "+options.BaseURL+"/poet", wrapper.PostPoet) m.HandleFunc("POST "+options.BaseURL+"/publish/{protocol}", wrapper.PostPublishProtocol) @@ -495,19 +506,20 @@ func (response GetHareRoundTemplateLayerIterRound204Response) VisitGetHareRoundT return nil } -type GetHareTotalWeightRequestObject struct { +type GetHareTotalWeightLayerRequestObject struct { + Layer uint32 `json:"layer"` } -type GetHareTotalWeightResponseObject interface { - VisitGetHareTotalWeightResponse(w http.ResponseWriter) error +type GetHareTotalWeightLayerResponseObject interface { + VisitGetHareTotalWeightLayerResponse(w http.ResponseWriter) error } -type GetHareTotalWeight200ApplicationoctetStreamResponse struct { +type GetHareTotalWeightLayer200ApplicationoctetStreamResponse struct { Body io.Reader ContentLength int64 } -func (response GetHareTotalWeight200ApplicationoctetStreamResponse) VisitGetHareTotalWeightResponse(w http.ResponseWriter) error { +func (response GetHareTotalWeightLayer200ApplicationoctetStreamResponse) VisitGetHareTotalWeightLayerResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/octet-stream") if response.ContentLength != 0 { w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) @@ -521,10 +533,10 @@ func (response GetHareTotalWeight200ApplicationoctetStreamResponse) VisitGetHare return err } -type GetHareTotalWeight204Response struct { +type GetHareTotalWeightLayer204Response struct { } -func (response GetHareTotalWeight204Response) VisitGetHareTotalWeightResponse(w http.ResponseWriter) error { +func (response GetHareTotalWeightLayer204Response) VisitGetHareTotalWeightLayerResponse(w http.ResponseWriter) error { w.WriteHeader(204) return nil } @@ -595,9 +607,9 @@ type StrictServerInterface interface { // Get a hare message to sign // (GET /hare/round_template/{layer}/{iter}/{round}) GetHareRoundTemplateLayerIterRound(ctx context.Context, request GetHareRoundTemplateLayerIterRoundRequestObject) (GetHareRoundTemplateLayerIterRoundResponseObject, error) - // Get the current total weight - // (GET /hare/total_weight) - GetHareTotalWeight(ctx context.Context, request GetHareTotalWeightRequestObject) (GetHareTotalWeightResponseObject, error) + // Get the total weight for layer + // (GET /hare/total_weight/{layer}) + GetHareTotalWeightLayer(ctx context.Context, request GetHareTotalWeightLayerRequestObject) (GetHareTotalWeightLayerResponseObject, error) // Store PoET proof // (POST /poet) PostPoet(ctx context.Context, request PostPoetRequestObject) (PostPoetResponseObject, error) @@ -741,23 +753,25 @@ func (sh *strictHandler) GetHareRoundTemplateLayerIterRound(w http.ResponseWrite } } -// GetHareTotalWeight operation middleware -func (sh *strictHandler) GetHareTotalWeight(w http.ResponseWriter, r *http.Request) { - var request GetHareTotalWeightRequestObject +// GetHareTotalWeightLayer operation middleware +func (sh *strictHandler) GetHareTotalWeightLayer(w http.ResponseWriter, r *http.Request, layer uint32) { + var request GetHareTotalWeightLayerRequestObject + + request.Layer = layer handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.GetHareTotalWeight(ctx, request.(GetHareTotalWeightRequestObject)) + return sh.ssi.GetHareTotalWeightLayer(ctx, request.(GetHareTotalWeightLayerRequestObject)) } for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetHareTotalWeight") + handler = middleware(handler, "GetHareTotalWeightLayer") } response, err := handler(r.Context(), w, r, request) if err != nil { sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(GetHareTotalWeightResponseObject); ok { - if err := validResponse.VisitGetHareTotalWeightResponse(w); err != nil { + } else if validResponse, ok := response.(GetHareTotalWeightLayerResponseObject); ok { + if err := validResponse.VisitGetHareTotalWeightLayerResponse(w); err != nil { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { diff --git a/api/node/server/server.go b/api/node/server/server.go index 2cd06191d0..20ba694dd0 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -30,7 +30,7 @@ type poetDB interface { type hare interface { RoundMessage(layer types.LayerID, round hare3.IterRound) *hare3.Message - TotalWeight() uint64 + TotalWeight(ctx context.Context, layer types.LayerID) uint64 } type Server struct { @@ -238,13 +238,13 @@ type totalWeightResp struct { w uint64 } -func (t *totalWeightResp) VisitGetHareTotalWeightResponse(w http.ResponseWriter) error { +func (t *totalWeightResp) VisitGetHareTotalWeightLayerResponse(w http.ResponseWriter) error { w.Header().Add("content-type", "application/octet-stream") w.WriteHeader(200) _, err := w.Write([]byte(fmt.Sprintf("%d", t.w))) return err } -func (s *Server) GetHareTotalWeight(ctx context.Context, _ GetHareTotalWeightRequestObject) (GetHareTotalWeightResponseObject, error) { - return &totalWeightResp{s.hare.TotalWeight()}, nil +func (s *Server) GetHareTotalWeightLayer(ctx context.Context, req GetHareTotalWeightLayerRequestObject) (GetHareTotalWeightLayerResponseObject, error) { + return &totalWeightResp{s.hare.TotalWeight(ctx, types.LayerID(req.Layer))}, nil } diff --git a/hare3/eligibility/oracle.go b/hare3/eligibility/oracle.go index 1880a0df73..dba653b16a 100644 --- a/hare3/eligibility/oracle.go +++ b/hare3/eligibility/oracle.go @@ -95,6 +95,7 @@ type Oracle struct { beacons system.BeaconGetter atxsdata *atxsdata.Data minerWeightFn func(ctx context.Context, layer types.LayerID, id types.NodeID) (uint64, error) + totalWeightFn func(ctx context.Context, layer types.LayerID) (uint64, error) db sql.Executor vrfVerifier vrfVerifier layersPerEpoch uint32 @@ -110,6 +111,12 @@ func WithMinerWeightFunc(f func(ctx context.Context, layer types.LayerID, id typ } } +func WithTotalWeightFunc(f func(ctx context.Context, layer types.LayerID) (uint64, error)) Opt { + return func(o *Oracle) { + o.totalWeightFn = f + } +} + func WithConfig(config Config) Opt { return func(o *Oracle) { o.cfg = config @@ -147,6 +154,7 @@ func New( log: zap.NewNop(), } oracle.minerWeightFn = oracle.minerWeight + oracle.totalWeightFn = oracle.totalWeight for _, opt := range opts { opt(oracle) } @@ -261,7 +269,7 @@ func (o *Oracle) prepareEligibilityCheck( } // get active set size - totalWeight, err := o.totalWeight(ctx, layer) + totalWeight, err := o.totalWeightFn(ctx, layer) if err != nil { logger.Error("failed to get total weight", zap.Error(err)) return 0, fixed.Fixed{}, fixed.Fixed{}, true, err @@ -575,3 +583,11 @@ func (o *Oracle) UpdateActiveSet(epoch types.EpochID, activeSet []types.ATXID) { } o.fallback[epoch] = activeSet } + +func (o *Oracle) TotalWeight(ctx context.Context, layer types.LayerID) uint64 { + totalWeight, err := o.totalWeightFn(ctx, layer) + if err != nil { + panic(err) + } + return totalWeight +} diff --git a/hare3/hare.go b/hare3/hare.go index d473078230..f8b2b1d3ce 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -671,3 +671,7 @@ func (h *Hare) RoundMessage(layer types.LayerID, round IterRound) *Message { } return r.message } + +func (h *Hare) TotalWeight(ctx context.Context, layer types.LayerID) uint64 { + return h.oracle.oracle.TotalWeight(ctx, layer) +} diff --git a/hare3/legacy_oracle.go b/hare3/legacy_oracle.go index 07be7f77a4..5383360960 100644 --- a/hare3/legacy_oracle.go +++ b/hare3/legacy_oracle.go @@ -14,6 +14,7 @@ import ( type oracle interface { Validate(context.Context, types.LayerID, uint32, int, types.NodeID, types.VrfSignature, uint16) (bool, error) CalcEligibility(context.Context, types.LayerID, uint32, int, types.NodeID, types.VrfSignature) (uint16, error) + TotalWeight(ctx context.Context, layer types.LayerID) uint64 } type legacyOracle struct { From f5c2990e259c2f1319be064957a2a28c9b1cecdb Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:32:49 -0600 Subject: [PATCH 07/21] more --- api/node/client/client.gen.go | 105 ++++++++++++++++++++++++++++++++++ api/node/client/client.go | 15 +++++ api/node/node_service.yaml | 27 +++++++++ api/node/server/mocks.go | 12 ++-- api/node/server/server.gen.go | 104 +++++++++++++++++++++++++++++++++ api/node/server/server.go | 21 +++++++ hare3/eligibility/oracle.go | 8 +++ hare3/hare.go | 4 ++ hare3/legacy_oracle.go | 1 + node/node.go | 6 +- 10 files changed, 296 insertions(+), 7 deletions(-) diff --git a/api/node/client/client.gen.go b/api/node/client/client.gen.go index 874e054113..830c8ace83 100644 --- a/api/node/client/client.gen.go +++ b/api/node/client/client.gen.go @@ -121,6 +121,9 @@ type ClientInterface interface { // GetHareTotalWeightLayer request GetHareTotalWeightLayer(ctx context.Context, layer uint32, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetHareWeightNodeIdLayer request + GetHareWeightNodeIdLayer(ctx context.Context, nodeId externalRef0.NodeID, layer uint32, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostPoetWithBody request with any body PostPoetWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -188,6 +191,18 @@ func (c *Client) GetHareTotalWeightLayer(ctx context.Context, layer uint32, reqE return c.Client.Do(req) } +func (c *Client) GetHareWeightNodeIdLayer(ctx context.Context, nodeId externalRef0.NodeID, layer uint32, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHareWeightNodeIdLayerRequest(c.Server, nodeId, layer) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostPoetWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostPoetRequestWithBody(c.Server, contentType, body) if err != nil { @@ -396,6 +411,47 @@ func NewGetHareTotalWeightLayerRequest(server string, layer uint32) (*http.Reque return req, nil } +// NewGetHareWeightNodeIdLayerRequest generates requests for GetHareWeightNodeIdLayer +func NewGetHareWeightNodeIdLayerRequest(server string, nodeId externalRef0.NodeID, layer uint32) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "node_id", runtime.ParamLocationPath, nodeId) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "layer", runtime.ParamLocationPath, layer) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/hare/weight/%s/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewPostPoetRequestWithBody generates requests for PostPoet with any type of body func NewPostPoetRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error @@ -519,6 +575,9 @@ type ClientWithResponsesInterface interface { // GetHareTotalWeightLayerWithResponse request GetHareTotalWeightLayerWithResponse(ctx context.Context, layer uint32, reqEditors ...RequestEditorFn) (*GetHareTotalWeightLayerResponse, error) + // GetHareWeightNodeIdLayerWithResponse request + GetHareWeightNodeIdLayerWithResponse(ctx context.Context, nodeId externalRef0.NodeID, layer uint32, reqEditors ...RequestEditorFn) (*GetHareWeightNodeIdLayerResponse, error) + // PostPoetWithBodyWithResponse request with any body PostPoetWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPoetResponse, error) @@ -636,6 +695,27 @@ func (r GetHareTotalWeightLayerResponse) StatusCode() int { return 0 } +type GetHareWeightNodeIdLayerResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r GetHareWeightNodeIdLayerResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetHareWeightNodeIdLayerResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type PostPoetResponse struct { Body []byte HTTPResponse *http.Response @@ -723,6 +803,15 @@ func (c *ClientWithResponses) GetHareTotalWeightLayerWithResponse(ctx context.Co return ParseGetHareTotalWeightLayerResponse(rsp) } +// GetHareWeightNodeIdLayerWithResponse request returning *GetHareWeightNodeIdLayerResponse +func (c *ClientWithResponses) GetHareWeightNodeIdLayerWithResponse(ctx context.Context, nodeId externalRef0.NodeID, layer uint32, reqEditors ...RequestEditorFn) (*GetHareWeightNodeIdLayerResponse, error) { + rsp, err := c.GetHareWeightNodeIdLayer(ctx, nodeId, layer, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetHareWeightNodeIdLayerResponse(rsp) +} + // PostPoetWithBodyWithResponse request with arbitrary body returning *PostPoetResponse func (c *ClientWithResponses) PostPoetWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPoetResponse, error) { rsp, err := c.PostPoetWithBody(ctx, contentType, body, reqEditors...) @@ -853,6 +942,22 @@ func ParseGetHareTotalWeightLayerResponse(rsp *http.Response) (*GetHareTotalWeig return response, nil } +// ParseGetHareWeightNodeIdLayerResponse parses an HTTP response from a GetHareWeightNodeIdLayerWithResponse call +func ParseGetHareWeightNodeIdLayerResponse(rsp *http.Response) (*GetHareWeightNodeIdLayerResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetHareWeightNodeIdLayerResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + // ParsePostPoetResponse parses an HTTP response from a PostPoetWithResponse call func ParsePostPoetResponse(rsp *http.Response) (*PostPoetResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/api/node/client/client.go b/api/node/client/client.go index 496d7a7dc1..0139996bf1 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -158,3 +158,18 @@ func (s *NodeService) TotalWeight(ctx context.Context, layer types.LayerID) (uin } return strconv.ParseUint(string(bytes), 10, 64) } + +func (s *NodeService) MinerWeight(ctx context.Context, layer types.LayerID, node types.NodeID) (uint64, error) { + resp, err := s.client.GetHareWeightNodeIdLayer(ctx, node.String(), uint32(layer)) + if err != nil { + return 0, err + } + if resp.StatusCode != http.StatusOK { + return 0, fmt.Errorf("unexpected status: %s", resp.Status) + } + bytes, err := io.ReadAll(resp.Body) + if err != nil { + return 0, fmt.Errorf("read all: %w", err) + } + return strconv.ParseUint(string(bytes), 10, 64) +} diff --git a/api/node/node_service.yaml b/api/node/node_service.yaml index 22be789a22..37dd5d56fb 100644 --- a/api/node/node_service.yaml +++ b/api/node/node_service.yaml @@ -175,4 +175,31 @@ paths: format: binary "204": description: did not find a message to retrieve + /hare/weight/{node_id}/{layer}: + get: + summary: Get the miner weight in layer + tags: + - "hare" + parameters: + - in: path + name: node_id + required: true + schema: + $ref: "models/components.yaml#/components/schemas/NodeID" + - in: path + name: layer + required: true + schema: + type: integer + format: uint32 + responses: + "200": + description: successfully found the total weight to return to client + content: + application/octet-stream: + schema: + type: string + format: binary + "204": + description: did not find a message to retrieve diff --git a/api/node/server/mocks.go b/api/node/server/mocks.go index e8965f4b3b..db911dff3e 100644 --- a/api/node/server/mocks.go +++ b/api/node/server/mocks.go @@ -141,17 +141,17 @@ func (c *MockhareRoundMessageCall) DoAndReturn(f func(types.LayerID, hare3.IterR } // TotalWeight mocks base method. -func (m *Mockhare) TotalWeight() uint64 { +func (m *Mockhare) TotalWeight(ctx context.Context, layer types.LayerID) uint64 { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "TotalWeight") + ret := m.ctrl.Call(m, "TotalWeight", ctx, layer) ret0, _ := ret[0].(uint64) return ret0 } // TotalWeight indicates an expected call of TotalWeight. -func (mr *MockhareMockRecorder) TotalWeight() *MockhareTotalWeightCall { +func (mr *MockhareMockRecorder) TotalWeight(ctx, layer any) *MockhareTotalWeightCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalWeight", reflect.TypeOf((*Mockhare)(nil).TotalWeight)) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalWeight", reflect.TypeOf((*Mockhare)(nil).TotalWeight), ctx, layer) return &MockhareTotalWeightCall{Call: call} } @@ -167,13 +167,13 @@ func (c *MockhareTotalWeightCall) Return(arg0 uint64) *MockhareTotalWeightCall { } // Do rewrite *gomock.Call.Do -func (c *MockhareTotalWeightCall) Do(f func() uint64) *MockhareTotalWeightCall { +func (c *MockhareTotalWeightCall) Do(f func(context.Context, types.LayerID) uint64) *MockhareTotalWeightCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockhareTotalWeightCall) DoAndReturn(f func() uint64) *MockhareTotalWeightCall { +func (c *MockhareTotalWeightCall) DoAndReturn(f func(context.Context, types.LayerID) uint64) *MockhareTotalWeightCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/api/node/server/server.gen.go b/api/node/server/server.gen.go index 951a574aae..0605c4325f 100644 --- a/api/node/server/server.gen.go +++ b/api/node/server/server.gen.go @@ -51,6 +51,9 @@ type ServerInterface interface { // Get the total weight for layer // (GET /hare/total_weight/{layer}) GetHareTotalWeightLayer(w http.ResponseWriter, r *http.Request, layer uint32) + // Get the miner weight in layer + // (GET /hare/weight/{node_id}/{layer}) + GetHareWeightNodeIdLayer(w http.ResponseWriter, r *http.Request, nodeId externalRef0.NodeID, layer uint32) // Store PoET proof // (POST /poet) PostPoet(w http.ResponseWriter, r *http.Request) @@ -211,6 +214,40 @@ func (siw *ServerInterfaceWrapper) GetHareTotalWeightLayer(w http.ResponseWriter handler.ServeHTTP(w, r) } +// GetHareWeightNodeIdLayer operation middleware +func (siw *ServerInterfaceWrapper) GetHareWeightNodeIdLayer(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "node_id" ------------- + var nodeId externalRef0.NodeID + + err = runtime.BindStyledParameterWithOptions("simple", "node_id", r.PathValue("node_id"), &nodeId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "node_id", Err: err}) + return + } + + // ------------- Path parameter "layer" ------------- + var layer uint32 + + err = runtime.BindStyledParameterWithOptions("simple", "layer", r.PathValue("layer"), &layer, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "layer", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetHareWeightNodeIdLayer(w, r, nodeId, layer) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // PostPoet operation middleware func (siw *ServerInterfaceWrapper) PostPoet(w http.ResponseWriter, r *http.Request) { @@ -375,6 +412,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc("GET "+options.BaseURL+"/activation/positioning_atx/{publish_epoch}", wrapper.GetActivationPositioningAtxPublishEpoch) m.HandleFunc("GET "+options.BaseURL+"/hare/round_template/{layer}/{iter}/{round}", wrapper.GetHareRoundTemplateLayerIterRound) m.HandleFunc("GET "+options.BaseURL+"/hare/total_weight/{layer}", wrapper.GetHareTotalWeightLayer) + m.HandleFunc("GET "+options.BaseURL+"/hare/weight/{node_id}/{layer}", wrapper.GetHareWeightNodeIdLayer) m.HandleFunc("POST "+options.BaseURL+"/poet", wrapper.PostPoet) m.HandleFunc("POST "+options.BaseURL+"/publish/{protocol}", wrapper.PostPublishProtocol) @@ -541,6 +579,42 @@ func (response GetHareTotalWeightLayer204Response) VisitGetHareTotalWeightLayerR return nil } +type GetHareWeightNodeIdLayerRequestObject struct { + NodeId externalRef0.NodeID `json:"node_id"` + Layer uint32 `json:"layer"` +} + +type GetHareWeightNodeIdLayerResponseObject interface { + VisitGetHareWeightNodeIdLayerResponse(w http.ResponseWriter) error +} + +type GetHareWeightNodeIdLayer200ApplicationoctetStreamResponse struct { + Body io.Reader + ContentLength int64 +} + +func (response GetHareWeightNodeIdLayer200ApplicationoctetStreamResponse) VisitGetHareWeightNodeIdLayerResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/octet-stream") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type GetHareWeightNodeIdLayer204Response struct { +} + +func (response GetHareWeightNodeIdLayer204Response) VisitGetHareWeightNodeIdLayerResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + type PostPoetRequestObject struct { Body io.Reader } @@ -610,6 +684,9 @@ type StrictServerInterface interface { // Get the total weight for layer // (GET /hare/total_weight/{layer}) GetHareTotalWeightLayer(ctx context.Context, request GetHareTotalWeightLayerRequestObject) (GetHareTotalWeightLayerResponseObject, error) + // Get the miner weight in layer + // (GET /hare/weight/{node_id}/{layer}) + GetHareWeightNodeIdLayer(ctx context.Context, request GetHareWeightNodeIdLayerRequestObject) (GetHareWeightNodeIdLayerResponseObject, error) // Store PoET proof // (POST /poet) PostPoet(ctx context.Context, request PostPoetRequestObject) (PostPoetResponseObject, error) @@ -779,6 +856,33 @@ func (sh *strictHandler) GetHareTotalWeightLayer(w http.ResponseWriter, r *http. } } +// GetHareWeightNodeIdLayer operation middleware +func (sh *strictHandler) GetHareWeightNodeIdLayer(w http.ResponseWriter, r *http.Request, nodeId externalRef0.NodeID, layer uint32) { + var request GetHareWeightNodeIdLayerRequestObject + + request.NodeId = nodeId + request.Layer = layer + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetHareWeightNodeIdLayer(ctx, request.(GetHareWeightNodeIdLayerRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetHareWeightNodeIdLayer") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetHareWeightNodeIdLayerResponseObject); ok { + if err := validResponse.VisitGetHareWeightNodeIdLayerResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // PostPoet operation middleware func (sh *strictHandler) PostPoet(w http.ResponseWriter, r *http.Request) { var request PostPoetRequestObject diff --git a/api/node/server/server.go b/api/node/server/server.go index 20ba694dd0..c03dd9d6cb 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -31,6 +31,7 @@ type poetDB interface { type hare interface { RoundMessage(layer types.LayerID, round hare3.IterRound) *hare3.Message TotalWeight(ctx context.Context, layer types.LayerID) uint64 + MinerWeight(ctx context.Context, node types.NodeID, layer types.LayerID) uint64 } type Server struct { @@ -248,3 +249,23 @@ func (t *totalWeightResp) VisitGetHareTotalWeightLayerResponse(w http.ResponseWr func (s *Server) GetHareTotalWeightLayer(ctx context.Context, req GetHareTotalWeightLayerRequestObject) (GetHareTotalWeightLayerResponseObject, error) { return &totalWeightResp{s.hare.TotalWeight(ctx, types.LayerID(req.Layer))}, nil } + +type nodeWeightResp struct { + val uint64 +} + +func (n *nodeWeightResp) VisitGetHareWeightNodeIdLayerResponse(w http.ResponseWriter) error { + w.Header().Add("content-type", "application/octet-stream") + w.WriteHeader(200) + _, err := w.Write([]byte(fmt.Sprintf("%d", n.val))) + return err +} + +func (s *Server) GetHareWeightNodeIdLayer(ctx context.Context, request GetHareWeightNodeIdLayerRequestObject) (GetHareWeightNodeIdLayerResponseObject, error) { + id := &types.NodeID{} + err := id.UnmarshalText([]byte(request.NodeId)) + if err != nil { + panic(err) + } + return &nodeWeightResp{val: s.hare.MinerWeight(ctx, *id, types.LayerID(request.Layer))}, nil +} diff --git a/hare3/eligibility/oracle.go b/hare3/eligibility/oracle.go index dba653b16a..64aa5a97a5 100644 --- a/hare3/eligibility/oracle.go +++ b/hare3/eligibility/oracle.go @@ -591,3 +591,11 @@ func (o *Oracle) TotalWeight(ctx context.Context, layer types.LayerID) uint64 { } return totalWeight } + +func (o *Oracle) MinerWeight(ctx context.Context, node types.NodeID, layer types.LayerID) uint64 { + minerWeight, err := o.minerWeightFn(ctx, layer, node) + if err != nil { + panic(err) + } + return minerWeight +} diff --git a/hare3/hare.go b/hare3/hare.go index f8b2b1d3ce..f3b2e7b542 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -675,3 +675,7 @@ func (h *Hare) RoundMessage(layer types.LayerID, round IterRound) *Message { func (h *Hare) TotalWeight(ctx context.Context, layer types.LayerID) uint64 { return h.oracle.oracle.TotalWeight(ctx, layer) } + +func (h *Hare) MinerWeight(ctx context.Context, miner types.NodeID, layer types.LayerID) uint64 { + return h.oracle.oracle.MinerWeight(ctx, miner, layer) +} diff --git a/hare3/legacy_oracle.go b/hare3/legacy_oracle.go index 5383360960..7be15a3080 100644 --- a/hare3/legacy_oracle.go +++ b/hare3/legacy_oracle.go @@ -15,6 +15,7 @@ type oracle interface { Validate(context.Context, types.LayerID, uint32, int, types.NodeID, types.VrfSignature, uint16) (bool, error) CalcEligibility(context.Context, types.LayerID, uint32, int, types.NodeID, types.VrfSignature) (uint16, error) TotalWeight(ctx context.Context, layer types.LayerID) uint64 + MinerWeight(ctx context.Context, node types.NodeID, layer types.LayerID) uint64 } type legacyOracle struct { diff --git a/node/node.go b/node/node.go index 08aed68a45..175555a1e1 100644 --- a/node/node.go +++ b/node/node.go @@ -783,7 +783,11 @@ func (app *App) initServices(ctx context.Context) error { app.host.ID(), app.addLogger(TxHandlerLogger, lg).Zap(), ) - + var extraOpts []eligibility.Opt + if nodeServiceClient != nil { + extraOpts = append(extraOpts, eligibility.WithTotalWeightFunc(nodeServiceClient.TotalWeight)) + extraOpts = append(extraOpts, eligibility.WithMinerWeightFunc(nodeServiceClient.MinerWeight)) + } app.hOracle = eligibility.New( beaconProtocol, app.db, From 3c4e32802db46b72214b81e7415fe86ef15b9953 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:06:47 -0600 Subject: [PATCH 08/21] logger --- hare3/remote.go | 2 +- node/node.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hare3/remote.go b/hare3/remote.go index f2a98fd89f..23de6687c4 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -38,7 +38,7 @@ type RemoteHare struct { } // type remote -func NewRemoteHare(config Config, nodeClock nodeClock, nodeService nodeService) *RemoteHare { +func NewRemoteHare(config Config, nodeClock nodeClock, nodeService nodeService, log *zap.Logger) *RemoteHare { return &RemoteHare{ config: config, nodeClock: nodeClock, diff --git a/node/node.go b/node/node.go index 175555a1e1..ec41d177c8 100644 --- a/node/node.go +++ b/node/node.go @@ -895,6 +895,7 @@ func (app *App) initServices(ctx context.Context) error { app.Config.HARE3, app.clock, nodeServiceClient, + logger, // app.host, // app.db, // app.atxsdata, From c7e2336ea2ae644ac88cddd3be803741fb05fcf0 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:27:26 -0600 Subject: [PATCH 09/21] logoggogogogog --- hare3/remote.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hare3/remote.go b/hare3/remote.go index 23de6687c4..61baffeda8 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -49,7 +49,7 @@ func NewRemoteHare(config Config, nodeClock nodeClock, nodeService nodeService, eg: errgroup.Group{}, ctx: context.Background(), svc: nodeService, - + log: log, wallClock: clockwork.NewRealClock(), } } From 09894be80bac0c7c00d3889416a80737ad485a49 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:43:46 -0600 Subject: [PATCH 10/21] add logging --- hare3/remote.go | 9 +++++---- timesync/clock.go | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/hare3/remote.go b/hare3/remote.go index 61baffeda8..66871dc426 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -70,17 +70,18 @@ func (h *RemoteHare) Start() { //disabled = h.config.DisableLayer //} h.log.Info("started", - // zap.Inline(&h.config), + zap.Inline(&h.config), zap.Uint32("enabled", enabled.Uint32()), zap.Uint32("disabled", disabled.Uint32()), ) h.eg.Go(func() error { + h.log.Info("remote hare processing starting") for next := enabled; next < disabled; next++ { + h.log.Info("remote hare processing layer", zap.Int("next", int(next))) select { case <-h.nodeClock.AwaitLayer(next): h.log.Debug("notified", zap.Uint32("layer", next.Uint32())) h.onLayer(next) - // h.cleanMessageCache(next - 1) case <-h.ctx.Done(): return nil } @@ -101,6 +102,7 @@ func (h *RemoteHare) beacon(e types.EpochID) types.Beacon { } func (h *RemoteHare) onLayer(layer types.LayerID) { + h.log.Debug("remote hare: on layer", zap.Int("layer", int(layer))) beacon := h.beacon(layer.GetEpoch()) if beacon == types.EmptyBeacon { h.log.Debug("no beacon", @@ -118,8 +120,7 @@ func (h *RemoteHare) onLayer(layer types.LayerID) { beacon: beacon, signers: maps.Values(h.signers), vrfs: make([]*types.HareEligibility, len(h.signers)), - // proto: newProtocol(h.config.CommitteeFor(layer)/2 + 1), - proto: newProtocol(123), + proto: newProtocol(h.config.CommitteeFor(layer)/2 + 1), } h.sessions[layer] = s.proto h.mu.Unlock() diff --git a/timesync/clock.go b/timesync/clock.go index 0cf275bbe6..47b400046c 100644 --- a/timesync/clock.go +++ b/timesync/clock.go @@ -1,6 +1,7 @@ package timesync import ( + "fmt" "sync" "time" @@ -184,7 +185,7 @@ func (t *NodeClock) AwaitLayer(layerID types.LayerID) <-chan struct{} { ch = make(chan struct{}) t.layerChannels[layerID] = ch } - + fmt.Println("node clock waiting for layer", layerID) if t.minLayer.After(layerID) { t.minLayer = layerID } From d076cd75818a0a685465ed8f4dadc39f5cbcd8d8 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:46:45 -0600 Subject: [PATCH 11/21] oracle --- hare3/remote.go | 9 +++++++-- node/node.go | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/hare3/remote.go b/hare3/remote.go index 66871dc426..2a21050624 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -38,13 +38,18 @@ type RemoteHare struct { } // type remote -func NewRemoteHare(config Config, nodeClock nodeClock, nodeService nodeService, log *zap.Logger) *RemoteHare { +func NewRemoteHare(config Config, nodeClock nodeClock, nodeService nodeService, oracle oracle, log *zap.Logger) *RemoteHare { return &RemoteHare{ config: config, nodeClock: nodeClock, beacons: make(map[types.EpochID]types.Beacon), signers: make(map[string]*signing.EdSigner), - oracle: nil, + oracle: &legacyOracle{ + log: zap.NewNop(), + oracle: oracle, + config: DefaultConfig(), + }, + sessions: make(map[types.LayerID]*protocol), eg: errgroup.Group{}, ctx: context.Background(), diff --git a/node/node.go b/node/node.go index ec41d177c8..10a130e78c 100644 --- a/node/node.go +++ b/node/node.go @@ -895,13 +895,13 @@ func (app *App) initServices(ctx context.Context) error { app.Config.HARE3, app.clock, nodeServiceClient, + app.hOracle, logger, // app.host, // app.db, // app.atxsdata, // proposalsStore, // app.edVerifier, - // app.hOracle, // newSyncer, // patrol, // hare3.WithLogger(logger), From c976ed4baf76c59ea5d18f1ce7205de2a7fa9b86 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:58:40 -0600 Subject: [PATCH 12/21] logging --- hare3/remote.go | 4 +--- timesync/clock.go | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/hare3/remote.go b/hare3/remote.go index 2a21050624..8c3b1de100 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -71,9 +71,6 @@ func (h *RemoteHare) Start() { enableLayer := types.LayerID(0) enabled := max(current, enableLayer /* h.config.EnableLayer*/, types.GetEffectiveGenesis()+1) disabled := types.LayerID(math.MaxUint32) - //if h.config.DisableLayer > 0 { - //disabled = h.config.DisableLayer - //} h.log.Info("started", zap.Inline(&h.config), zap.Uint32("enabled", enabled.Uint32()), @@ -88,6 +85,7 @@ func (h *RemoteHare) Start() { h.log.Debug("notified", zap.Uint32("layer", next.Uint32())) h.onLayer(next) case <-h.ctx.Done(): + h.log.Info("remote hare processing layer - context done") return nil } } diff --git a/timesync/clock.go b/timesync/clock.go index 47b400046c..f365543ff2 100644 --- a/timesync/clock.go +++ b/timesync/clock.go @@ -1,7 +1,6 @@ package timesync import ( - "fmt" "sync" "time" @@ -185,7 +184,6 @@ func (t *NodeClock) AwaitLayer(layerID types.LayerID) <-chan struct{} { ch = make(chan struct{}) t.layerChannels[layerID] = ch } - fmt.Println("node clock waiting for layer", layerID) if t.minLayer.After(layerID) { t.minLayer = layerID } From 3431ac10a6876f7bd5d13cfe9412642f1766096d Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:01:47 -0600 Subject: [PATCH 13/21] loglvelv --- activation_service_poc/config.standalone.client.json | 3 ++- activation_service_poc/config.standalone.node-service.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/activation_service_poc/config.standalone.client.json b/activation_service_poc/config.standalone.client.json index ce162cca95..335ecd9a26 100644 --- a/activation_service_poc/config.standalone.client.json +++ b/activation_service_poc/config.standalone.client.json @@ -11,7 +11,8 @@ "trtl": "WARN", "beacon": "ERROR", "proposalBuilder": "ERROR", - "atxBuilder": "DEBUG" + "atxBuilder": "DEBUG", + "hare": "DEBUG" }, "main": { "node-service-address": "http://0.0.0.0:9099", diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index 99d10328af..20f244fcb2 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -11,7 +11,8 @@ "logging": { "trtl": "WARN", "beacon": "ERROR", - "proposalBuilder": "ERROR" + "proposalBuilder": "ERROR", + "hare": "DEBUG" }, "main": { "data-folder": "/tmp/spacemesh-node-service", From d6cbb3cd5fa765a1bf4c8f8a91fca7af42f35660 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:18:54 -0600 Subject: [PATCH 14/21] beacon --- api/node/client/client.gen.go | 98 +++++++++++++++++++++++++++++++++++ api/node/client/client.go | 17 ++++++ api/node/node_service.yaml | 21 ++++++++ api/node/server/mocks.go | 38 ++++++++++++++ api/node/server/server.gen.go | 93 +++++++++++++++++++++++++++++++++ api/node/server/server.go | 15 ++++++ hare3/hare.go | 8 +++ hare3/remote.go | 9 +++- 8 files changed, 298 insertions(+), 1 deletion(-) diff --git a/api/node/client/client.gen.go b/api/node/client/client.gen.go index 830c8ace83..5216946db2 100644 --- a/api/node/client/client.gen.go +++ b/api/node/client/client.gen.go @@ -115,6 +115,9 @@ type ClientInterface interface { // GetActivationPositioningAtxPublishEpoch request GetActivationPositioningAtxPublishEpoch(ctx context.Context, publishEpoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetHareBeaconEpoch request + GetHareBeaconEpoch(ctx context.Context, epoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetHareRoundTemplateLayerIterRound request GetHareRoundTemplateLayerIterRound(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -167,6 +170,18 @@ func (c *Client) GetActivationPositioningAtxPublishEpoch(ctx context.Context, pu return c.Client.Do(req) } +func (c *Client) GetHareBeaconEpoch(ctx context.Context, epoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHareBeaconEpochRequest(c.Server, epoch) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) GetHareRoundTemplateLayerIterRound(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewGetHareRoundTemplateLayerIterRoundRequest(c.Server, layer, iter, round) if err != nil { @@ -329,6 +344,40 @@ func NewGetActivationPositioningAtxPublishEpochRequest(server string, publishEpo return req, nil } +// NewGetHareBeaconEpochRequest generates requests for GetHareBeaconEpoch +func NewGetHareBeaconEpochRequest(server string, epoch externalRef0.EpochID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "epoch", runtime.ParamLocationPath, epoch) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/hare/beacon/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewGetHareRoundTemplateLayerIterRoundRequest generates requests for GetHareRoundTemplateLayerIterRound func NewGetHareRoundTemplateLayerIterRoundRequest(server string, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound) (*http.Request, error) { var err error @@ -569,6 +618,9 @@ type ClientWithResponsesInterface interface { // GetActivationPositioningAtxPublishEpochWithResponse request GetActivationPositioningAtxPublishEpochWithResponse(ctx context.Context, publishEpoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*GetActivationPositioningAtxPublishEpochResponse, error) + // GetHareBeaconEpochWithResponse request + GetHareBeaconEpochWithResponse(ctx context.Context, epoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*GetHareBeaconEpochResponse, error) + // GetHareRoundTemplateLayerIterRoundWithResponse request GetHareRoundTemplateLayerIterRoundWithResponse(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerIterRoundResponse, error) @@ -653,6 +705,27 @@ func (r GetActivationPositioningAtxPublishEpochResponse) StatusCode() int { return 0 } +type GetHareBeaconEpochResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r GetHareBeaconEpochResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetHareBeaconEpochResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type GetHareRoundTemplateLayerIterRoundResponse struct { Body []byte HTTPResponse *http.Response @@ -785,6 +858,15 @@ func (c *ClientWithResponses) GetActivationPositioningAtxPublishEpochWithRespons return ParseGetActivationPositioningAtxPublishEpochResponse(rsp) } +// GetHareBeaconEpochWithResponse request returning *GetHareBeaconEpochResponse +func (c *ClientWithResponses) GetHareBeaconEpochWithResponse(ctx context.Context, epoch externalRef0.EpochID, reqEditors ...RequestEditorFn) (*GetHareBeaconEpochResponse, error) { + rsp, err := c.GetHareBeaconEpoch(ctx, epoch, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetHareBeaconEpochResponse(rsp) +} + // GetHareRoundTemplateLayerIterRoundWithResponse request returning *GetHareRoundTemplateLayerIterRoundResponse func (c *ClientWithResponses) GetHareRoundTemplateLayerIterRoundWithResponse(ctx context.Context, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound, reqEditors ...RequestEditorFn) (*GetHareRoundTemplateLayerIterRoundResponse, error) { rsp, err := c.GetHareRoundTemplateLayerIterRound(ctx, layer, iter, round, reqEditors...) @@ -910,6 +992,22 @@ func ParseGetActivationPositioningAtxPublishEpochResponse(rsp *http.Response) (* return response, nil } +// ParseGetHareBeaconEpochResponse parses an HTTP response from a GetHareBeaconEpochWithResponse call +func ParseGetHareBeaconEpochResponse(rsp *http.Response) (*GetHareBeaconEpochResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetHareBeaconEpochResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + // ParseGetHareRoundTemplateLayerIterRoundResponse parses an HTTP response from a GetHareRoundTemplateLayerIterRoundWithResponse call func ParseGetHareRoundTemplateLayerIterRoundResponse(rsp *http.Response) (*GetHareRoundTemplateLayerIterRoundResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/api/node/client/client.go b/api/node/client/client.go index 0139996bf1..d2206ac45a 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -173,3 +173,20 @@ func (s *NodeService) MinerWeight(ctx context.Context, layer types.LayerID, node } return strconv.ParseUint(string(bytes), 10, 64) } + +func (s *NodeService) Beacon(ctx context.Context, epoch types.EpochID) (types.Beacon, error) { + v := types.Beacon{} + resp, err := s.client.GetHareBeaconEpoch(ctx, externalRef0.EpochID(epoch)) + if err != nil { + return v, err + } + if resp.StatusCode != http.StatusOK { + return v, fmt.Errorf("unexpected status: %s", resp.Status) + } + bytes, err := io.ReadAll(resp.Body) + if err != nil { + return v, fmt.Errorf("read all: %w", err) + } + copy(v[:], bytes) + return v, nil +} diff --git a/api/node/node_service.yaml b/api/node/node_service.yaml index 37dd5d56fb..97cbc7c079 100644 --- a/api/node/node_service.yaml +++ b/api/node/node_service.yaml @@ -202,4 +202,25 @@ paths: format: binary "204": description: did not find a message to retrieve + /hare/beacon/{epoch}: + get: + summary: Get the beacon value for an epoch + tags: + - "hare" + parameters: + - in: path + name: epoch + required: true + schema: + $ref: "models/components.yaml#/components/schemas/EpochID" + responses: + "200": + description: successfully found the epoch id + content: + application/octet-stream: + schema: + type: string + format: binary + "204": + description: did not find a message to retrieve diff --git a/api/node/server/mocks.go b/api/node/server/mocks.go index db911dff3e..11ba872186 100644 --- a/api/node/server/mocks.go +++ b/api/node/server/mocks.go @@ -102,6 +102,44 @@ func (m *Mockhare) EXPECT() *MockhareMockRecorder { return m.recorder } +// MinerWeight mocks base method. +func (m *Mockhare) MinerWeight(ctx context.Context, node types.NodeID, layer types.LayerID) uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MinerWeight", ctx, node, layer) + ret0, _ := ret[0].(uint64) + return ret0 +} + +// MinerWeight indicates an expected call of MinerWeight. +func (mr *MockhareMockRecorder) MinerWeight(ctx, node, layer any) *MockhareMinerWeightCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MinerWeight", reflect.TypeOf((*Mockhare)(nil).MinerWeight), ctx, node, layer) + return &MockhareMinerWeightCall{Call: call} +} + +// MockhareMinerWeightCall wrap *gomock.Call +type MockhareMinerWeightCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockhareMinerWeightCall) Return(arg0 uint64) *MockhareMinerWeightCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockhareMinerWeightCall) Do(f func(context.Context, types.NodeID, types.LayerID) uint64) *MockhareMinerWeightCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockhareMinerWeightCall) DoAndReturn(f func(context.Context, types.NodeID, types.LayerID) uint64) *MockhareMinerWeightCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // RoundMessage mocks base method. func (m *Mockhare) RoundMessage(layer types.LayerID, round hare3.IterRound) *hare3.Message { m.ctrl.T.Helper() diff --git a/api/node/server/server.gen.go b/api/node/server/server.gen.go index 0605c4325f..8e80c59d1e 100644 --- a/api/node/server/server.gen.go +++ b/api/node/server/server.gen.go @@ -45,6 +45,9 @@ type ServerInterface interface { // Get Positioning ATX ID with given maximum publish epoch // (GET /activation/positioning_atx/{publish_epoch}) GetActivationPositioningAtxPublishEpoch(w http.ResponseWriter, r *http.Request, publishEpoch externalRef0.EpochID) + // Get the beacon value for an epoch + // (GET /hare/beacon/{epoch}) + GetHareBeaconEpoch(w http.ResponseWriter, r *http.Request, epoch externalRef0.EpochID) // Get a hare message to sign // (GET /hare/round_template/{layer}/{iter}/{round}) GetHareRoundTemplateLayerIterRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound) @@ -146,6 +149,31 @@ func (siw *ServerInterfaceWrapper) GetActivationPositioningAtxPublishEpoch(w htt handler.ServeHTTP(w, r) } +// GetHareBeaconEpoch operation middleware +func (siw *ServerInterfaceWrapper) GetHareBeaconEpoch(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "epoch" ------------- + var epoch externalRef0.EpochID + + err = runtime.BindStyledParameterWithOptions("simple", "epoch", r.PathValue("epoch"), &epoch, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "epoch", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetHareBeaconEpoch(w, r, epoch) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // GetHareRoundTemplateLayerIterRound operation middleware func (siw *ServerInterfaceWrapper) GetHareRoundTemplateLayerIterRound(w http.ResponseWriter, r *http.Request) { @@ -410,6 +438,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc("GET "+options.BaseURL+"/activation/atx/{atx_id}", wrapper.GetActivationAtxAtxId) m.HandleFunc("GET "+options.BaseURL+"/activation/last_atx/{node_id}", wrapper.GetActivationLastAtxNodeId) m.HandleFunc("GET "+options.BaseURL+"/activation/positioning_atx/{publish_epoch}", wrapper.GetActivationPositioningAtxPublishEpoch) + m.HandleFunc("GET "+options.BaseURL+"/hare/beacon/{epoch}", wrapper.GetHareBeaconEpoch) m.HandleFunc("GET "+options.BaseURL+"/hare/round_template/{layer}/{iter}/{round}", wrapper.GetHareRoundTemplateLayerIterRound) m.HandleFunc("GET "+options.BaseURL+"/hare/total_weight/{layer}", wrapper.GetHareTotalWeightLayer) m.HandleFunc("GET "+options.BaseURL+"/hare/weight/{node_id}/{layer}", wrapper.GetHareWeightNodeIdLayer) @@ -507,6 +536,41 @@ func (response GetActivationPositioningAtxPublishEpoch200JSONResponse) VisitGetA return json.NewEncoder(w).Encode(response) } +type GetHareBeaconEpochRequestObject struct { + Epoch externalRef0.EpochID `json:"epoch"` +} + +type GetHareBeaconEpochResponseObject interface { + VisitGetHareBeaconEpochResponse(w http.ResponseWriter) error +} + +type GetHareBeaconEpoch200ApplicationoctetStreamResponse struct { + Body io.Reader + ContentLength int64 +} + +func (response GetHareBeaconEpoch200ApplicationoctetStreamResponse) VisitGetHareBeaconEpochResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/octet-stream") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type GetHareBeaconEpoch204Response struct { +} + +func (response GetHareBeaconEpoch204Response) VisitGetHareBeaconEpochResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + type GetHareRoundTemplateLayerIterRoundRequestObject struct { Layer externalRef0.LayerID `json:"layer"` Iter externalRef0.HareIter `json:"iter"` @@ -678,6 +742,9 @@ type StrictServerInterface interface { // Get Positioning ATX ID with given maximum publish epoch // (GET /activation/positioning_atx/{publish_epoch}) GetActivationPositioningAtxPublishEpoch(ctx context.Context, request GetActivationPositioningAtxPublishEpochRequestObject) (GetActivationPositioningAtxPublishEpochResponseObject, error) + // Get the beacon value for an epoch + // (GET /hare/beacon/{epoch}) + GetHareBeaconEpoch(ctx context.Context, request GetHareBeaconEpochRequestObject) (GetHareBeaconEpochResponseObject, error) // Get a hare message to sign // (GET /hare/round_template/{layer}/{iter}/{round}) GetHareRoundTemplateLayerIterRound(ctx context.Context, request GetHareRoundTemplateLayerIterRoundRequestObject) (GetHareRoundTemplateLayerIterRoundResponseObject, error) @@ -802,6 +869,32 @@ func (sh *strictHandler) GetActivationPositioningAtxPublishEpoch(w http.Response } } +// GetHareBeaconEpoch operation middleware +func (sh *strictHandler) GetHareBeaconEpoch(w http.ResponseWriter, r *http.Request, epoch externalRef0.EpochID) { + var request GetHareBeaconEpochRequestObject + + request.Epoch = epoch + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetHareBeaconEpoch(ctx, request.(GetHareBeaconEpochRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetHareBeaconEpoch") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetHareBeaconEpochResponseObject); ok { + if err := validResponse.VisitGetHareBeaconEpochResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // GetHareRoundTemplateLayerIterRound operation middleware func (sh *strictHandler) GetHareRoundTemplateLayerIterRound(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, iter externalRef0.HareIter, round externalRef0.HareRound) { var request GetHareRoundTemplateLayerIterRoundRequestObject diff --git a/api/node/server/server.go b/api/node/server/server.go index c03dd9d6cb..74d6d3c198 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -32,6 +32,7 @@ type hare interface { RoundMessage(layer types.LayerID, round hare3.IterRound) *hare3.Message TotalWeight(ctx context.Context, layer types.LayerID) uint64 MinerWeight(ctx context.Context, node types.NodeID, layer types.LayerID) uint64 + Beacon(ctx context.Context, epoch types.EpochID) types.Beacon } type Server struct { @@ -269,3 +270,17 @@ func (s *Server) GetHareWeightNodeIdLayer(ctx context.Context, request GetHareWe } return &nodeWeightResp{val: s.hare.MinerWeight(ctx, *id, types.LayerID(request.Layer))}, nil } + +type beaconResp struct{ b types.Beacon } + +func (b *beaconResp) VisitGetHareBeaconEpochResponse(w http.ResponseWriter) error { + w.Header().Add("content-type", "application/octet-stream") + w.WriteHeader(200) + _, err := w.Write(b.b[:]) + return err +} + +func (s *Server) GetHareBeaconEpoch(ctx context.Context, request GetHareBeaconEpochRequestObject) (GetHareBeaconEpochResponseObject, error) { + beacon := s.hare.Beacon(ctx, types.EpochID(request.Epoch)) + return &beaconResp{b: beacon}, nil +} diff --git a/hare3/hare.go b/hare3/hare.go index f3b2e7b542..932c6c4ff5 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -679,3 +679,11 @@ func (h *Hare) TotalWeight(ctx context.Context, layer types.LayerID) uint64 { func (h *Hare) MinerWeight(ctx context.Context, miner types.NodeID, layer types.LayerID) uint64 { return h.oracle.oracle.MinerWeight(ctx, miner, layer) } + +func (h *Hare) Beacon(ctx context.Context, epoch types.EpochID) types.Beacon { + beacon, err := beacons.Get(h.db, epoch) + if err != nil { + panic(err) + } + return beacon +} diff --git a/hare3/remote.go b/hare3/remote.go index 8c3b1de100..40108f7df7 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -18,6 +18,7 @@ import ( type nodeService interface { GetHareMessage(ctx context.Context, layer types.LayerID, round IterRound) ([]byte, error) + Beacon(ctx context.Context, epoch types.EpochID) (types.Beacon, error) Publish(ctx context.Context, proto string, blob []byte) error } @@ -98,7 +99,13 @@ func (h *RemoteHare) beacon(e types.EpochID) types.Beacon { defer h.mu.Unlock() b, ok := h.beacons[e] if !ok { - return types.EmptyBeacon + bcn, err := h.svc.Beacon(context.Background(), e) + if err != nil { + h.log.Error("error getting beacon", zap.Error(err)) + return types.EmptyBeacon + } + h.beacons[e] = bcn + return bcn } return b From a12e1ab9f2e35336414bb46e361089dfb36bc455 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:28:27 -0600 Subject: [PATCH 15/21] wip --- hare3/hare.go | 1 + 1 file changed, 1 insertion(+) diff --git a/hare3/hare.go b/hare3/hare.go index 932c6c4ff5..97b8328daf 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -181,6 +181,7 @@ func New( patrol *layerpatrol.LayerPatrol, opts ...Opt, ) *Hare { + log.Info("new hare3") ctx, cancel := context.WithCancel(context.Background()) hr := &Hare{ ctx: ctx, From 33770dd33dc187f7122f06091387fece8d0569db Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:08:36 -0600 Subject: [PATCH 16/21] add hare3 enable --- activation_service_poc/config.standalone.node-service.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index 20f244fcb2..6bc4611e53 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -14,6 +14,9 @@ "proposalBuilder": "ERROR", "hare": "DEBUG" }, + "hare3": { + "enable": true + }, "main": { "data-folder": "/tmp/spacemesh-node-service", "filelock": "/tmp/spacemesh-node-service/node.lock" From bebd00d251625ceb2cccd264693f3363cc4da9e0 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:30:12 -0600 Subject: [PATCH 17/21] advance onround anyway --- hare3/remote.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hare3/remote.go b/hare3/remote.go index 40108f7df7..6b0a9ee9b2 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -70,7 +70,7 @@ func (h *RemoteHare) Register(sig *signing.EdSigner) { func (h *RemoteHare) Start() { current := h.nodeClock.CurrentLayer() + 1 enableLayer := types.LayerID(0) - enabled := max(current, enableLayer /* h.config.EnableLayer*/, types.GetEffectiveGenesis()+1) + enabled := max(current, h.config.EnableLayer, types.GetEffectiveGenesis()+1) disabled := types.LayerID(math.MaxUint32) h.log.Info("started", zap.Inline(&h.config), @@ -198,12 +198,12 @@ func (h *RemoteHare) run(session *session) error { walltime = walltime.Add(h.config.RoundDuration) current = session.proto.IterRound start = time.Now() - eligible := false + active := false for i := range session.signers { if current.IsMessageRound() { session.vrfs[i] = h.oracle.active(session.signers[i], session.beacon, session.lid, current) - eligible = eligible || (session.vrfs[i] != nil) + active = active || (session.vrfs[i] != nil) } else { session.vrfs[i] = nil } @@ -230,8 +230,9 @@ func (h *RemoteHare) run(session *session) error { h.log.Error("decode remote hare message", zap.Error(err)) } h.signPub(session, msg) - onRound(session.proto) // advance the protocol state before continuing } + + onRound(session.proto) // advance the protocol state before continuing case <-h.ctx.Done(): return nil } From bbf96a0bb33fb086fd86d24058a484e106732144 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:32:13 -0600 Subject: [PATCH 18/21] build --- hare3/remote.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hare3/remote.go b/hare3/remote.go index 6b0a9ee9b2..1b66fb49e3 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -69,7 +69,6 @@ func (h *RemoteHare) Register(sig *signing.EdSigner) { func (h *RemoteHare) Start() { current := h.nodeClock.CurrentLayer() + 1 - enableLayer := types.LayerID(0) enabled := max(current, h.config.EnableLayer, types.GetEffectiveGenesis()+1) disabled := types.LayerID(math.MaxUint32) h.log.Info("started", @@ -218,7 +217,7 @@ func (h *RemoteHare) run(session *session) error { zap.Bool("active", active), ) - if eligible { + if active { msgBytes, err := h.svc.GetHareMessage(context.Background(), session.lid, session.proto.IterRound) if err != nil { h.log.Error("get hare message", zap.Error(err)) From 11b01bf8210bd36e1416387a2602eecad39a11f5 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Sat, 12 Oct 2024 07:44:15 -0600 Subject: [PATCH 19/21] wip --- hare3/hare.go | 1 - hare3/remote.go | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/hare3/hare.go b/hare3/hare.go index 97b8328daf..932c6c4ff5 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -181,7 +181,6 @@ func New( patrol *layerpatrol.LayerPatrol, opts ...Opt, ) *Hare { - log.Info("new hare3") ctx, cancel := context.WithCancel(context.Background()) hr := &Hare{ ctx: ctx, diff --git a/hare3/remote.go b/hare3/remote.go index 1b66fb49e3..95e661fe86 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -190,7 +190,7 @@ func (h *RemoteHare) run(session *session) error { } onRound(session.proto) for { - if session.proto.IterRound.Iter > h.config.IterationsLimit { + if session.proto.IterRound.Iter >= h.config.IterationsLimit { return nil } @@ -211,13 +211,13 @@ func (h *RemoteHare) run(session *session) error { select { case <-h.wallClock.After(walltime.Sub(h.wallClock.Now())): - h.log.Debug("execute round", - zap.Uint32("lid", session.lid.Uint32()), - zap.Uint8("iter", session.proto.Iter), zap.Stringer("round", session.proto.Round), - zap.Bool("active", active), - ) - if active { + h.log.Debug("execute round", + zap.Uint32("lid", session.lid.Uint32()), + zap.Uint8("iter", session.proto.Iter), zap.Stringer("round", session.proto.Round), + zap.Bool("active", active), + ) + msgBytes, err := h.svc.GetHareMessage(context.Background(), session.lid, session.proto.IterRound) if err != nil { h.log.Error("get hare message", zap.Error(err)) From d74fec8ed10fdd9a0c77ede1befa24884e7a34ec Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Mon, 14 Oct 2024 07:41:11 -0600 Subject: [PATCH 20/21] lg --- hare3/hare.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hare3/hare.go b/hare3/hare.go index 932c6c4ff5..64efb84c03 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -363,6 +363,8 @@ func (h *Hare) onLayer(layer types.LayerID) { return } beacon, err := beacons.Get(h.db, layer.GetEpoch()) + fmt.Println("beacon value get result", beacon) + h.log.Info("hare tried to get beacon value", zap.Error(err)) if err != nil || beacon == types.EmptyBeacon { h.log.Debug("no beacon", zap.Uint32("epoch", layer.GetEpoch().Uint32()), @@ -372,7 +374,7 @@ func (h *Hare) onLayer(layer types.LayerID) { return } h.patrol.SetHareInCharge(layer) - + fmt.Println("continuing hare") h.mu.Lock() // signer can't join mid session s := &session{ From cff3752987e24159bb04a931372222072cd8df4a Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:37:32 -0600 Subject: [PATCH 21/21] enable smeshing and enable hare --- activation_service_poc/config.standalone.node-service.json | 3 --- config/presets/standalone.go | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index 6bc4611e53..e511285b6c 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -20,8 +20,5 @@ "main": { "data-folder": "/tmp/spacemesh-node-service", "filelock": "/tmp/spacemesh-node-service/node.lock" - }, - "smeshing": { - "smeshing-start": false } } diff --git a/config/presets/standalone.go b/config/presets/standalone.go index b49b3860a8..8e85260d7e 100644 --- a/config/presets/standalone.go +++ b/config/presets/standalone.go @@ -36,7 +36,7 @@ func standalone() config.Config { conf.LayerDuration = 6 * time.Second conf.Sync.Interval = 3 * time.Second conf.LayersPerEpoch = 10 - + conf.HARE3.Enable = true conf.HARE3.PreroundDelay = 1 * time.Second conf.HARE3.RoundDuration = 100 * time.Millisecond