From 271da0fabf5cc4c940f2dae1fb2fcba25b850f07 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:03:39 -0600 Subject: [PATCH 01/71] wip build proposals --- miner/proposal_builder.go | 206 +++++++++++++++++++++++++++++++++++++- txs/conservative_state.go | 2 +- 2 files changed, 206 insertions(+), 2 deletions(-) diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index a9a3403502..b5dbf8907e 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -41,7 +41,8 @@ var errAtxNotAvailable = errors.New("atx not available") //go:generate mockgen -typed -package=mocks -destination=./mocks/mocks.go -source=./proposal_builder.go type conservativeState interface { - SelectProposalTXs(types.LayerID, int) []types.TransactionID + SelectProposalTXs(types.LayerID, int, bool) []types.TransactionID + PredictBlock(types.LayerID) []types.TransactionID } type votesEncoder interface { @@ -570,6 +571,209 @@ func (pb *ProposalBuilder) initSignerData(ctx context.Context, ss *signerSession return nil } +func (pb *ProposalBuilder) initSignerDataFor(ctx context.Context, ss *signerSession, lid types.LayerID, nodeID types.NodeID) error { + if ss.session.epoch != lid.GetEpoch() { + ss.session = session{epoch: lid.GetEpoch()} + } + if ss.session.atx == types.EmptyATXID { + atxid, err := pb.atxs.GetIDByEpochAndNodeID(ctx, ss.session.epoch-1, nodeID) + switch { + case errors.Is(err, sql.ErrNotFound): + return errAtxNotAvailable + case err != nil: + return fmt.Errorf("get atx in epoch %v: %w", ss.session.epoch-1, err) + } + atx := pb.atxsdata.Get(ss.session.epoch, atxid) + if atx == nil { + return fmt.Errorf("missing atx in atxsdata %v", atxid) + } + ss.session.atx = atxid + ss.session.atxWeight = atx.Weight + ss.session.nonce = atx.Nonce + } + if ss.session.prev == 0 { + prev, err := ballots.LastInEpoch(pb.db, ss.session.atx, ss.session.epoch) + if err != nil && !errors.Is(err, sql.ErrNotFound) { + return err + } + if err == nil { + ss.session.prev = prev.Layer + } + } + if ss.session.ref == types.EmptyBallotID { + ballot, err := ballots.FirstInEpoch(pb.db, ss.session.atx, ss.session.epoch) + if err != nil && !errors.Is(err, sql.ErrNotFound) { + return fmt.Errorf("get refballot %w", err) + } + if errors.Is(err, sql.ErrNotFound) { + ss.session.beacon = pb.shared.beacon + ss.session.eligibilities.slots = proposals.MustGetNumEligibleSlots( + ss.session.atxWeight, + minweight.Select(lid.GetEpoch(), pb.cfg.minActiveSetWeight), + pb.shared.active.weight, + pb.cfg.layerSize, + pb.cfg.layersPerEpoch, + ) + } else { + if ballot.EpochData == nil { + return fmt.Errorf("atx %d created invalid first ballot", ss.session.atx) + } + ss.session.ref = ballot.ID() + ss.session.beacon = ballot.EpochData.Beacon + ss.session.eligibilities.slots = ballot.EpochData.EligibilityCount + } + } + return nil +} + +func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, nodeID types.NodeID) error { + if err := pb.initSharedData(lid); err != nil { + return err + } + + // don't accept registration in the middle of computing proposals + // pb.signers.mu.Lock() + // signers := maps.Values(pb.signers.signers) + // pb.signers.mu.Unlock() + signer := &signerSession{} + encodeVotesOnce := sync.OnceValues(func() (*types.Opinion, error) { + pb.tortoise.TallyVotes(ctx, lid) + // TODO(dshulyak) get rid from the EncodeVotesWithCurrent option in a followup + // there are some dependencies in the tests + opinion, err := pb.tortoise.EncodeVotes(ctx, tortoise.EncodeVotesWithCurrent(lid)) + if err != nil { + return nil, fmt.Errorf("encoding votes: %w", err) + } + return opinion, nil + }) + + calcMeshHashOnce := sync.OnceValue(func() types.Hash32 { + meshHash := pb.decideMeshHash(ctx, lid) + return meshHash + }) + + persistActiveSetOnce := sync.OnceValue(func() error { + err := activesets.Add(pb.db, pb.shared.active.id, &types.EpochActiveSet{ + Epoch: pb.shared.epoch, + Set: pb.shared.active.set, + }) + if err != nil && !errors.Is(err, sql.ErrObjectExists) { + return err + } + return nil + }) + + // Two stage pipeline, with the stages running in parallel. + // 1. Initializes signers. Runs limited number of goroutines because the initialization is CPU and DB bound. + // 2. Collects eligible signers' sessions from the stage 1 and creates and publishes proposals. + + // Used to pass eligible singers from stage 1 → 2. + // Buffered with capacity for all signers so that writes don't block. + eligible := make(chan *signerSession, 2) + + // Stage 1 + // Use a semaphore instead of eg.SetLimit so that the stage 2 starts immediately after + // scheduling all signers in the stage 1. Otherwise, stage 2 would wait for all stage 1 + // goroutines to at least start, which is not what we want. We want to start stage 2 as soon as possible. + // limiter := semaphore.NewWeighted(int64(pb.cfg.workersLimit)) + var eg errgroup.Group + // for _, ss := range signers { + eg.Go(func() error { + if err := pb.initSignerDataFor(ctx, signer, lid, nodeID); err != nil { + if errors.Is(err, errAtxNotAvailable) { + pb.logger.Debug("smesher doesn't have atx that targets this epoch", + log.ZContext(ctx), + zap.Uint32("epoch_id", signer.session.epoch.Uint32()), + ) + } else { + return err + } + } + if lid <= signer.session.prev { + return fmt.Errorf("layer %d was already built by signer %s", lid, nodeID.ShortString()) + } + signer.session.prev = lid + proofs := signer.session.eligibilities.slots + if proofs == 0 { + pb.logger.Debug("not eligible for proposal in layer", + log.ZContext(ctx), + zap.Uint32("layer_id", lid.Uint32()), + zap.Uint32("epoch_id", lid.GetEpoch().Uint32()), + ) + return nil + } + pb.logger.Debug("eligible for proposals in layer", + log.ZContext(ctx), + zap.Uint32("layer_id", lid.Uint32()), + zap.Uint32("epoch_id", lid.GetEpoch().Uint32()), + zap.Int("num proposals", int(proofs)), + ) + eligible <- signer // won't block + return nil + }) + //} + + var stage1Err error + go func() { + stage1Err = eg.Wait() + close(eligible) + }() + + // Stage 2 + eg2 := errgroup.Group{} + for ss := range eligible { + opinion, err := encodeVotesOnce() + if err != nil { + return err + } + + meshHash := calcMeshHashOnce() + + eg2.Go(func() error { + // needs to be saved before publishing, as we will query it in handler + if ss.session.ref == types.EmptyBallotID { + if err := persistActiveSetOnce(); err != nil { + return err + } + } + slots := ss.session.eligibilities.slots + + txs := pb.conState.SelectProposalTXs(lid, int(slots)) + + proposal := createPartialProposal( + &ss.session, + pb.shared.beacon, + pb.shared.active.set, + ss.signer, + lid, + txs, + opinion, + meshHash, + ) + if err := pb.publisher.Publish(ctx, pubsub.ProposalProtocol, codec.MustEncode(proposal)); err != nil { + pb.logger.Error("failed to publish proposal", + log.ZContext(ctx), + zap.Uint32("lid", proposal.Layer.Uint32()), + zap.Stringer("id", proposal.ID()), + zap.Error(err), + ) + } else { + pb.logger.Info("proposal created", + log.ZContext(ctx), + zap.Inline(proposal), + zap.Object("latency", &ss.latency), + ) + proposalBuild.Observe(ss.latency.total().Seconds()) + events.EmitProposal(ss.signer.NodeID(), lid, proposal.ID()) + events.ReportProposal(events.ProposalCreated, proposal) + } + return nil + }) + } + + return errors.Join(stage1Err, eg2.Wait()) +} + func (pb *ProposalBuilder) build(ctx context.Context, lid types.LayerID) error { buildStartTime := time.Now() if err := pb.initSharedData(lid); err != nil { diff --git a/txs/conservative_state.go b/txs/conservative_state.go index c252e540f5..6e84f4c9c4 100644 --- a/txs/conservative_state.go +++ b/txs/conservative_state.go @@ -87,7 +87,7 @@ func (cs *ConservativeState) getState(addr types.Address) (uint64, uint64) { } // SelectProposalTXs picks a specific number of random txs for miner to pack in a proposal. -func (cs *ConservativeState) SelectProposalTXs(lid types.LayerID, numEligibility int) []types.TransactionID { +func (cs *ConservativeState) SelectProposalTXs(lid types.LayerID, numEligibility int, shuffle bool) []types.TransactionID { logger := cs.logger.With(zap.Uint32("layer_id", lid.Uint32())) mi := newMempoolIterator(logger, cs.cache, cs.cfg.BlockGasLimit) predictedBlock, byAddrAndNonce := mi.PopAll() From 738938eefc88fa8bbc822342da0993d9aee909bc Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:35:35 -0600 Subject: [PATCH 02/71] wip --- miner/proposal_builder.go | 81 ++++++++++++++++++++++++++------------- txs/conservative_state.go | 15 +++++++- 2 files changed, 68 insertions(+), 28 deletions(-) diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index b5dbf8907e..145355beef 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -41,8 +41,8 @@ var errAtxNotAvailable = errors.New("atx not available") //go:generate mockgen -typed -package=mocks -destination=./mocks/mocks.go -source=./proposal_builder.go type conservativeState interface { - SelectProposalTXs(types.LayerID, int, bool) []types.TransactionID - PredictBlock(types.LayerID) []types.TransactionID + SelectProposalTXs(types.LayerID, int) []types.TransactionID + PredictBlock(types.LayerID, int) []types.TransactionID } type votesEncoder interface { @@ -626,9 +626,9 @@ func (pb *ProposalBuilder) initSignerDataFor(ctx context.Context, ss *signerSess return nil } -func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, nodeID types.NodeID) error { +func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, nodeID types.NodeID) (*types.Proposal, error) { if err := pb.initSharedData(lid); err != nil { - return err + return nil, err } // don't accept registration in the middle of computing proposals @@ -719,12 +719,13 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, node close(eligible) }() + var prop *types.Proposal // Stage 2 eg2 := errgroup.Group{} for ss := range eligible { opinion, err := encodeVotesOnce() if err != nil { - return err + return nil, err } meshHash := calcMeshHashOnce() @@ -738,40 +739,28 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, node } slots := ss.session.eligibilities.slots - txs := pb.conState.SelectProposalTXs(lid, int(slots)) + txs := pb.conState.PredictBlock(lid, int(slots)) - proposal := createPartialProposal( + prop = createPartialProposal( &ss.session, pb.shared.beacon, pb.shared.active.set, - ss.signer, + nodeID, lid, txs, opinion, meshHash, ) - if err := pb.publisher.Publish(ctx, pubsub.ProposalProtocol, codec.MustEncode(proposal)); err != nil { - pb.logger.Error("failed to publish proposal", - log.ZContext(ctx), - zap.Uint32("lid", proposal.Layer.Uint32()), - zap.Stringer("id", proposal.ID()), - zap.Error(err), - ) - } else { - pb.logger.Info("proposal created", - log.ZContext(ctx), - zap.Inline(proposal), - zap.Object("latency", &ss.latency), - ) - proposalBuild.Observe(ss.latency.total().Seconds()) - events.EmitProposal(ss.signer.NodeID(), lid, proposal.ID()) - events.ReportProposal(events.ProposalCreated, proposal) - } + pb.logger.Info("proposal created", + log.ZContext(ctx), + zap.Inline(prop), + zap.Object("latency", &ss.latency), + ) return nil }) } - - return errors.Join(stage1Err, eg2.Wait()) + err := errors.Join(stage1Err, eg2.Wait()) + return prop, err } func (pb *ProposalBuilder) build(ctx context.Context, lid types.LayerID) error { @@ -948,6 +937,44 @@ func (pb *ProposalBuilder) build(ctx context.Context, lid types.LayerID) error { return errors.Join(stage1Err, eg2.Wait()) } +func createPartialProposal( + session *session, + beacon types.Beacon, + activeset types.ATXIDList, + smesher types.NodeID, + lid types.LayerID, + txs []types.TransactionID, + opinion *types.Opinion, + meshHash types.Hash32, +) *types.Proposal { + p := &types.Proposal{ + InnerProposal: types.InnerProposal{ + Ballot: types.Ballot{ + InnerBallot: types.InnerBallot{ + Layer: lid, + AtxID: session.atx, + OpinionHash: opinion.Hash, + }, + Votes: opinion.Votes, + // EligibilityProofs: eligibility, + }, + TxIDs: txs, + MeshHash: meshHash, + }, + } + if session.ref == types.EmptyBallotID { + p.Ballot.RefBallot = types.EmptyBallotID + p.Ballot.EpochData = &types.EpochData{ + ActiveSetHash: activeset.Hash(), + Beacon: beacon, + } + } else { + p.Ballot.RefBallot = session.ref + } + p.SmesherID = smesher + return p +} + func createProposal( session *session, beacon types.Beacon, diff --git a/txs/conservative_state.go b/txs/conservative_state.go index 6e84f4c9c4..f48471e57b 100644 --- a/txs/conservative_state.go +++ b/txs/conservative_state.go @@ -87,7 +87,7 @@ func (cs *ConservativeState) getState(addr types.Address) (uint64, uint64) { } // SelectProposalTXs picks a specific number of random txs for miner to pack in a proposal. -func (cs *ConservativeState) SelectProposalTXs(lid types.LayerID, numEligibility int, shuffle bool) []types.TransactionID { +func (cs *ConservativeState) SelectProposalTXs(lid types.LayerID, numEligibility int) []types.TransactionID { logger := cs.logger.With(zap.Uint32("layer_id", lid.Uint32())) mi := newMempoolIterator(logger, cs.cache, cs.cfg.BlockGasLimit) predictedBlock, byAddrAndNonce := mi.PopAll() @@ -95,6 +95,19 @@ func (cs *ConservativeState) SelectProposalTXs(lid types.LayerID, numEligibility return getProposalTXs(logger, numTXs, predictedBlock, byAddrAndNonce) } +func (cs *ConservativeState) PredictBlock(lid types.LayerID, numEligibility int) []types.TransactionID { + logger := cs.logger.With(zap.Uint32("layer_id", lid.Uint32())) + mi := newMempoolIterator(logger, cs.cache, cs.cfg.BlockGasLimit) + predictedBlock, _ := mi.PopAll() + numTXs := numEligibility * cs.cfg.NumTXsPerProposal + n := min(numTXs, len(predictedBlock)) + txs := make([]types.TransactionID, 0, n) + for i, tx := range predictedBlock[:n] { + txs[i] = tx.ID + } + return txs +} + func getProposalTXs( logger *zap.Logger, numTXs int, From 7b19bc9463dacd5aaed8fb709821315370f8a054 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:04:28 -0600 Subject: [PATCH 03/71] api and make gen --- api/node/client/client.gen.go | 105 ++++++++++++++++++++++++++++++++++ api/node/node_service.yaml | 26 +++++++++ api/node/server/mocks.go | 38 ++++++++++++ api/node/server/server.gen.go | 104 +++++++++++++++++++++++++++++++++ miner/mocks/mocks.go | 38 ++++++++++++ miner/proposal_builder.go | 3 - 6 files changed, 311 insertions(+), 3 deletions(-) diff --git a/api/node/client/client.gen.go b/api/node/client/client.gen.go index 5216946db2..63870f0b8c 100644 --- a/api/node/client/client.gen.go +++ b/api/node/client/client.gen.go @@ -130,6 +130,9 @@ type ClientInterface interface { // PostPoetWithBody request with any body PostPoetWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetProposalLayerNode request + GetProposalLayerNode(ctx context.Context, layer externalRef0.LayerID, node externalRef0.NodeID, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostPublishProtocolWithBody request with any body PostPublishProtocolWithBody(ctx context.Context, protocol PostPublishProtocolParamsProtocol, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) } @@ -230,6 +233,18 @@ func (c *Client) PostPoetWithBody(ctx context.Context, contentType string, body return c.Client.Do(req) } +func (c *Client) GetProposalLayerNode(ctx context.Context, layer externalRef0.LayerID, node externalRef0.NodeID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetProposalLayerNodeRequest(c.Server, layer, node) + 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) PostPublishProtocolWithBody(ctx context.Context, protocol PostPublishProtocolParamsProtocol, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostPublishProtocolRequestWithBody(c.Server, protocol, contentType, body) if err != nil { @@ -530,6 +545,47 @@ func NewPostPoetRequestWithBody(server string, contentType string, body io.Reade return req, nil } +// NewGetProposalLayerNodeRequest generates requests for GetProposalLayerNode +func NewGetProposalLayerNodeRequest(server string, layer externalRef0.LayerID, node externalRef0.NodeID) (*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, "node", runtime.ParamLocationPath, node) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/proposal/%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 +} + // NewPostPublishProtocolRequestWithBody generates requests for PostPublishProtocol with any type of body func NewPostPublishProtocolRequestWithBody(server string, protocol PostPublishProtocolParamsProtocol, contentType string, body io.Reader) (*http.Request, error) { var err error @@ -633,6 +689,9 @@ type ClientWithResponsesInterface interface { // PostPoetWithBodyWithResponse request with any body PostPoetWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPoetResponse, error) + // GetProposalLayerNodeWithResponse request + GetProposalLayerNodeWithResponse(ctx context.Context, layer externalRef0.LayerID, node externalRef0.NodeID, reqEditors ...RequestEditorFn) (*GetProposalLayerNodeResponse, error) + // PostPublishProtocolWithBodyWithResponse request with any body PostPublishProtocolWithBodyWithResponse(ctx context.Context, protocol PostPublishProtocolParamsProtocol, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPublishProtocolResponse, error) } @@ -810,6 +869,27 @@ func (r PostPoetResponse) StatusCode() int { return 0 } +type GetProposalLayerNodeResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r GetProposalLayerNodeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetProposalLayerNodeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type PostPublishProtocolResponse struct { Body []byte HTTPResponse *http.Response @@ -903,6 +983,15 @@ func (c *ClientWithResponses) PostPoetWithBodyWithResponse(ctx context.Context, return ParsePostPoetResponse(rsp) } +// GetProposalLayerNodeWithResponse request returning *GetProposalLayerNodeResponse +func (c *ClientWithResponses) GetProposalLayerNodeWithResponse(ctx context.Context, layer externalRef0.LayerID, node externalRef0.NodeID, reqEditors ...RequestEditorFn) (*GetProposalLayerNodeResponse, error) { + rsp, err := c.GetProposalLayerNode(ctx, layer, node, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetProposalLayerNodeResponse(rsp) +} + // PostPublishProtocolWithBodyWithResponse request with arbitrary body returning *PostPublishProtocolResponse func (c *ClientWithResponses) PostPublishProtocolWithBodyWithResponse(ctx context.Context, protocol PostPublishProtocolParamsProtocol, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostPublishProtocolResponse, error) { rsp, err := c.PostPublishProtocolWithBody(ctx, protocol, contentType, body, reqEditors...) @@ -1072,6 +1161,22 @@ func ParsePostPoetResponse(rsp *http.Response) (*PostPoetResponse, error) { return response, nil } +// ParseGetProposalLayerNodeResponse parses an HTTP response from a GetProposalLayerNodeWithResponse call +func ParseGetProposalLayerNodeResponse(rsp *http.Response) (*GetProposalLayerNodeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetProposalLayerNodeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + // ParsePostPublishProtocolResponse parses an HTTP response from a PostPublishProtocolWithResponse call func ParsePostPublishProtocolResponse(rsp *http.Response) (*PostPublishProtocolResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/api/node/node_service.yaml b/api/node/node_service.yaml index 97cbc7c079..f618c67cd2 100644 --- a/api/node/node_service.yaml +++ b/api/node/node_service.yaml @@ -223,4 +223,30 @@ paths: format: binary "204": description: did not find a message to retrieve + /proposal/{layer}/{node}: + get: + summary: Get a partial proposal for a given node in a layer + tags: + - "proposals" + parameters: + - in: path + name: layer + required: true + schema: + $ref: "models/components.yaml#/components/schemas/LayerID" + - in: path + name: node + required: true + schema: + $ref: "models/components.yaml#/components/schemas/NodeID" + responses: + "200": + description: successfully created a partial proposal + content: + application/octet-stream: + schema: + type: string + format: binary + "500": + description: could not generate proposal diff --git a/api/node/server/mocks.go b/api/node/server/mocks.go index 11ba872186..d968050499 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 } +// Beacon mocks base method. +func (m *Mockhare) Beacon(ctx context.Context, epoch types.EpochID) types.Beacon { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Beacon", ctx, epoch) + ret0, _ := ret[0].(types.Beacon) + return ret0 +} + +// Beacon indicates an expected call of Beacon. +func (mr *MockhareMockRecorder) Beacon(ctx, epoch any) *MockhareBeaconCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Beacon", reflect.TypeOf((*Mockhare)(nil).Beacon), ctx, epoch) + return &MockhareBeaconCall{Call: call} +} + +// MockhareBeaconCall wrap *gomock.Call +type MockhareBeaconCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockhareBeaconCall) Return(arg0 types.Beacon) *MockhareBeaconCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockhareBeaconCall) Do(f func(context.Context, types.EpochID) types.Beacon) *MockhareBeaconCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockhareBeaconCall) DoAndReturn(f func(context.Context, types.EpochID) types.Beacon) *MockhareBeaconCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // MinerWeight mocks base method. func (m *Mockhare) MinerWeight(ctx context.Context, node types.NodeID, layer types.LayerID) uint64 { m.ctrl.T.Helper() diff --git a/api/node/server/server.gen.go b/api/node/server/server.gen.go index 8e80c59d1e..d48943aaa3 100644 --- a/api/node/server/server.gen.go +++ b/api/node/server/server.gen.go @@ -60,6 +60,9 @@ type ServerInterface interface { // Store PoET proof // (POST /poet) PostPoet(w http.ResponseWriter, r *http.Request) + // Get a partial proposal for a given node in a layer + // (GET /proposal/{layer}/{node}) + GetProposalLayerNode(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, node externalRef0.NodeID) // Publish a blob in the given p2p protocol // (POST /publish/{protocol}) PostPublishProtocol(w http.ResponseWriter, r *http.Request, protocol PostPublishProtocolParamsProtocol) @@ -290,6 +293,40 @@ func (siw *ServerInterfaceWrapper) PostPoet(w http.ResponseWriter, r *http.Reque handler.ServeHTTP(w, r) } +// GetProposalLayerNode operation middleware +func (siw *ServerInterfaceWrapper) GetProposalLayerNode(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 "node" ------------- + var node externalRef0.NodeID + + err = runtime.BindStyledParameterWithOptions("simple", "node", r.PathValue("node"), &node, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "node", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetProposalLayerNode(w, r, layer, node) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // PostPublishProtocol operation middleware func (siw *ServerInterfaceWrapper) PostPublishProtocol(w http.ResponseWriter, r *http.Request) { @@ -443,6 +480,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H 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("GET "+options.BaseURL+"/proposal/{layer}/{node}", wrapper.GetProposalLayerNode) m.HandleFunc("POST "+options.BaseURL+"/publish/{protocol}", wrapper.PostPublishProtocol) return m @@ -714,6 +752,42 @@ func (response PostPoet400PlaintextResponse) VisitPostPoetResponse(w http.Respon return err } +type GetProposalLayerNodeRequestObject struct { + Layer externalRef0.LayerID `json:"layer"` + Node externalRef0.NodeID `json:"node"` +} + +type GetProposalLayerNodeResponseObject interface { + VisitGetProposalLayerNodeResponse(w http.ResponseWriter) error +} + +type GetProposalLayerNode200ApplicationoctetStreamResponse struct { + Body io.Reader + ContentLength int64 +} + +func (response GetProposalLayerNode200ApplicationoctetStreamResponse) VisitGetProposalLayerNodeResponse(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 GetProposalLayerNode500Response struct { +} + +func (response GetProposalLayerNode500Response) VisitGetProposalLayerNodeResponse(w http.ResponseWriter) error { + w.WriteHeader(500) + return nil +} + type PostPublishProtocolRequestObject struct { Protocol PostPublishProtocolParamsProtocol `json:"protocol"` Body io.Reader @@ -757,6 +831,9 @@ type StrictServerInterface interface { // Store PoET proof // (POST /poet) PostPoet(ctx context.Context, request PostPoetRequestObject) (PostPoetResponseObject, error) + // Get a partial proposal for a given node in a layer + // (GET /proposal/{layer}/{node}) + GetProposalLayerNode(ctx context.Context, request GetProposalLayerNodeRequestObject) (GetProposalLayerNodeResponseObject, error) // Publish a blob in the given p2p protocol // (POST /publish/{protocol}) PostPublishProtocol(ctx context.Context, request PostPublishProtocolRequestObject) (PostPublishProtocolResponseObject, error) @@ -1002,6 +1079,33 @@ func (sh *strictHandler) PostPoet(w http.ResponseWriter, r *http.Request) { } } +// GetProposalLayerNode operation middleware +func (sh *strictHandler) GetProposalLayerNode(w http.ResponseWriter, r *http.Request, layer externalRef0.LayerID, node externalRef0.NodeID) { + var request GetProposalLayerNodeRequestObject + + request.Layer = layer + request.Node = node + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetProposalLayerNode(ctx, request.(GetProposalLayerNodeRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetProposalLayerNode") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetProposalLayerNodeResponseObject); ok { + if err := validResponse.VisitGetProposalLayerNodeResponse(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)) + } +} + // PostPublishProtocol operation middleware func (sh *strictHandler) PostPublishProtocol(w http.ResponseWriter, r *http.Request, protocol PostPublishProtocolParamsProtocol) { var request PostPublishProtocolRequestObject diff --git a/miner/mocks/mocks.go b/miner/mocks/mocks.go index c873f06297..0e53009a8b 100644 --- a/miner/mocks/mocks.go +++ b/miner/mocks/mocks.go @@ -42,6 +42,44 @@ func (m *MockconservativeState) EXPECT() *MockconservativeStateMockRecorder { return m.recorder } +// PredictBlock mocks base method. +func (m *MockconservativeState) PredictBlock(arg0 types.LayerID, arg1 int) []types.TransactionID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PredictBlock", arg0, arg1) + ret0, _ := ret[0].([]types.TransactionID) + return ret0 +} + +// PredictBlock indicates an expected call of PredictBlock. +func (mr *MockconservativeStateMockRecorder) PredictBlock(arg0, arg1 any) *MockconservativeStatePredictBlockCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PredictBlock", reflect.TypeOf((*MockconservativeState)(nil).PredictBlock), arg0, arg1) + return &MockconservativeStatePredictBlockCall{Call: call} +} + +// MockconservativeStatePredictBlockCall wrap *gomock.Call +type MockconservativeStatePredictBlockCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockconservativeStatePredictBlockCall) Return(arg0 []types.TransactionID) *MockconservativeStatePredictBlockCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockconservativeStatePredictBlockCall) Do(f func(types.LayerID, int) []types.TransactionID) *MockconservativeStatePredictBlockCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockconservativeStatePredictBlockCall) DoAndReturn(f func(types.LayerID, int) []types.TransactionID) *MockconservativeStatePredictBlockCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // SelectProposalTXs mocks base method. func (m *MockconservativeState) SelectProposalTXs(arg0 types.LayerID, arg1 int) []types.TransactionID { m.ctrl.T.Helper() diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index 145355beef..34f37e50ef 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -632,9 +632,6 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, node } // don't accept registration in the middle of computing proposals - // pb.signers.mu.Lock() - // signers := maps.Values(pb.signers.signers) - // pb.signers.mu.Unlock() signer := &signerSession{} encodeVotesOnce := sync.OnceValues(func() (*types.Opinion, error) { pb.tortoise.TallyVotes(ctx, lid) From d80b5544cae3ecbb74c5425d2489a41f0a9f6a8f Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 07:52:22 -0600 Subject: [PATCH 04/71] wip --- api/node/client/client.go | 25 +++ api/node/node_service.yaml | 2 + api/node/server/mocks.go | 62 +++++++ api/node/server/server.gen.go | 8 + api/node/server/server.go | 41 +++++ hare3/eligibility/oracle_scale.go | 65 +------- hare3/types_scale.go | 171 +------------------ hare4/eligibility/oracle_scale.go | 65 +------- hare4/types_scale.go | 219 ++---------------------- miner/proposal_builder.go | 9 +- miner/remote_proposals.go | 266 ++++++++++++++++++++++++++++++ 11 files changed, 435 insertions(+), 498 deletions(-) create mode 100644 miner/remote_proposals.go diff --git a/api/node/client/client.go b/api/node/client/client.go index d2206ac45a..2a609f7b87 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -190,3 +190,28 @@ func (s *NodeService) Beacon(ctx context.Context, epoch types.EpochID) (types.Be copy(v[:], bytes) return v, nil } + +func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, error) { + resp, err := s.client.GetProposalLayerNode(ctx, externalRef0.LayerID(layer), node.String()) + if err != nil { + return nil, err + } + switch resp.StatusCode { + case http.StatusOK: + case http.StatusNoContent: + // special case - no error but also no proposal, means + // we're no eligibile this epoch with this node ID + return nil, nil + default: + 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) + } + + prop := types.Proposal{} + codec.MustDecode(bytes, &prop) + + return &prop, nil +} diff --git a/api/node/node_service.yaml b/api/node/node_service.yaml index f618c67cd2..2b1a0a3525 100644 --- a/api/node/node_service.yaml +++ b/api/node/node_service.yaml @@ -247,6 +247,8 @@ paths: schema: type: string format: binary + "204": + description: no eligibilities for this node in this epoch "500": description: could not generate proposal diff --git a/api/node/server/mocks.go b/api/node/server/mocks.go index d968050499..31852dc9ee 100644 --- a/api/node/server/mocks.go +++ b/api/node/server/mocks.go @@ -253,3 +253,65 @@ func (c *MockhareTotalWeightCall) DoAndReturn(f func(context.Context, types.Laye c.Call = c.Call.DoAndReturn(f) return c } + +// MockproposalBuilder is a mock of proposalBuilder interface. +type MockproposalBuilder struct { + ctrl *gomock.Controller + recorder *MockproposalBuilderMockRecorder +} + +// MockproposalBuilderMockRecorder is the mock recorder for MockproposalBuilder. +type MockproposalBuilderMockRecorder struct { + mock *MockproposalBuilder +} + +// NewMockproposalBuilder creates a new mock instance. +func NewMockproposalBuilder(ctrl *gomock.Controller) *MockproposalBuilder { + mock := &MockproposalBuilder{ctrl: ctrl} + mock.recorder = &MockproposalBuilderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockproposalBuilder) EXPECT() *MockproposalBuilderMockRecorder { + return m.recorder +} + +// BuildFor mocks base method. +func (m *MockproposalBuilder) BuildFor(layer types.LayerID, node types.NodeID) (*types.Proposal, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BuildFor", layer, node) + ret0, _ := ret[0].(*types.Proposal) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BuildFor indicates an expected call of BuildFor. +func (mr *MockproposalBuilderMockRecorder) BuildFor(layer, node any) *MockproposalBuilderBuildForCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildFor", reflect.TypeOf((*MockproposalBuilder)(nil).BuildFor), layer, node) + return &MockproposalBuilderBuildForCall{Call: call} +} + +// MockproposalBuilderBuildForCall wrap *gomock.Call +type MockproposalBuilderBuildForCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockproposalBuilderBuildForCall) Return(arg0 *types.Proposal, arg1 error) *MockproposalBuilderBuildForCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockproposalBuilderBuildForCall) Do(f func(types.LayerID, types.NodeID) (*types.Proposal, error)) *MockproposalBuilderBuildForCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockproposalBuilderBuildForCall) DoAndReturn(f func(types.LayerID, types.NodeID) (*types.Proposal, error)) *MockproposalBuilderBuildForCall { + 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 d48943aaa3..4fd4fc5df7 100644 --- a/api/node/server/server.gen.go +++ b/api/node/server/server.gen.go @@ -780,6 +780,14 @@ func (response GetProposalLayerNode200ApplicationoctetStreamResponse) VisitGetPr return err } +type GetProposalLayerNode204Response struct { +} + +func (response GetProposalLayerNode204Response) VisitGetProposalLayerNodeResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + type GetProposalLayerNode500Response struct { } diff --git a/api/node/server/server.go b/api/node/server/server.go index 3fc47b6704..d7a18746e4 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -35,11 +35,16 @@ type hare interface { Beacon(ctx context.Context, epoch types.EpochID) types.Beacon } +type proposalBuilder interface { + BuildFor(layer types.LayerID, node types.NodeID) (*types.Proposal, types.VRFPostIndex, error) +} + type Server struct { atxService activation.AtxService publisher pubsub.Publisher poetDB poetDB hare hare + proposals proposalBuilder logger *zap.Logger } @@ -50,6 +55,7 @@ func NewServer( publisher pubsub.Publisher, poetDB poetDB, hare hare, + proposals proposalBuilder, logger *zap.Logger, ) *Server { return &Server{ @@ -57,6 +63,7 @@ func NewServer( publisher: publisher, poetDB: poetDB, hare: hare, + proposals: proposals, logger: logger, } } @@ -284,3 +291,37 @@ func (s *Server) GetHareBeaconEpoch(ctx context.Context, request GetHareBeaconEp beacon := s.hare.Beacon(ctx, types.EpochID(request.Epoch)) return &beaconResp{b: beacon}, nil } + +type proposalResp struct { + buf []byte + nonce types.VRFPostIndex +} + +func (p *proposalResp) VisitGetProposalLayerNodeResponse(w http.ResponseWriter) error { + if buf == nil { + w.WriteHeader(204) + return nil + } + w.Header().Add("content-type", "application/octet-stream") + w.Header().Add("x-spacemesh-atx-nonce", fmt.Sprintf("%d", p.nonce)) + w.WriteHeader(200) + _, err := w.Write(p.buf) + return err +} + +func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLayerNodeRequestObject) (GetProposalLayerNodeResponseObject, error) { + hexBuf, err := hex.DecodeString(request.Node) + if err != nil { + return &proposalResp{}, err + } + id := types.BytesToNodeID(hexBuf) + + proposal, nonce, err := s.proposals.BuildFor(types.LayerID(request.Layer), id) + if err != nil { + return &proposalResp{}, err + } + if proposal.Ballot.EpochData.EligibilityCount == 0 { + return &proposalResp{}, nil + } + return &proposalResp{buf: codec.MustEncode(proposal), nonce: nonce}, err +} diff --git a/hare3/eligibility/oracle_scale.go b/hare3/eligibility/oracle_scale.go index 03f43bbfef..b4bc18113e 100644 --- a/hare3/eligibility/oracle_scale.go +++ b/hare3/eligibility/oracle_scale.go @@ -1,76 +1,15 @@ -// Code generated by github.com/spacemeshos/go-scale/scalegen. DO NOT EDIT. - -// nolint package eligibility import ( "github.com/spacemeshos/go-scale" - "github.com/spacemeshos/go-spacemesh/common/types" ) func (t *VrfMessage) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeCompact16(enc, uint16(t.Type)) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeByteArray(enc, t.Beacon[:]) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeCompact32(enc, uint32(t.Round)) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeCompact32(enc, uint32(t.Layer)) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *VrfMessage) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeCompact16(dec) - if err != nil { - return total, err - } - total += n - t.Type = types.EligibilityType(field) - } - { - n, err := scale.DecodeByteArray(dec, t.Beacon[:]) - if err != nil { - return total, err - } - total += n - } - { - field, n, err := scale.DecodeCompact32(dec) - if err != nil { - return total, err - } - total += n - t.Round = uint32(field) - } - { - field, n, err := scale.DecodeCompact32(dec) - if err != nil { - return total, err - } - total += n - t.Layer = types.LayerID(field) - } + return total, nil } diff --git a/hare3/types_scale.go b/hare3/types_scale.go index 28e8b64035..710c27e745 100644 --- a/hare3/types_scale.go +++ b/hare3/types_scale.go @@ -1,200 +1,45 @@ -// Code generated by github.com/spacemeshos/go-scale/scalegen. DO NOT EDIT. - -// nolint package hare3 import ( "github.com/spacemeshos/go-scale" - "github.com/spacemeshos/go-spacemesh/common/types" ) func (t *IterRound) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeCompact8(enc, uint8(t.Iter)) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeCompact8(enc, uint8(t.Round)) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *IterRound) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeCompact8(dec) - if err != nil { - return total, err - } - total += n - t.Iter = uint8(field) - } - { - field, n, err := scale.DecodeCompact8(dec) - if err != nil { - return total, err - } - total += n - t.Round = Round(field) - } + return total, nil } func (t *Value) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeStructSliceWithLimit(enc, t.Proposals, 2350) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeOption(enc, t.Reference) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *Value) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeStructSliceWithLimit[types.ProposalID](dec, 2350) - if err != nil { - return total, err - } - total += n - t.Proposals = field - } - { - field, n, err := scale.DecodeOption[types.Hash32](dec) - if err != nil { - return total, err - } - total += n - t.Reference = field - } + return total, nil } func (t *Body) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeCompact32(enc, uint32(t.Layer)) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.IterRound.EncodeScale(enc) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.Value.EncodeScale(enc) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.Eligibility.EncodeScale(enc) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *Body) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeCompact32(dec) - if err != nil { - return total, err - } - total += n - t.Layer = types.LayerID(field) - } - { - n, err := t.IterRound.DecodeScale(dec) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.Value.DecodeScale(dec) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.Eligibility.DecodeScale(dec) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *Message) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := t.Body.EncodeScale(enc) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeByteArray(enc, t.Sender[:]) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeByteArray(enc, t.Signature[:]) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *Message) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - n, err := t.Body.DecodeScale(dec) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.DecodeByteArray(dec, t.Sender[:]) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.DecodeByteArray(dec, t.Signature[:]) - if err != nil { - return total, err - } - total += n - } + return total, nil } diff --git a/hare4/eligibility/oracle_scale.go b/hare4/eligibility/oracle_scale.go index 03f43bbfef..b4bc18113e 100644 --- a/hare4/eligibility/oracle_scale.go +++ b/hare4/eligibility/oracle_scale.go @@ -1,76 +1,15 @@ -// Code generated by github.com/spacemeshos/go-scale/scalegen. DO NOT EDIT. - -// nolint package eligibility import ( "github.com/spacemeshos/go-scale" - "github.com/spacemeshos/go-spacemesh/common/types" ) func (t *VrfMessage) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeCompact16(enc, uint16(t.Type)) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeByteArray(enc, t.Beacon[:]) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeCompact32(enc, uint32(t.Round)) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeCompact32(enc, uint32(t.Layer)) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *VrfMessage) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeCompact16(dec) - if err != nil { - return total, err - } - total += n - t.Type = types.EligibilityType(field) - } - { - n, err := scale.DecodeByteArray(dec, t.Beacon[:]) - if err != nil { - return total, err - } - total += n - } - { - field, n, err := scale.DecodeCompact32(dec) - if err != nil { - return total, err - } - total += n - t.Round = uint32(field) - } - { - field, n, err := scale.DecodeCompact32(dec) - if err != nil { - return total, err - } - total += n - t.Layer = types.LayerID(field) - } + return total, nil } diff --git a/hare4/types_scale.go b/hare4/types_scale.go index 2f06ce6ff9..37fb3b6201 100644 --- a/hare4/types_scale.go +++ b/hare4/types_scale.go @@ -1,260 +1,65 @@ -// Code generated by github.com/spacemeshos/go-scale/scalegen. DO NOT EDIT. - -// nolint package hare4 import ( "github.com/spacemeshos/go-scale" - "github.com/spacemeshos/go-spacemesh/common/types" ) func (t *IterRound) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeCompact8(enc, uint8(t.Iter)) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeCompact8(enc, uint8(t.Round)) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *IterRound) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeCompact8(dec) - if err != nil { - return total, err - } - total += n - t.Iter = uint8(field) - } - { - field, n, err := scale.DecodeCompact8(dec) - if err != nil { - return total, err - } - total += n - t.Round = Round(field) - } + return total, nil } func (t *Value) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeStructSliceWithLimit(enc, t.Proposals, 2350) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeOption(enc, t.Reference) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeStructSliceWithLimit(enc, t.CompactProposals, 2350) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *Value) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeStructSliceWithLimit[types.ProposalID](dec, 2350) - if err != nil { - return total, err - } - total += n - t.Proposals = field - } - { - field, n, err := scale.DecodeOption[types.Hash32](dec) - if err != nil { - return total, err - } - total += n - t.Reference = field - } - { - field, n, err := scale.DecodeStructSliceWithLimit[types.CompactProposalID](dec, 2350) - if err != nil { - return total, err - } - total += n - t.CompactProposals = field - } + return total, nil } func (t *Body) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeCompact32(enc, uint32(t.Layer)) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.IterRound.EncodeScale(enc) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.Value.EncodeScale(enc) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.Eligibility.EncodeScale(enc) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *Body) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeCompact32(dec) - if err != nil { - return total, err - } - total += n - t.Layer = types.LayerID(field) - } - { - n, err := t.IterRound.DecodeScale(dec) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.Value.DecodeScale(dec) - if err != nil { - return total, err - } - total += n - } - { - n, err := t.Eligibility.DecodeScale(dec) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *Message) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := t.Body.EncodeScale(enc) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeByteArray(enc, t.Sender[:]) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.EncodeByteArray(enc, t.Signature[:]) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *Message) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - n, err := t.Body.DecodeScale(dec) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.DecodeByteArray(dec, t.Sender[:]) - if err != nil { - return total, err - } - total += n - } - { - n, err := scale.DecodeByteArray(dec, t.Signature[:]) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *CompactIdRequest) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeByteArray(enc, t.MsgId[:]) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *CompactIdRequest) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - n, err := scale.DecodeByteArray(dec, t.MsgId[:]) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *CompactIdResponse) EncodeScale(enc *scale.Encoder) (total int, err error) { - { - n, err := scale.EncodeStructSliceWithLimit(enc, t.Ids, 2050) - if err != nil { - return total, err - } - total += n - } + return total, nil } func (t *CompactIdResponse) DecodeScale(dec *scale.Decoder) (total int, err error) { - { - field, n, err := scale.DecodeStructSliceWithLimit[types.ProposalID](dec, 2050) - if err != nil { - return total, err - } - total += n - t.Ids = field - } + return total, nil } diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index 34f37e50ef..3f9921584b 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -962,13 +962,18 @@ func createPartialProposal( if session.ref == types.EmptyBallotID { p.Ballot.RefBallot = types.EmptyBallotID p.Ballot.EpochData = &types.EpochData{ - ActiveSetHash: activeset.Hash(), - Beacon: beacon, + ActiveSetHash: activeset.Hash(), + Beacon: beacon, + EligibilityCount: session.eligibilities.slots, } } else { p.Ballot.RefBallot = session.ref } p.SmesherID = smesher + // p.Ballot.Signature = signer.Sign(signing.BALLOT, p.Ballot.SignedBytes()) + // p.Signature = signer.Sign(signing.PROPOSAL, p.SignedBytes()) + // p.MustInitialize() + return p } diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go new file mode 100644 index 0000000000..33f641d5df --- /dev/null +++ b/miner/remote_proposals.go @@ -0,0 +1,266 @@ +package miner + +import ( + "context" + "fmt" + "runtime" + "sync" + + "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/log" + "github.com/spacemeshos/go-spacemesh/p2p/pubsub" + "github.com/spacemeshos/go-spacemesh/signing" + "go.uber.org/zap" + "golang.org/x/exp/maps" + "golang.org/x/sync/errgroup" +) + +// Opt for configuring ProposalBuilder. +// type Opt func(h *RemoteProposalBuilder) + +//// WithLayerSize defines the average number of proposal per layer. +//func WithLayerSize(size uint32) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.cfg.layerSize = size +//} +//} + +//// WithWorkersLimit configures paralelization factor for builder operation when working with +//// more than one signer. +//func WithWorkersLimit(limit int) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.cfg.workersLimit = limit +//} +//} + +//// WithLayerPerEpoch defines the number of layers per epoch. +//func WithLayerPerEpoch(layers uint32) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.cfg.layersPerEpoch = layers +//} +//} + +//func WithMinimalActiveSetWeight(weight []types.EpochMinimalActiveWeight) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.cfg.minActiveSetWeight = weight +//} +//} + +//// WithLogger defines the logger. +//func WithLogger(logger *zap.Logger) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.logger = logger +//} +//} + +//func WithHdist(dist uint32) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.cfg.hdist = dist +//} +//} + +//func WithNetworkDelay(delay time.Duration) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.cfg.networkDelay = delay +//} +//} + +//func WithMinGoodAtxPercent(percent int) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.cfg.goodAtxPercent = percent +//} +//} + +//// WithSigners guarantees that builder will start execution with provided list of signers. +//// Should be after logging. +//func WithSigners(signers ...*signing.EdSigner) Opt { +//return func(pb *RemoteProposalBuilder) { +//for _, signer := range signers { +//pb.Register(signer) +//} +//} +//} + +//// WithActivesetPreparation overwrites configuration for activeset preparation. +//func WithActivesetPreparation(prep ActiveSetPreparation) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.cfg.activeSet = prep +//} +//} + +//func withAtxSearch(p atxSearch) Opt { +//return func(pb *RemoteProposalBuilder) { +//pb.atxs = p +//} +//} + +type nodeService interface { + Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, error) +} +type RemoteProposalBuilder struct { + logger *zap.Logger + cfg config + + // db sql.Executor + // localdb sql.Executor + // atxsdata *atxsdata.Data + clock layerClock + publisher pubsub.Publisher + nodeSvc nodeService + // conState conservativeState + // tortoise votesEncoder + // syncer system.SyncStateProvider + // activeGen *activeSetGenerator + // atxs atxSearch + + signers struct { + mu sync.Mutex + signers map[types.NodeID]*signerSession + } + shared sharedSession +} + +// New creates a struct of block builder type. +func NewRemoteBuilder( + clock layerClock, + publisher pubsub.Publisher, + svc nodeService, +) *RemoteProposalBuilder { + pb := &RemoteProposalBuilder{ + cfg: config{ + workersLimit: runtime.NumCPU(), + activeSet: DefaultActiveSetPreparation(), + }, + logger: zap.NewNop(), + clock: clock, + publisher: publisher, + nodeSvc: svc, + signers: struct { + mu sync.Mutex + signers map[types.NodeID]*signerSession + }{ + signers: map[types.NodeID]*signerSession{}, + }, + } + return pb +} + +func (pb *RemoteProposalBuilder) Register(sig *signing.EdSigner) { + pb.signers.mu.Lock() + defer pb.signers.mu.Unlock() + _, exist := pb.signers.signers[sig.NodeID()] + if !exist { + pb.logger.Info("registered signing key", log.ZShortStringer("id", sig.NodeID())) + pb.signers.signers[sig.NodeID()] = &signerSession{ + signer: sig, + log: pb.logger.With(zap.String("signer", sig.NodeID().ShortString())), + } + } +} + +// Start the loop that listens to layers and build proposals. +func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { + current := pb.clock.CurrentLayer() + next := current + 1 + pb.logger.Info("started", zap.Inline(&pb.cfg), zap.Uint32("next", next.Uint32())) + var eg errgroup.Group + prepareDisabled := pb.cfg.activeSet.Tries == 0 || pb.cfg.activeSet.RetryInterval == 0 + if prepareDisabled { + pb.logger.Warn("activeset will not be prepared in advance") + } + for { + select { + case <-ctx.Done(): + eg.Wait() + return nil + case <-pb.clock.AwaitLayer(next): + current := pb.clock.CurrentLayer() + if current.Before(next) { + pb.logger.Info("time sync detected, realigning ProposalBuilder", + zap.Uint32("current", current.Uint32()), + zap.Uint32("next", next.Uint32()), + ) + continue + } + next = current.Add(1) + ctx := log.WithNewSessionID(ctx) + + if current <= types.GetEffectiveGenesis() { + continue + } + + if err := pb.build(ctx, current); err != nil { + pb.logger.Warn("failed to build proposal", + log.ZContext(ctx), + zap.Uint32("lid", current.Uint32()), + zap.Error(err), + ) + } + } + } +} + +/* + p := &types.Proposal{ + InnerProposal: types.InnerProposal{ + Ballot: types.Ballot{ + InnerBallot: types.InnerBallot{ + Layer: lid, + AtxID: session.atx, + OpinionHash: opinion.Hash, + }, + Votes: opinion.Votes, + // EligibilityProofs: eligibility, + }, + TxIDs: txs, + MeshHash: meshHash, + }, + } + + if session.ref == types.EmptyBallotID { + p.Ballot.RefBallot = types.EmptyBallotID + p.Ballot.EpochData = &types.EpochData{ + ActiveSetHash: activeset.Hash(), + Beacon: beacon, + EligibilityCount: session.eligibilities.slots, + } + } else { + + p.Ballot.RefBallot = session.ref + } + +p.SmesherID = smesher +// p.Ballot.Signature = signer.Sign(signing.BALLOT, p.Ballot.SignedBytes()) +// p.Signature = signer.Sign(signing.PROPOSAL, p.SignedBytes()) +// p.MustInitialize() + +return p +*/ +func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) error { + pb.signers.mu.Lock() + signers := maps.Values(pb.signers.signers) + pb.signers.mu.Unlock() + + for _, signer := range signers { + proposal, err := pb.nodeSvc.Proposal(ctx, layer, signer.signer.NodeID()) + if err != nil { + return fmt.Errorf("get partial proposal: %w", err) + } + + if proposal == nil { + // this node signer isn't eligible this epoch, continue + continue + } + + proofs := calcEligibilityProofs( + signer.signer.VRFSigner(), + layer.GetEpoch(), + proposal.Ballot.EpochData.Beacon, + ss.session.nonce, // atx nonce + proposal.Ballot.EpochData.EligibilityCount, + pb.cfg.layersPerEpoch, + ) + + } + return nil +} From db8920ab8deb97e3e1dc56353e63fd128581e0bc Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 07:57:31 -0600 Subject: [PATCH 05/71] makegen --- api/node/server/mocks.go | 15 +- hare3/eligibility/oracle_scale.go | 65 ++++++++- hare3/types_scale.go | 171 +++++++++++++++++++++-- hare4/eligibility/oracle_scale.go | 65 ++++++++- hare4/types_scale.go | 219 ++++++++++++++++++++++++++++-- miner/remote_proposals.go | 4 +- 6 files changed, 507 insertions(+), 32 deletions(-) diff --git a/api/node/server/mocks.go b/api/node/server/mocks.go index 31852dc9ee..352731f7e8 100644 --- a/api/node/server/mocks.go +++ b/api/node/server/mocks.go @@ -278,12 +278,13 @@ func (m *MockproposalBuilder) EXPECT() *MockproposalBuilderMockRecorder { } // BuildFor mocks base method. -func (m *MockproposalBuilder) BuildFor(layer types.LayerID, node types.NodeID) (*types.Proposal, error) { +func (m *MockproposalBuilder) BuildFor(layer types.LayerID, node types.NodeID) (*types.Proposal, types.VRFPostIndex, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BuildFor", layer, node) ret0, _ := ret[0].(*types.Proposal) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret1, _ := ret[1].(types.VRFPostIndex) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } // BuildFor indicates an expected call of BuildFor. @@ -299,19 +300,19 @@ type MockproposalBuilderBuildForCall struct { } // Return rewrite *gomock.Call.Return -func (c *MockproposalBuilderBuildForCall) Return(arg0 *types.Proposal, arg1 error) *MockproposalBuilderBuildForCall { - c.Call = c.Call.Return(arg0, arg1) +func (c *MockproposalBuilderBuildForCall) Return(arg0 *types.Proposal, arg1 types.VRFPostIndex, arg2 error) *MockproposalBuilderBuildForCall { + c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do -func (c *MockproposalBuilderBuildForCall) Do(f func(types.LayerID, types.NodeID) (*types.Proposal, error)) *MockproposalBuilderBuildForCall { +func (c *MockproposalBuilderBuildForCall) Do(f func(types.LayerID, types.NodeID) (*types.Proposal, types.VRFPostIndex, error)) *MockproposalBuilderBuildForCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockproposalBuilderBuildForCall) DoAndReturn(f func(types.LayerID, types.NodeID) (*types.Proposal, error)) *MockproposalBuilderBuildForCall { +func (c *MockproposalBuilderBuildForCall) DoAndReturn(f func(types.LayerID, types.NodeID) (*types.Proposal, types.VRFPostIndex, error)) *MockproposalBuilderBuildForCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/hare3/eligibility/oracle_scale.go b/hare3/eligibility/oracle_scale.go index b4bc18113e..03f43bbfef 100644 --- a/hare3/eligibility/oracle_scale.go +++ b/hare3/eligibility/oracle_scale.go @@ -1,15 +1,76 @@ +// Code generated by github.com/spacemeshos/go-scale/scalegen. DO NOT EDIT. + +// nolint package eligibility import ( "github.com/spacemeshos/go-scale" + "github.com/spacemeshos/go-spacemesh/common/types" ) func (t *VrfMessage) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeCompact16(enc, uint16(t.Type)) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeByteArray(enc, t.Beacon[:]) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeCompact32(enc, uint32(t.Round)) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeCompact32(enc, uint32(t.Layer)) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *VrfMessage) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeCompact16(dec) + if err != nil { + return total, err + } + total += n + t.Type = types.EligibilityType(field) + } + { + n, err := scale.DecodeByteArray(dec, t.Beacon[:]) + if err != nil { + return total, err + } + total += n + } + { + field, n, err := scale.DecodeCompact32(dec) + if err != nil { + return total, err + } + total += n + t.Round = uint32(field) + } + { + field, n, err := scale.DecodeCompact32(dec) + if err != nil { + return total, err + } + total += n + t.Layer = types.LayerID(field) + } return total, nil } diff --git a/hare3/types_scale.go b/hare3/types_scale.go index 710c27e745..28e8b64035 100644 --- a/hare3/types_scale.go +++ b/hare3/types_scale.go @@ -1,45 +1,200 @@ +// Code generated by github.com/spacemeshos/go-scale/scalegen. DO NOT EDIT. + +// nolint package hare3 import ( "github.com/spacemeshos/go-scale" + "github.com/spacemeshos/go-spacemesh/common/types" ) func (t *IterRound) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeCompact8(enc, uint8(t.Iter)) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeCompact8(enc, uint8(t.Round)) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *IterRound) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeCompact8(dec) + if err != nil { + return total, err + } + total += n + t.Iter = uint8(field) + } + { + field, n, err := scale.DecodeCompact8(dec) + if err != nil { + return total, err + } + total += n + t.Round = Round(field) + } return total, nil } func (t *Value) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeStructSliceWithLimit(enc, t.Proposals, 2350) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeOption(enc, t.Reference) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *Value) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeStructSliceWithLimit[types.ProposalID](dec, 2350) + if err != nil { + return total, err + } + total += n + t.Proposals = field + } + { + field, n, err := scale.DecodeOption[types.Hash32](dec) + if err != nil { + return total, err + } + total += n + t.Reference = field + } return total, nil } func (t *Body) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeCompact32(enc, uint32(t.Layer)) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.IterRound.EncodeScale(enc) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.Value.EncodeScale(enc) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.Eligibility.EncodeScale(enc) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *Body) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeCompact32(dec) + if err != nil { + return total, err + } + total += n + t.Layer = types.LayerID(field) + } + { + n, err := t.IterRound.DecodeScale(dec) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.Value.DecodeScale(dec) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.Eligibility.DecodeScale(dec) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *Message) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := t.Body.EncodeScale(enc) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeByteArray(enc, t.Sender[:]) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeByteArray(enc, t.Signature[:]) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *Message) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + n, err := t.Body.DecodeScale(dec) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.DecodeByteArray(dec, t.Sender[:]) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.DecodeByteArray(dec, t.Signature[:]) + if err != nil { + return total, err + } + total += n + } return total, nil } diff --git a/hare4/eligibility/oracle_scale.go b/hare4/eligibility/oracle_scale.go index b4bc18113e..03f43bbfef 100644 --- a/hare4/eligibility/oracle_scale.go +++ b/hare4/eligibility/oracle_scale.go @@ -1,15 +1,76 @@ +// Code generated by github.com/spacemeshos/go-scale/scalegen. DO NOT EDIT. + +// nolint package eligibility import ( "github.com/spacemeshos/go-scale" + "github.com/spacemeshos/go-spacemesh/common/types" ) func (t *VrfMessage) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeCompact16(enc, uint16(t.Type)) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeByteArray(enc, t.Beacon[:]) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeCompact32(enc, uint32(t.Round)) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeCompact32(enc, uint32(t.Layer)) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *VrfMessage) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeCompact16(dec) + if err != nil { + return total, err + } + total += n + t.Type = types.EligibilityType(field) + } + { + n, err := scale.DecodeByteArray(dec, t.Beacon[:]) + if err != nil { + return total, err + } + total += n + } + { + field, n, err := scale.DecodeCompact32(dec) + if err != nil { + return total, err + } + total += n + t.Round = uint32(field) + } + { + field, n, err := scale.DecodeCompact32(dec) + if err != nil { + return total, err + } + total += n + t.Layer = types.LayerID(field) + } return total, nil } diff --git a/hare4/types_scale.go b/hare4/types_scale.go index 37fb3b6201..2f06ce6ff9 100644 --- a/hare4/types_scale.go +++ b/hare4/types_scale.go @@ -1,65 +1,260 @@ +// Code generated by github.com/spacemeshos/go-scale/scalegen. DO NOT EDIT. + +// nolint package hare4 import ( "github.com/spacemeshos/go-scale" + "github.com/spacemeshos/go-spacemesh/common/types" ) func (t *IterRound) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeCompact8(enc, uint8(t.Iter)) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeCompact8(enc, uint8(t.Round)) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *IterRound) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeCompact8(dec) + if err != nil { + return total, err + } + total += n + t.Iter = uint8(field) + } + { + field, n, err := scale.DecodeCompact8(dec) + if err != nil { + return total, err + } + total += n + t.Round = Round(field) + } return total, nil } func (t *Value) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeStructSliceWithLimit(enc, t.Proposals, 2350) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeOption(enc, t.Reference) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeStructSliceWithLimit(enc, t.CompactProposals, 2350) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *Value) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeStructSliceWithLimit[types.ProposalID](dec, 2350) + if err != nil { + return total, err + } + total += n + t.Proposals = field + } + { + field, n, err := scale.DecodeOption[types.Hash32](dec) + if err != nil { + return total, err + } + total += n + t.Reference = field + } + { + field, n, err := scale.DecodeStructSliceWithLimit[types.CompactProposalID](dec, 2350) + if err != nil { + return total, err + } + total += n + t.CompactProposals = field + } return total, nil } func (t *Body) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeCompact32(enc, uint32(t.Layer)) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.IterRound.EncodeScale(enc) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.Value.EncodeScale(enc) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.Eligibility.EncodeScale(enc) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *Body) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeCompact32(dec) + if err != nil { + return total, err + } + total += n + t.Layer = types.LayerID(field) + } + { + n, err := t.IterRound.DecodeScale(dec) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.Value.DecodeScale(dec) + if err != nil { + return total, err + } + total += n + } + { + n, err := t.Eligibility.DecodeScale(dec) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *Message) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := t.Body.EncodeScale(enc) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeByteArray(enc, t.Sender[:]) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.EncodeByteArray(enc, t.Signature[:]) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *Message) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + n, err := t.Body.DecodeScale(dec) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.DecodeByteArray(dec, t.Sender[:]) + if err != nil { + return total, err + } + total += n + } + { + n, err := scale.DecodeByteArray(dec, t.Signature[:]) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *CompactIdRequest) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeByteArray(enc, t.MsgId[:]) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *CompactIdRequest) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + n, err := scale.DecodeByteArray(dec, t.MsgId[:]) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *CompactIdResponse) EncodeScale(enc *scale.Encoder) (total int, err error) { - + { + n, err := scale.EncodeStructSliceWithLimit(enc, t.Ids, 2050) + if err != nil { + return total, err + } + total += n + } return total, nil } func (t *CompactIdResponse) DecodeScale(dec *scale.Decoder) (total int, err error) { - + { + field, n, err := scale.DecodeStructSliceWithLimit[types.ProposalID](dec, 2050) + if err != nil { + return total, err + } + total += n + t.Ids = field + } return total, nil } diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 33f641d5df..28522901de 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -256,11 +256,13 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) signer.signer.VRFSigner(), layer.GetEpoch(), proposal.Ballot.EpochData.Beacon, - ss.session.nonce, // atx nonce + 111, // ss.session.nonce, // atx nonce proposal.Ballot.EpochData.EligibilityCount, pb.cfg.layersPerEpoch, ) + fmt.Println(proofs) + } return nil } From 315e4982b0cde6be8a0467d3e72f6943b06c4316 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 08:21:52 -0600 Subject: [PATCH 06/71] wip --- api/node/client/client.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/api/node/client/client.go b/api/node/client/client.go index 2a609f7b87..0c939983c9 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" @@ -191,27 +192,35 @@ func (s *NodeService) Beacon(ctx context.Context, epoch types.EpochID) (types.Be return v, nil } -func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, error) { +func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, uint64, error) { resp, err := s.client.GetProposalLayerNode(ctx, externalRef0.LayerID(layer), node.String()) if err != nil { - return nil, err + return nil, 0, err } switch resp.StatusCode { case http.StatusOK: case http.StatusNoContent: // special case - no error but also no proposal, means // we're no eligibile this epoch with this node ID - return nil, nil + return nil, 0, nil default: - return nil, fmt.Errorf("unexpected status: %s", resp.Status) + return nil, 0, 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 nil, 0, fmt.Errorf("read all: %w", err) } prop := types.Proposal{} codec.MustDecode(bytes, &prop) - return &prop, nil + atxNonce := resp.Header.Get("x-spacemesh-atx-nonce") + if atxNonce == "" { + return nil, 0, errors.New("atx nonce header not found") + } + nonce, err := strconv.ParseUint(atxNonce, 10, 64) + if err != nil { + return nil, 0, fmt.Errorf("nonce parse: %w", err) + } + return &prop, nonce, nil } From cedb73b05a27c01e36c415e817ccac3737713a79 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:00:50 -0600 Subject: [PATCH 07/71] wip --- api/node/server/server.go | 6 +- miner/proposal_builder.go | 8 +- miner/remote_proposals.go | 6 +- node/node.go | 158 +++++++++++++++++++++----------------- 4 files changed, 98 insertions(+), 80 deletions(-) diff --git a/api/node/server/server.go b/api/node/server/server.go index d7a18746e4..2ee6cbcfa8 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -36,7 +36,7 @@ type hare interface { } type proposalBuilder interface { - BuildFor(layer types.LayerID, node types.NodeID) (*types.Proposal, types.VRFPostIndex, error) + BuildFor(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, types.VRFPostIndex, error) } type Server struct { @@ -298,7 +298,7 @@ type proposalResp struct { } func (p *proposalResp) VisitGetProposalLayerNodeResponse(w http.ResponseWriter) error { - if buf == nil { + if p.buf == nil { w.WriteHeader(204) return nil } @@ -316,7 +316,7 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa } id := types.BytesToNodeID(hexBuf) - proposal, nonce, err := s.proposals.BuildFor(types.LayerID(request.Layer), id) + proposal, nonce, err := s.proposals.BuildFor(ctx, types.LayerID(request.Layer), id) if err != nil { return &proposalResp{}, err } diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index 3f9921584b..31d564d331 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -626,9 +626,9 @@ func (pb *ProposalBuilder) initSignerDataFor(ctx context.Context, ss *signerSess return nil } -func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, nodeID types.NodeID) (*types.Proposal, error) { +func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, nodeID types.NodeID) (*types.Proposal, types.VRFPostIndex, error) { if err := pb.initSharedData(lid); err != nil { - return nil, err + return nil, 0, err } // don't accept registration in the middle of computing proposals @@ -722,7 +722,7 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, node for ss := range eligible { opinion, err := encodeVotesOnce() if err != nil { - return nil, err + return nil, 0, err } meshHash := calcMeshHashOnce() @@ -757,7 +757,7 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, node }) } err := errors.Join(stage1Err, eg2.Wait()) - return prop, err + return prop, 0, err } func (pb *ProposalBuilder) build(ctx context.Context, lid types.LayerID) error { diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 28522901de..0400e53715 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -95,7 +95,7 @@ import ( //} type nodeService interface { - Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, error) + Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, uint64, error) } type RemoteProposalBuilder struct { logger *zap.Logger @@ -242,7 +242,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) pb.signers.mu.Unlock() for _, signer := range signers { - proposal, err := pb.nodeSvc.Proposal(ctx, layer, signer.signer.NodeID()) + proposal, nonce, err := pb.nodeSvc.Proposal(ctx, layer, signer.signer.NodeID()) if err != nil { return fmt.Errorf("get partial proposal: %w", err) } @@ -256,7 +256,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) signer.signer.VRFSigner(), layer.GetEpoch(), proposal.Ballot.EpochData.Beacon, - 111, // ss.session.nonce, // atx nonce + types.VRFPostIndex(nonce), // ss.session.nonce, // atx nonce proposal.Ballot.EpochData.EligibilityCount, pb.cfg.layersPerEpoch, ) diff --git a/node/node.go b/node/node.go index 0d01697363..a931e33236 100644 --- a/node/node.go +++ b/node/node.go @@ -373,54 +373,55 @@ func New(opts ...Option) *App { // App is the cli app singleton. type App struct { *cobra.Command - fileLock *flock.Flock - signers []*signing.EdSigner - Config *config.Config - db sql.StateDatabase - cachedDB *datastore.CachedDB - dbMetrics *dbmetrics.DBMetricsCollector - localDB sql.LocalDatabase - grpcPublicServer *grpcserver.Server - grpcPrivateServer *grpcserver.Server - grpcPostServer *grpcserver.Server - grpcTLSServer *grpcserver.Server - jsonAPIServer *grpcserver.JSONHTTPServer - nodeServiceServer *http.Server - grpcServices map[grpcserver.Service]grpcserver.ServiceAPI - pprofService *http.Server - profilerService *pyroscope.Profiler - syncer *syncer.Syncer - proposalListener *proposals.Handler - proposalBuilder *miner.ProposalBuilder - mesh *mesh.Mesh - atxsdata *atxsdata.Data - clock *timesync.NodeClock - hare3 *hare3.Hare - hare4 *hare4.Hare - remoteHare *hare3.RemoteHare - hareResultsChan chan hare4.ConsensusOutput - hOracle *eligibility.Oracle - blockGen *blocks.Generator - certifier *blocks.Certifier - atxBuilder *activation.Builder - nipostBuilder *activation.NIPostBuilder - atxHandler *activation.Handler - txHandler *txs.TxHandler - validator *activation.Validator - edVerifier *signing.EdVerifier - beaconProtocol *beacon.ProtocolDriver - log log.Log - syncLogger log.Log - svm *vm.VM - conState *txs.ConservativeState - fetcher *fetch.Fetch - ptimesync *peersync.Sync - tortoise *tortoise.Tortoise - updater *bootstrap.Updater - poetDb *activation.PoetDb - postVerifier activation.PostVerifier - postSupervisor *activation.PostSupervisor - errCh chan error + fileLock *flock.Flock + signers []*signing.EdSigner + Config *config.Config + db sql.StateDatabase + cachedDB *datastore.CachedDB + dbMetrics *dbmetrics.DBMetricsCollector + localDB sql.LocalDatabase + grpcPublicServer *grpcserver.Server + grpcPrivateServer *grpcserver.Server + grpcPostServer *grpcserver.Server + grpcTLSServer *grpcserver.Server + jsonAPIServer *grpcserver.JSONHTTPServer + nodeServiceServer *http.Server + grpcServices map[grpcserver.Service]grpcserver.ServiceAPI + pprofService *http.Server + profilerService *pyroscope.Profiler + syncer *syncer.Syncer + proposalListener *proposals.Handler + proposalBuilder *miner.ProposalBuilder + remoteProposalBuilder *miner.RemoteProposalBuilder + mesh *mesh.Mesh + atxsdata *atxsdata.Data + clock *timesync.NodeClock + hare3 *hare3.Hare + hare4 *hare4.Hare + remoteHare *hare3.RemoteHare + hareResultsChan chan hare4.ConsensusOutput + hOracle *eligibility.Oracle + blockGen *blocks.Generator + certifier *blocks.Certifier + atxBuilder *activation.Builder + nipostBuilder *activation.NIPostBuilder + atxHandler *activation.Handler + txHandler *txs.TxHandler + validator *activation.Validator + edVerifier *signing.EdVerifier + beaconProtocol *beacon.ProtocolDriver + log log.Log + syncLogger log.Log + svm *vm.VM + conState *txs.ConservativeState + fetcher *fetch.Fetch + ptimesync *peersync.Sync + tortoise *tortoise.Tortoise + updater *bootstrap.Updater + poetDb *activation.PoetDb + postVerifier activation.PostVerifier + postSupervisor *activation.PostSupervisor + errCh chan error host *p2p.Host @@ -1029,26 +1030,34 @@ func (app *App) initServices(ctx context.Context) error { if app.Config.MinerGoodAtxsPercent > 0 { minerGoodAtxPct = app.Config.MinerGoodAtxsPercent } - proposalBuilder := miner.New( - app.clock, - app.db, - app.localDB, - app.atxsdata, - app.host, - trtl, - newSyncer, - app.conState, - miner.WithLayerSize(layerSize), - miner.WithLayerPerEpoch(layersPerEpoch), - miner.WithMinimalActiveSetWeight(app.Config.Tortoise.MinimalActiveSetWeight), - miner.WithHdist(app.Config.Tortoise.Hdist), - miner.WithNetworkDelay(app.Config.ATXGradeDelay), - miner.WithMinGoodAtxPercent(minerGoodAtxPct), - miner.WithLogger(app.addLogger(ProposalBuilderLogger, lg).Zap()), - miner.WithActivesetPreparation(app.Config.ActiveSet), - ) - for _, sig := range app.signers { - proposalBuilder.Register(sig) + var proposalBuilder *miner.ProposalBuilder + var remoteProposalBuilder *miner.RemoteProposalBuilder + if nodeServiceClient != nil { + remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, nodeServiceClient, nodeServiceClient) + app.remoteProposalBuilder = remoteProposalBuilder + } else { + proposalBuilder = miner.New( + app.clock, + app.db, + app.localDB, + app.atxsdata, + app.host, + trtl, + newSyncer, + app.conState, + miner.WithLayerSize(layerSize), + miner.WithLayerPerEpoch(layersPerEpoch), + miner.WithMinimalActiveSetWeight(app.Config.Tortoise.MinimalActiveSetWeight), + miner.WithHdist(app.Config.Tortoise.Hdist), + miner.WithNetworkDelay(app.Config.ATXGradeDelay), + miner.WithMinGoodAtxPercent(minerGoodAtxPct), + miner.WithLogger(app.addLogger(ProposalBuilderLogger, lg).Zap()), + miner.WithActivesetPreparation(app.Config.ActiveSet), + ) + for _, sig := range app.signers { + proposalBuilder.Register(sig) + } + app.proposalBuilder = proposalBuilder } postSetupMgr, err := activation.NewPostSetupManager( @@ -1487,7 +1496,16 @@ func (app *App) startServices(ctx context.Context) error { app.blockGen.Start(ctx) app.certifier.Start(ctx) app.eg.Go(func() error { - return app.proposalBuilder.Run(ctx) + if app.proposalBuilder != nil { + return app.proposalBuilder.Run(ctx) + } + return nil + }) + app.eg.Go(func() error { + if app.remoteProposalBuilder != nil { + return app.remoteProposalBuilder.Run(ctx) + } + return nil }) if app.Config.SMESHING.CoinbaseAccount != "" { @@ -1896,7 +1914,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, app.hare3, logger) + server := nodeserver.NewServer(actSvc, app.host, app.poetDb, app.hare3, app.proposalBuilder, logger) app.nodeServiceServer = &http.Server{ Handler: server.IntoHandler(http.NewServeMux()), From add533a030ce5f05eea8c5054064210c72f55ccd Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:57:45 -0600 Subject: [PATCH 08/71] wip --- api/node/client/client.go | 2 ++ miner/remote_proposals.go | 57 +++++++++++++-------------------------- node/node.go | 1 + 3 files changed, 22 insertions(+), 38 deletions(-) diff --git a/api/node/client/client.go b/api/node/client/client.go index 0c939983c9..356f4d3bd1 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -216,10 +216,12 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty atxNonce := resp.Header.Get("x-spacemesh-atx-nonce") if atxNonce == "" { + panic("no nonce") return nil, 0, errors.New("atx nonce header not found") } nonce, err := strconv.ParseUint(atxNonce, 10, 64) if err != nil { + panic("bad atx integer parse") return nil, 0, fmt.Errorf("nonce parse: %w", err) } return &prop, nonce, nil diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 0400e53715..e69e261192 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -6,6 +6,7 @@ import ( "runtime" "sync" + "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/log" "github.com/spacemeshos/go-spacemesh/p2p/pubsub" @@ -200,42 +201,6 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { } } -/* - p := &types.Proposal{ - InnerProposal: types.InnerProposal{ - Ballot: types.Ballot{ - InnerBallot: types.InnerBallot{ - Layer: lid, - AtxID: session.atx, - OpinionHash: opinion.Hash, - }, - Votes: opinion.Votes, - // EligibilityProofs: eligibility, - }, - TxIDs: txs, - MeshHash: meshHash, - }, - } - - if session.ref == types.EmptyBallotID { - p.Ballot.RefBallot = types.EmptyBallotID - p.Ballot.EpochData = &types.EpochData{ - ActiveSetHash: activeset.Hash(), - Beacon: beacon, - EligibilityCount: session.eligibilities.slots, - } - } else { - - p.Ballot.RefBallot = session.ref - } - -p.SmesherID = smesher -// p.Ballot.Signature = signer.Sign(signing.BALLOT, p.Ballot.SignedBytes()) -// p.Signature = signer.Sign(signing.PROPOSAL, p.SignedBytes()) -// p.MustInitialize() - -return p -*/ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) error { pb.signers.mu.Lock() signers := maps.Values(pb.signers.signers) @@ -256,13 +221,29 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) signer.signer.VRFSigner(), layer.GetEpoch(), proposal.Ballot.EpochData.Beacon, - types.VRFPostIndex(nonce), // ss.session.nonce, // atx nonce + types.VRFPostIndex(nonce), proposal.Ballot.EpochData.EligibilityCount, pb.cfg.layersPerEpoch, ) - fmt.Println(proofs) + eligibilities, ok := proofs[layer] + if !ok { + // not eligible in this layer, continue + continue + } + proposal.EligibilityProofs = eligibilities + proposal.Ballot.Signature = signer.signer.Sign(signing.BALLOT, proposal.Ballot.SignedBytes()) + proposal.Signature = signer.signer.Sign(signing.PROPOSAL, proposal.SignedBytes()) + proposal.MustInitialize() + if err := pb.publisher.Publish(ctx, pubsub.ProposalProtocol, codec.MustEncode(proposal)); err != nil { + pb.logger.Error("failed to publish proposal", + log.ZContext(ctx), + zap.Uint32("lid", proposal.Layer.Uint32()), + zap.Stringer("id", proposal.ID()), + zap.Error(err), + ) + } } return nil } diff --git a/node/node.go b/node/node.go index a931e33236..6857bf336a 100644 --- a/node/node.go +++ b/node/node.go @@ -1503,6 +1503,7 @@ func (app *App) startServices(ctx context.Context) error { }) app.eg.Go(func() error { if app.remoteProposalBuilder != nil { + fmt.Println("starting remote proposal building") return app.remoteProposalBuilder.Run(ctx) } return nil From d9d55574bd6bbadff38e54f5d84fca4693416e2d Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:32:16 -0600 Subject: [PATCH 09/71] wip --- miner/remote_proposals.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index e69e261192..b58656274f 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -189,7 +189,7 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { if current <= types.GetEffectiveGenesis() { continue } - + fmt.Println("remote proposal builder going to build. layer", current.Uint32()) if err := pb.build(ctx, current); err != nil { pb.logger.Warn("failed to build proposal", log.ZContext(ctx), @@ -205,15 +205,16 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) pb.signers.mu.Lock() signers := maps.Values(pb.signers.signers) pb.signers.mu.Unlock() - + fmt.Println("remote proposal builder got some signers", signers) for _, signer := range signers { proposal, nonce, err := pb.nodeSvc.Proposal(ctx, layer, signer.signer.NodeID()) if err != nil { - return fmt.Errorf("get partial proposal: %w", err) + pb.logger.Error("get partial proposal", zap.Error(err)) } if proposal == nil { // this node signer isn't eligible this epoch, continue + pb.logger.Info("node not eligible on this layer. will try next") continue } @@ -229,6 +230,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) eligibilities, ok := proofs[layer] if !ok { // not eligible in this layer, continue + pb.logger.Info("node not eligible in this layer, will try later") continue } @@ -236,6 +238,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) proposal.Ballot.Signature = signer.signer.Sign(signing.BALLOT, proposal.Ballot.SignedBytes()) proposal.Signature = signer.signer.Sign(signing.PROPOSAL, proposal.SignedBytes()) proposal.MustInitialize() + pb.logger.Info("did all the proposal stuff nicely, publishing", zap.Inline(proposal)) if err := pb.publisher.Publish(ctx, pubsub.ProposalProtocol, codec.MustEncode(proposal)); err != nil { pb.logger.Error("failed to publish proposal", log.ZContext(ctx), @@ -244,6 +247,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) zap.Error(err), ) } + pb.logger.Info("proposal published successfully") } return nil } From fb1da1df891b1418e0bde0db186c3c6759b6c575 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:41:17 -0600 Subject: [PATCH 10/71] signers --- node/node.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/node/node.go b/node/node.go index 6857bf336a..2d9e04fc8f 100644 --- a/node/node.go +++ b/node/node.go @@ -1034,6 +1034,10 @@ func (app *App) initServices(ctx context.Context) error { var remoteProposalBuilder *miner.RemoteProposalBuilder if nodeServiceClient != nil { remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, nodeServiceClient, nodeServiceClient) + for _, sig := range app.signers { + remoteProposalBuilder.Register(sig) + } + app.remoteProposalBuilder = remoteProposalBuilder } else { proposalBuilder = miner.New( From 31f2831f51066211ede8edd4ae9e1bfe691e810d Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:49:56 -0600 Subject: [PATCH 11/71] wip --- miner/remote_proposals.go | 8 ++++++-- node/node.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index b58656274f..6121db6b8a 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -126,11 +126,15 @@ func NewRemoteBuilder( clock layerClock, publisher pubsub.Publisher, svc nodeService, + layerSize uint32, + layersPerEpoch uint32, ) *RemoteProposalBuilder { pb := &RemoteProposalBuilder{ cfg: config{ - workersLimit: runtime.NumCPU(), - activeSet: DefaultActiveSetPreparation(), + workersLimit: runtime.NumCPU(), + activeSet: DefaultActiveSetPreparation(), + layerSize: layerSize, + layersPerEpoch: layersPerEpoch, }, logger: zap.NewNop(), clock: clock, diff --git a/node/node.go b/node/node.go index 2d9e04fc8f..7dbe79b036 100644 --- a/node/node.go +++ b/node/node.go @@ -1033,7 +1033,7 @@ func (app *App) initServices(ctx context.Context) error { var proposalBuilder *miner.ProposalBuilder var remoteProposalBuilder *miner.RemoteProposalBuilder if nodeServiceClient != nil { - remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, nodeServiceClient, nodeServiceClient) + remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, nodeServiceClient, nodeServiceClient, layerSize, layersPerEpoch) for _, sig := range app.signers { remoteProposalBuilder.Register(sig) } From 23c03795b5433520da7c5579258508ce9c9fa80c Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:10:50 -0600 Subject: [PATCH 12/71] prints --- api/node/client/client.go | 8 +++++++- api/node/server/server.go | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/api/node/client/client.go b/api/node/client/client.go index 356f4d3bd1..8540655b06 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -199,21 +199,26 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty } switch resp.StatusCode { case http.StatusOK: + fmt.Println("client got OK for proposal") case http.StatusNoContent: // special case - no error but also no proposal, means // we're no eligibile this epoch with this node ID + fmt.Println("client not eligible for proposal") return nil, 0, nil default: + fmt.Println("client got unexpected status code") return nil, 0, fmt.Errorf("unexpected status: %s", resp.Status) } + bytes, err := io.ReadAll(resp.Body) if err != nil { return nil, 0, fmt.Errorf("read all: %w", err) } + fmt.Println("client going for decode") prop := types.Proposal{} codec.MustDecode(bytes, &prop) - + fmt.Println("client decoded proposal") atxNonce := resp.Header.Get("x-spacemesh-atx-nonce") if atxNonce == "" { panic("no nonce") @@ -224,5 +229,6 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty panic("bad atx integer parse") return nil, 0, fmt.Errorf("nonce parse: %w", err) } + fmt.Println("client returning proposal with nonce", prop, nonce) return &prop, nonce, nil } diff --git a/api/node/server/server.go b/api/node/server/server.go index 2ee6cbcfa8..94350e1cad 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -299,6 +299,7 @@ type proposalResp struct { func (p *proposalResp) VisitGetProposalLayerNodeResponse(w http.ResponseWriter) error { if p.buf == nil { + fmt.Println("writing no content to requester") w.WriteHeader(204) return nil } @@ -323,5 +324,6 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa if proposal.Ballot.EpochData.EligibilityCount == 0 { return &proposalResp{}, nil } + fmt.Println("writing response to client") return &proposalResp{buf: codec.MustEncode(proposal), nonce: nonce}, err } From 5d2d7b70f54a0f8168f48a2df5ab7f8af6ad5d1e Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:23:13 -0600 Subject: [PATCH 13/71] logger --- miner/remote_proposals.go | 8 ++++++-- node/node.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 6121db6b8a..01cb6f33a7 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -128,6 +128,7 @@ func NewRemoteBuilder( svc nodeService, layerSize uint32, layersPerEpoch uint32, + logger *zap.Logger, ) *RemoteProposalBuilder { pb := &RemoteProposalBuilder{ cfg: config{ @@ -136,7 +137,7 @@ func NewRemoteBuilder( layerSize: layerSize, layersPerEpoch: layersPerEpoch, }, - logger: zap.NewNop(), + logger: logger, clock: clock, publisher: publisher, nodeSvc: svc, @@ -147,6 +148,9 @@ func NewRemoteBuilder( signers: map[types.NodeID]*signerSession{}, }, } + if logger == nil { + pb.logger = zap.NewNop() + } return pb } @@ -215,7 +219,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) if err != nil { pb.logger.Error("get partial proposal", zap.Error(err)) } - + fmt.Println("remote proposal builder got proposal and nonce", proposal, nonce, err) if proposal == nil { // this node signer isn't eligible this epoch, continue pb.logger.Info("node not eligible on this layer. will try next") diff --git a/node/node.go b/node/node.go index 7dbe79b036..5fffb39281 100644 --- a/node/node.go +++ b/node/node.go @@ -1033,7 +1033,7 @@ func (app *App) initServices(ctx context.Context) error { var proposalBuilder *miner.ProposalBuilder var remoteProposalBuilder *miner.RemoteProposalBuilder if nodeServiceClient != nil { - remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, nodeServiceClient, nodeServiceClient, layerSize, layersPerEpoch) + remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, nodeServiceClient, nodeServiceClient, layerSize, layersPerEpoch, app.addLogger(ProposalBuilderLogger, lg).Zap()) for _, sig := range app.signers { remoteProposalBuilder.Register(sig) } From 97210228a13451c1a8da769add9f84421a24f144 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:31:29 -0600 Subject: [PATCH 14/71] housekeeping --- api/node/client/client.go | 8 -------- api/node/server/server.go | 2 -- hare3/hare.go | 2 -- miner/remote_proposals.go | 4 ---- node/node.go | 1 - 5 files changed, 17 deletions(-) diff --git a/api/node/client/client.go b/api/node/client/client.go index 8540655b06..92d2182d5e 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -199,14 +199,11 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty } switch resp.StatusCode { case http.StatusOK: - fmt.Println("client got OK for proposal") case http.StatusNoContent: // special case - no error but also no proposal, means // we're no eligibile this epoch with this node ID - fmt.Println("client not eligible for proposal") return nil, 0, nil default: - fmt.Println("client got unexpected status code") return nil, 0, fmt.Errorf("unexpected status: %s", resp.Status) } @@ -215,20 +212,15 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty return nil, 0, fmt.Errorf("read all: %w", err) } - fmt.Println("client going for decode") prop := types.Proposal{} codec.MustDecode(bytes, &prop) - fmt.Println("client decoded proposal") atxNonce := resp.Header.Get("x-spacemesh-atx-nonce") if atxNonce == "" { - panic("no nonce") return nil, 0, errors.New("atx nonce header not found") } nonce, err := strconv.ParseUint(atxNonce, 10, 64) if err != nil { - panic("bad atx integer parse") return nil, 0, fmt.Errorf("nonce parse: %w", err) } - fmt.Println("client returning proposal with nonce", prop, nonce) return &prop, nonce, nil } diff --git a/api/node/server/server.go b/api/node/server/server.go index 94350e1cad..2ee6cbcfa8 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -299,7 +299,6 @@ type proposalResp struct { func (p *proposalResp) VisitGetProposalLayerNodeResponse(w http.ResponseWriter) error { if p.buf == nil { - fmt.Println("writing no content to requester") w.WriteHeader(204) return nil } @@ -324,6 +323,5 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa if proposal.Ballot.EpochData.EligibilityCount == 0 { return &proposalResp{}, nil } - fmt.Println("writing response to client") return &proposalResp{buf: codec.MustEncode(proposal), nonce: nonce}, err } diff --git a/hare3/hare.go b/hare3/hare.go index 64efb84c03..943d73d94c 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -363,7 +363,6 @@ 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", @@ -374,7 +373,6 @@ 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{ diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 01cb6f33a7..95a431152e 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -2,7 +2,6 @@ package miner import ( "context" - "fmt" "runtime" "sync" @@ -197,7 +196,6 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { if current <= types.GetEffectiveGenesis() { continue } - fmt.Println("remote proposal builder going to build. layer", current.Uint32()) if err := pb.build(ctx, current); err != nil { pb.logger.Warn("failed to build proposal", log.ZContext(ctx), @@ -213,13 +211,11 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) pb.signers.mu.Lock() signers := maps.Values(pb.signers.signers) pb.signers.mu.Unlock() - fmt.Println("remote proposal builder got some signers", signers) for _, signer := range signers { proposal, nonce, err := pb.nodeSvc.Proposal(ctx, layer, signer.signer.NodeID()) if err != nil { pb.logger.Error("get partial proposal", zap.Error(err)) } - fmt.Println("remote proposal builder got proposal and nonce", proposal, nonce, err) if proposal == nil { // this node signer isn't eligible this epoch, continue pb.logger.Info("node not eligible on this layer. will try next") diff --git a/node/node.go b/node/node.go index 5fffb39281..5bdeea3bbe 100644 --- a/node/node.go +++ b/node/node.go @@ -1507,7 +1507,6 @@ func (app *App) startServices(ctx context.Context) error { }) app.eg.Go(func() error { if app.remoteProposalBuilder != nil { - fmt.Println("starting remote proposal building") return app.remoteProposalBuilder.Run(ctx) } return nil From 9fa34d43aaa8d6c056659a42279123eb82f90f4f Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:44:47 -0600 Subject: [PATCH 15/71] setup --- activation_service_poc/config.standalone.node-service.json | 5 +++++ activation_service_poc/docker-compose.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index e511285b6c..524d8b384c 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -20,5 +20,10 @@ "main": { "data-folder": "/tmp/spacemesh-node-service", "filelock": "/tmp/spacemesh-node-service/node.lock" + }, + "smeshing": { + "smeshing-opts": { + "smeshing-opts-datadir": "/tmp/spacemesh-node-service/post-data" + } } } diff --git a/activation_service_poc/docker-compose.yml b/activation_service_poc/docker-compose.yml index 6963bb6fea..edbb5b0e76 100644 --- a/activation_service_poc/docker-compose.yml +++ b/activation_service_poc/docker-compose.yml @@ -12,7 +12,7 @@ services: node-service: image: spacemeshos/go-spacemesh-dev:activation-service-poc.0 - command: ["-c", "/config.json", "--smeshing-opts-datadir", "/tmp/spacemesh-node-post"] + command: ["-c", "/config.json"] volumes: - /tmp/spacemesh-node-service:/tmp/spacemesh-node-service - ./config.standalone.node-service.json:/config.json From c3e8dea099ac67412328c0f7bcd8bef0876b4070 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:45:49 -0600 Subject: [PATCH 16/71] setup --- activation_service_poc/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activation_service_poc/start.sh b/activation_service_poc/start.sh index b1e2e96326..bc7af4abf3 100644 --- a/activation_service_poc/start.sh +++ b/activation_service_poc/start.sh @@ -10,5 +10,5 @@ TIME=$(date -u -d '2 minutes' "+%Y-%m-%dT%H:%M:%S%:z") sed -i "s/\"genesis-time\".*/\"genesis-time\"\:\"$TIME\"/g" config.standalone.client.json sed -i "s/\"genesis-time\".*/\"genesis-time\"\:\"$TIME\"/g" config.standalone.node-service.json -rm -rf /tmp/spacemesh* +rm -rf /tmp/space* docker compose up From 5e427452461004c1105d3e256cb2ef3a4dccdb41 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:55:01 -0600 Subject: [PATCH 17/71] config --- activation_service_poc/config.standalone.client.json | 6 +++--- activation_service_poc/config.standalone.node-service.json | 2 +- activation_service_poc/start.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/activation_service_poc/config.standalone.client.json b/activation_service_poc/config.standalone.client.json index 335ecd9a26..e475c1b491 100644 --- a/activation_service_poc/config.standalone.client.json +++ b/activation_service_poc/config.standalone.client.json @@ -10,9 +10,9 @@ "logging": { "trtl": "WARN", "beacon": "ERROR", - "proposalBuilder": "ERROR", - "atxBuilder": "DEBUG", - "hare": "DEBUG" + "proposalBuilder": "DEBUG", + "atxBuilder": "ERROR", + "hare": "ERROR" }, "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 524d8b384c..d27f3597d4 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -12,7 +12,7 @@ "trtl": "WARN", "beacon": "ERROR", "proposalBuilder": "ERROR", - "hare": "DEBUG" + "hare": "ERROR" }, "hare3": { "enable": true diff --git a/activation_service_poc/start.sh b/activation_service_poc/start.sh index bc7af4abf3..a2106d60b1 100644 --- a/activation_service_poc/start.sh +++ b/activation_service_poc/start.sh @@ -6,7 +6,7 @@ cd activation_service_poc IMAGE=$(docker images | head -n 2 | tail -n 1 | awk '{print $3}') sed -i "s/image.*/image:\ $IMAGE/g" docker-compose.yml -TIME=$(date -u -d '2 minutes' "+%Y-%m-%dT%H:%M:%S%:z") +TIME=$(date -u -d '1 minutes' "+%Y-%m-%dT%H:%M:%S%:z") sed -i "s/\"genesis-time\".*/\"genesis-time\"\:\"$TIME\"/g" config.standalone.client.json sed -i "s/\"genesis-time\".*/\"genesis-time\"\:\"$TIME\"/g" config.standalone.node-service.json From 409baaaaedb1dfb5317beca02d8793f7d5aee64c Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:58:40 -0600 Subject: [PATCH 18/71] compose-ports --- activation_service_poc/docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/activation_service_poc/docker-compose.yml b/activation_service_poc/docker-compose.yml index edbb5b0e76..ec6e3cb945 100644 --- a/activation_service_poc/docker-compose.yml +++ b/activation_service_poc/docker-compose.yml @@ -18,6 +18,9 @@ services: - ./config.standalone.node-service.json:/config.json networks: - spacemesh-net + ports: + - 9092:9092 + - 9093:9093 networks: spacemesh-net: From 5a0371078cca63720c4007e38557d351d7072006 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:03:28 -0600 Subject: [PATCH 19/71] chmod --- activation_service_poc/start.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 activation_service_poc/start.sh diff --git a/activation_service_poc/start.sh b/activation_service_poc/start.sh old mode 100644 new mode 100755 From 3843e22e39a1966fe2f7b5100331053b68a1a47f Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:25:32 -0600 Subject: [PATCH 20/71] print coinbase --- activation/activation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activation/activation.go b/activation/activation.go index bd27a313ce..4118d934b5 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -744,7 +744,7 @@ func (b *Builder) PublishActivationTx(ctx context.Context, sig *signing.EdSigner ) size, err := b.broadcast(ctx, atx) if err == nil { - b.logger.Info("atx published", log.ZShortStringer("atx_id", atx.ID()), zap.Int("size", size)) + b.logger.Info("atx published", log.ZShortStringer("atx_id", atx.ID()), zap.Stringer("coinbase", b.Coinbase().String()), zap.Int("size", size)) break } From 4590513e5044bfc64368f8834255a95d036960e0 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:30:07 -0600 Subject: [PATCH 21/71] build --- activation/activation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activation/activation.go b/activation/activation.go index 4118d934b5..dd5321606e 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -744,7 +744,7 @@ func (b *Builder) PublishActivationTx(ctx context.Context, sig *signing.EdSigner ) size, err := b.broadcast(ctx, atx) if err == nil { - b.logger.Info("atx published", log.ZShortStringer("atx_id", atx.ID()), zap.Stringer("coinbase", b.Coinbase().String()), zap.Int("size", size)) + b.logger.Info("atx published", log.ZShortStringer("atx_id", atx.ID()), zap.Stringer("coinbase", b.Coinbase()), zap.Int("size", size)) break } From 49ef08ce6ab61da04f182bcd322b39f46baaf233 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:40:02 -0600 Subject: [PATCH 22/71] smesh --- activation_service_poc/config.standalone.node-service.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index d27f3597d4..48cd4947eb 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -24,6 +24,7 @@ "smeshing": { "smeshing-opts": { "smeshing-opts-datadir": "/tmp/spacemesh-node-service/post-data" - } + }, + "smeshing-start": true } } From 8cda487d37c8cc2bf7bfe878e1af0ced277dc31b Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:46:35 -0600 Subject: [PATCH 23/71] unsmesh --- activation_service_poc/config.standalone.node-service.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index 48cd4947eb..d27f3597d4 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -24,7 +24,6 @@ "smeshing": { "smeshing-opts": { "smeshing-opts-datadir": "/tmp/spacemesh-node-service/post-data" - }, - "smeshing-start": true + } } } From a40c6855311434a1b8828ab66bf01cfea2d3d604 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:23:27 -0600 Subject: [PATCH 24/71] just rand shuffle --- miner/remote_proposals.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 95a431152e..201e9d1bf8 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -2,8 +2,10 @@ package miner import ( "context" + "math/rand" "runtime" "sync" + "time" "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" @@ -238,6 +240,9 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) continue } + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + rng.Shuffle(len(proposal.TxIDs), func(i, j int) { proposal.TxIDs[i], proposal.TxIDs[j] = proposal.TxIDs[j], proposal.TxIDs[i] }) + proposal.EligibilityProofs = eligibilities proposal.Ballot.Signature = signer.signer.Sign(signing.BALLOT, proposal.Ballot.SignedBytes()) proposal.Signature = signer.signer.Sign(signing.PROPOSAL, proposal.SignedBytes()) From 73da8a2bbc6222a416a2b0998e26560fe07fb2b7 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:26:31 -0600 Subject: [PATCH 25/71] docker --- .../config.standalone.client2.json | 32 +++++++++++++++++++ activation_service_poc/docker-compose.yml | 8 +++++ 2 files changed, 40 insertions(+) create mode 100644 activation_service_poc/config.standalone.client2.json diff --git a/activation_service_poc/config.standalone.client2.json b/activation_service_poc/config.standalone.client2.json new file mode 100644 index 0000000000..9988c0789b --- /dev/null +++ b/activation_service_poc/config.standalone.client2.json @@ -0,0 +1,32 @@ +{ + "preset": "standalone", + "api": { + "grpc-public-listener": "0.0.0.0:9082", + "grpc-private-listener": "0.0.0.0:9083" + }, + "genesis": { + "genesis-time": "2024-09-25T13:00:00.000Z" + }, + "logging": { + "trtl": "WARN", + "beacon": "ERROR", + "proposalBuilder": "DEBUG", + "atxBuilder": "ERROR", + "hare": "ERROR" + }, + "main": { + "node-service-address": "http://0.0.0.0:9099", + "data-folder": "/tmp/spacemesh-client2", + "filelock": "/tmp/spacemesh-client2/node.lock", + "poet-servers": [ + { + "address": "http://127.0.0.1:10011" + } + ] + }, + "smeshing": { + "smeshing-opts": { + "smeshing-opts-datadir": "/tmp/spacemesh-client2/post-data" + } + } +} diff --git a/activation_service_poc/docker-compose.yml b/activation_service_poc/docker-compose.yml index ec6e3cb945..657037fe3d 100644 --- a/activation_service_poc/docker-compose.yml +++ b/activation_service_poc/docker-compose.yml @@ -9,6 +9,14 @@ services: - ./config.standalone.client.json:/config.json networks: - spacemesh-net + activation-service-2: + image: spacemeshos/go-spacemesh-dev:activation-service-poc.0 + command: ["-c", "/config.json", "--node-service-address", "http://node-service:9099"] + volumes: + - /tmp/spacemesh-client2:/tmp/spacemesh-client2 + - ./config.standalone.client2.json:/config.json + networks: + - spacemesh-net node-service: image: spacemeshos/go-spacemesh-dev:activation-service-poc.0 From 0bbe0eb40f7d971a9702081aeee337d78dade5a2 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:28:29 -0600 Subject: [PATCH 26/71] one --- activation_service_poc/docker-compose.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/activation_service_poc/docker-compose.yml b/activation_service_poc/docker-compose.yml index 657037fe3d..ec6e3cb945 100644 --- a/activation_service_poc/docker-compose.yml +++ b/activation_service_poc/docker-compose.yml @@ -9,14 +9,6 @@ services: - ./config.standalone.client.json:/config.json networks: - spacemesh-net - activation-service-2: - image: spacemeshos/go-spacemesh-dev:activation-service-poc.0 - command: ["-c", "/config.json", "--node-service-address", "http://node-service:9099"] - volumes: - - /tmp/spacemesh-client2:/tmp/spacemesh-client2 - - ./config.standalone.client2.json:/config.json - networks: - - spacemesh-net node-service: image: spacemeshos/go-spacemesh-dev:activation-service-poc.0 From 8d2ec429684e464bf7321005f086f4a167595b17 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:49:56 -0600 Subject: [PATCH 27/71] cleanups --- miner/remote_proposals.go | 90 +-------------------------------------- 1 file changed, 1 insertion(+), 89 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 201e9d1bf8..06be2c7c41 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -17,85 +17,6 @@ import ( "golang.org/x/sync/errgroup" ) -// Opt for configuring ProposalBuilder. -// type Opt func(h *RemoteProposalBuilder) - -//// WithLayerSize defines the average number of proposal per layer. -//func WithLayerSize(size uint32) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.cfg.layerSize = size -//} -//} - -//// WithWorkersLimit configures paralelization factor for builder operation when working with -//// more than one signer. -//func WithWorkersLimit(limit int) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.cfg.workersLimit = limit -//} -//} - -//// WithLayerPerEpoch defines the number of layers per epoch. -//func WithLayerPerEpoch(layers uint32) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.cfg.layersPerEpoch = layers -//} -//} - -//func WithMinimalActiveSetWeight(weight []types.EpochMinimalActiveWeight) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.cfg.minActiveSetWeight = weight -//} -//} - -//// WithLogger defines the logger. -//func WithLogger(logger *zap.Logger) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.logger = logger -//} -//} - -//func WithHdist(dist uint32) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.cfg.hdist = dist -//} -//} - -//func WithNetworkDelay(delay time.Duration) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.cfg.networkDelay = delay -//} -//} - -//func WithMinGoodAtxPercent(percent int) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.cfg.goodAtxPercent = percent -//} -//} - -//// WithSigners guarantees that builder will start execution with provided list of signers. -//// Should be after logging. -//func WithSigners(signers ...*signing.EdSigner) Opt { -//return func(pb *RemoteProposalBuilder) { -//for _, signer := range signers { -//pb.Register(signer) -//} -//} -//} - -//// WithActivesetPreparation overwrites configuration for activeset preparation. -//func WithActivesetPreparation(prep ActiveSetPreparation) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.cfg.activeSet = prep -//} -//} - -//func withAtxSearch(p atxSearch) Opt { -//return func(pb *RemoteProposalBuilder) { -//pb.atxs = p -//} -//} - type nodeService interface { Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, uint64, error) } @@ -103,19 +24,10 @@ type RemoteProposalBuilder struct { logger *zap.Logger cfg config - // db sql.Executor - // localdb sql.Executor - // atxsdata *atxsdata.Data clock layerClock publisher pubsub.Publisher nodeSvc nodeService - // conState conservativeState - // tortoise votesEncoder - // syncer system.SyncStateProvider - // activeGen *activeSetGenerator - // atxs atxSearch - - signers struct { + signers struct { mu sync.Mutex signers map[types.NodeID]*signerSession } From 51f85aa7cae7c69172bf5973527223832db7f656 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:25:50 -0600 Subject: [PATCH 28/71] fix --- api/node/client/client_e2e_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/node/client/client_e2e_test.go b/api/node/client/client_e2e_test.go index fd1ac8f818..98d6f4a80e 100644 --- a/api/node/client/client_e2e_test.go +++ b/api/node/client/client_e2e_test.go @@ -26,6 +26,7 @@ type mocks struct { poetDb *server.MockpoetDB hare *server.Mockhare publisher *pubsub.MockPublisher + proposals *server.MockproposalBuilder } func setupE2E(t *testing.T) (*client.NodeService, *mocks) { @@ -37,9 +38,10 @@ func setupE2E(t *testing.T) (*client.NodeService, *mocks) { poetDb: server.NewMockpoetDB(ctrl), hare: server.NewMockhare(ctrl), publisher: pubsub.NewMockPublisher(ctrl), + proposals: server.NewMockproposalBuilder(ctrl), } - activationServiceServer := server.NewServer(m.atxService, m.publisher, m.poetDb, m.hare, log.Named("server")) + activationServiceServer := server.NewServer(m.atxService, m.publisher, m.poetDb, m.hare, m.proposals, log.Named("server")) listener, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) From 7766c23c75292b785630ef2ebfa57f7bb0ce4187 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:38:41 -0600 Subject: [PATCH 29/71] cleanups --- miner/proposal_builder.go | 21 --------------------- miner/remote_proposals.go | 3 +-- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index 8bd620e607..fdf5f81e2d 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -620,12 +620,9 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, node return nil, 0, err } - // don't accept registration in the middle of computing proposals signer := &signerSession{} encodeVotesOnce := sync.OnceValues(func() (*types.Opinion, error) { pb.tortoise.TallyVotes(lid) - // TODO(dshulyak) get rid from the EncodeVotesWithCurrent option in a followup - // there are some dependencies in the tests opinion, err := pb.tortoise.EncodeVotes(ctx, tortoise.EncodeVotesWithCurrent(lid)) if err != nil { return nil, fmt.Errorf("encoding votes: %w", err) @@ -649,21 +646,8 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, node return nil }) - // Two stage pipeline, with the stages running in parallel. - // 1. Initializes signers. Runs limited number of goroutines because the initialization is CPU and DB bound. - // 2. Collects eligible signers' sessions from the stage 1 and creates and publishes proposals. - - // Used to pass eligible singers from stage 1 → 2. - // Buffered with capacity for all signers so that writes don't block. eligible := make(chan *signerSession, 2) - - // Stage 1 - // Use a semaphore instead of eg.SetLimit so that the stage 2 starts immediately after - // scheduling all signers in the stage 1. Otherwise, stage 2 would wait for all stage 1 - // goroutines to at least start, which is not what we want. We want to start stage 2 as soon as possible. - // limiter := semaphore.NewWeighted(int64(pb.cfg.workersLimit)) var eg errgroup.Group - // for _, ss := range signers { eg.Go(func() error { if err := pb.initSignerDataFor(ctx, signer, lid, nodeID); err != nil { if errors.Is(err, errAtxNotAvailable) { @@ -697,7 +681,6 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, node eligible <- signer // won't block return nil }) - //} var stage1Err error go func() { @@ -921,7 +904,6 @@ func createPartialProposal( OpinionHash: opinion.Hash, }, Votes: opinion.Votes, - // EligibilityProofs: eligibility, }, TxIDs: txs, MeshHash: meshHash, @@ -938,9 +920,6 @@ func createPartialProposal( p.Ballot.RefBallot = session.ref } p.SmesherID = smesher - // p.Ballot.Signature = signer.Sign(signing.BALLOT, p.Ballot.SignedBytes()) - // p.Signature = signer.Sign(signing.PROPOSAL, p.SignedBytes()) - // p.MustInitialize() return p } diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 06be2c7c41..6c3b466ea2 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -159,7 +159,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) proposal.Ballot.Signature = signer.signer.Sign(signing.BALLOT, proposal.Ballot.SignedBytes()) proposal.Signature = signer.signer.Sign(signing.PROPOSAL, proposal.SignedBytes()) proposal.MustInitialize() - pb.logger.Info("did all the proposal stuff nicely, publishing", zap.Inline(proposal)) + pb.logger.Info("publishing proposal", zap.Inline(proposal)) if err := pb.publisher.Publish(ctx, pubsub.ProposalProtocol, codec.MustEncode(proposal)); err != nil { pb.logger.Error("failed to publish proposal", log.ZContext(ctx), @@ -168,7 +168,6 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) zap.Error(err), ) } - pb.logger.Info("proposal published successfully") } return nil } From cd78aa885f343463b69798356279b44ef9eadc34 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:49:12 -0600 Subject: [PATCH 30/71] lint --- activation/activation.go | 5 ++++- api/node/client/client.go | 6 ++++-- api/node/client/client_e2e_test.go | 7 ++++++- api/node/server/server.go | 4 +++- miner/proposal_builder.go | 11 +++++++++-- miner/remote_proposals.go | 12 +++++++----- node/node.go | 8 +++++++- 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/activation/activation.go b/activation/activation.go index ecd1b421ec..692f8329ff 100644 --- a/activation/activation.go +++ b/activation/activation.go @@ -749,7 +749,10 @@ func (b *Builder) PublishActivationTx(ctx context.Context, sig *signing.EdSigner ) size, err := b.broadcast(ctx, atx) if err == nil { - b.logger.Info("atx published", log.ZShortStringer("atx_id", atx.ID()), zap.Stringer("coinbase", b.Coinbase()), zap.Int("size", size)) + b.logger.Info("atx published", + log.ZShortStringer("atx_id", atx.ID()), + zap.Stringer("coinbase", b.Coinbase()), + zap.Int("size", size)) break } diff --git a/api/node/client/client.go b/api/node/client/client.go index 59a000903b..853d89e3a7 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -193,7 +193,9 @@ func (s *NodeService) Beacon(ctx context.Context, epoch types.EpochID) (types.Be return v, nil } -func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, uint64, error) { +func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) ( + *types.Proposal, uint64, error, +) { resp, err := s.client.GetProposalLayerNode(ctx, externalRef0.LayerID(layer), node.String()) if err != nil { return nil, 0, err @@ -215,7 +217,7 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty prop := types.Proposal{} codec.MustDecode(bytes, &prop) - atxNonce := resp.Header.Get("x-spacemesh-atx-nonce") + atxNonce := resp.Header.Get("X-Spacemesh-Atx-Nonce") if atxNonce == "" { return nil, 0, errors.New("atx nonce header not found") } diff --git a/api/node/client/client_e2e_test.go b/api/node/client/client_e2e_test.go index 98d6f4a80e..b128e6b947 100644 --- a/api/node/client/client_e2e_test.go +++ b/api/node/client/client_e2e_test.go @@ -41,7 +41,12 @@ func setupE2E(t *testing.T) (*client.NodeService, *mocks) { proposals: server.NewMockproposalBuilder(ctrl), } - activationServiceServer := server.NewServer(m.atxService, m.publisher, m.poetDb, m.hare, m.proposals, log.Named("server")) + activationServiceServer := server.NewServer(m.atxService, + m.publisher, + m.poetDb, + m.hare, + m.proposals, + log.Named("server")) listener, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) diff --git a/api/node/server/server.go b/api/node/server/server.go index bd75af433a..132f2758ee 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -332,7 +332,9 @@ func (p *proposalResp) VisitGetProposalLayerNodeResponse(w http.ResponseWriter) return err } -func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLayerNodeRequestObject) (GetProposalLayerNodeResponseObject, error) { +func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLayerNodeRequestObject) ( + GetProposalLayerNodeResponseObject, error, +) { hexBuf, err := hex.DecodeString(request.Node) if err != nil { return &proposalResp{}, err diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index fdf5f81e2d..acbac45d7f 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -567,7 +567,11 @@ func (pb *ProposalBuilder) initSignerData(ss *signerSession, lid types.LayerID) return nil } -func (pb *ProposalBuilder) initSignerDataFor(ctx context.Context, ss *signerSession, lid types.LayerID, nodeID types.NodeID) error { +func (pb *ProposalBuilder) initSignerDataFor(ctx context.Context, + ss *signerSession, + lid types.LayerID, + nodeID types.NodeID, +) error { if ss.session.epoch != lid.GetEpoch() { ss.session = session{epoch: lid.GetEpoch()} } @@ -615,7 +619,10 @@ func (pb *ProposalBuilder) initSignerDataFor(ctx context.Context, ss *signerSess return nil } -func (pb *ProposalBuilder) BuildFor(ctx context.Context, lid types.LayerID, nodeID types.NodeID) (*types.Proposal, types.VRFPostIndex, error) { +func (pb *ProposalBuilder) BuildFor(ctx context.Context, + lid types.LayerID, + nodeID types.NodeID, +) (*types.Proposal, types.VRFPostIndex, error) { if err := pb.initSharedData(ctx, lid); err != nil { return nil, 0, err } diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 6c3b466ea2..daec80fcc1 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -7,14 +7,15 @@ import ( "sync" "time" + "go.uber.org/zap" + "golang.org/x/exp/maps" + "golang.org/x/sync/errgroup" + "github.com/spacemeshos/go-spacemesh/codec" "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/log" "github.com/spacemeshos/go-spacemesh/p2p/pubsub" "github.com/spacemeshos/go-spacemesh/signing" - "go.uber.org/zap" - "golang.org/x/exp/maps" - "golang.org/x/sync/errgroup" ) type nodeService interface { @@ -31,7 +32,6 @@ type RemoteProposalBuilder struct { mu sync.Mutex signers map[types.NodeID]*signerSession } - shared sharedSession } // New creates a struct of block builder type. @@ -153,7 +153,9 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) } rng := rand.New(rand.NewSource(time.Now().UnixNano())) - rng.Shuffle(len(proposal.TxIDs), func(i, j int) { proposal.TxIDs[i], proposal.TxIDs[j] = proposal.TxIDs[j], proposal.TxIDs[i] }) + rng.Shuffle(len(proposal.TxIDs), func(i, j int) { + proposal.TxIDs[i], proposal.TxIDs[j] = proposal.TxIDs[j], proposal.TxIDs[i] + }) proposal.EligibilityProofs = eligibilities proposal.Ballot.Signature = signer.signer.Sign(signing.BALLOT, proposal.Ballot.SignedBytes()) diff --git a/node/node.go b/node/node.go index 2359abb4ee..798b23a9c9 100644 --- a/node/node.go +++ b/node/node.go @@ -1043,7 +1043,13 @@ func (app *App) initServices(ctx context.Context) error { var proposalBuilder *miner.ProposalBuilder var remoteProposalBuilder *miner.RemoteProposalBuilder if nodeServiceClient != nil { - remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, nodeServiceClient, nodeServiceClient, layerSize, layersPerEpoch, app.addLogger(ProposalBuilderLogger, lg).Zap()) + remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, + nodeServiceClient, + nodeServiceClient, + layerSize, + layersPerEpoch, + app.addLogger(ProposalBuilderLogger, lg).Zap(), + ) for _, sig := range app.signers { remoteProposalBuilder.Register(sig) } From 127dc05bfcee430f8f4de23c7361720963865811 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:22:42 -0600 Subject: [PATCH 31/71] add test --- api/node/client/client.go | 10 ++++++++- api/node/client/client_e2e_test.go | 36 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/api/node/client/client.go b/api/node/client/client.go index 853d89e3a7..44b1f79d5f 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -216,7 +216,15 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty } prop := types.Proposal{} - codec.MustDecode(bytes, &prop) + err = codec.Decode(bytes, &prop) + if err != nil { + return nil, 0, fmt.Errorf("decode proposal: %w", err) + } + err = prop.Initialize() + if err != nil { + return nil, 0, fmt.Errorf("proposal initialize: %w", err) + } + atxNonce := resp.Header.Get("X-Spacemesh-Atx-Nonce") if atxNonce == "" { return nil, 0, errors.New("atx nonce header not found") diff --git a/api/node/client/client_e2e_test.go b/api/node/client/client_e2e_test.go index b128e6b947..1bbad1a14e 100644 --- a/api/node/client/client_e2e_test.go +++ b/api/node/client/client_e2e_test.go @@ -17,6 +17,7 @@ import ( "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/hare3" pubsub "github.com/spacemeshos/go-spacemesh/p2p/pubsub/mocks" + "github.com/spacemeshos/go-spacemesh/signing" ) const retries = 3 @@ -191,3 +192,38 @@ func Test_Hare(t *testing.T) { require.NoError(t, err) }) } + +func TestProposals(t *testing.T) { + svc, mock := setupE2E(t) + t.Run("build for", func(t *testing.T) { + p := createProposal(t) + mock.proposals.EXPECT().BuildFor(gomock.Any(), gomock.Any(), gomock.Any()).Return(p, 0, nil) + prop, _, err := svc.Proposal(context.Background(), types.LayerID(112), types.NodeID{}) + require.NoError(t, err) + require.Equal(t, p, prop) + }) +} + +func createProposal(tb testing.TB) *types.Proposal { + tb.Helper() + b := types.RandomBallot() + b.Layer = 10000 + p := &types.Proposal{ + InnerProposal: types.InnerProposal{ + Ballot: *b, + TxIDs: []types.TransactionID{types.RandomTransactionID(), types.RandomTransactionID()}, + }, + } + p.Ballot.EpochData = &types.EpochData{ + EligibilityCount: 1, + } + + signer, err := signing.NewEdSigner() + require.NoError(tb, err) + p.Ballot.Signature = signer.Sign(signing.BALLOT, p.Ballot.SignedBytes()) + p.Ballot.SmesherID = signer.NodeID() + p.Signature = signer.Sign(signing.PROPOSAL, p.SignedBytes()) + p.SmesherID = signer.NodeID() + require.NoError(tb, p.Initialize()) + return p +} From 700dfb8681a79ce608b170227ab494b9c3cb3f64 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:31:30 -0600 Subject: [PATCH 32/71] improve error handling --- api/node/client/client.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/node/client/client.go b/api/node/client/client.go index 44b1f79d5f..d54c6a7e32 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -134,7 +134,7 @@ func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, r externalRef0.HareIter(round.Iter), externalRef0.HareRound(round.Round)) if err != nil { - return nil, err + return nil, fmt.Errorf("get hare message: %w", err) } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status: %s", resp.Status) @@ -149,7 +149,7 @@ func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, r 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 + return 0, fmt.Errorf("get total weight: %w", err) } if resp.StatusCode != http.StatusOK { return 0, fmt.Errorf("unexpected status: %s", resp.Status) @@ -164,7 +164,7 @@ func (s *NodeService) TotalWeight(ctx context.Context, layer types.LayerID) (uin 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 + return 0, fmt.Errorf("get miner weight: %w", err) } if resp.StatusCode != http.StatusOK { return 0, fmt.Errorf("unexpected status: %s", resp.Status) @@ -180,7 +180,7 @@ func (s *NodeService) Beacon(ctx context.Context, epoch types.EpochID) (types.Be v := types.Beacon{} resp, err := s.client.GetHareBeaconEpoch(ctx, externalRef0.EpochID(epoch)) if err != nil { - return v, err + return v, fmt.Errorf("get hare beacon: %w", err) } if resp.StatusCode != http.StatusOK { return v, fmt.Errorf("unexpected status: %s", resp.Status) @@ -198,7 +198,7 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty ) { resp, err := s.client.GetProposalLayerNode(ctx, externalRef0.LayerID(layer), node.String()) if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("get proposal layer: %w", err) } switch resp.StatusCode { case http.StatusOK: @@ -227,7 +227,7 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty atxNonce := resp.Header.Get("X-Spacemesh-Atx-Nonce") if atxNonce == "" { - return nil, 0, errors.New("atx nonce header not found") + return nil, 0, errors.New("missing atx nonce") } nonce, err := strconv.ParseUint(atxNonce, 10, 64) if err != nil { From 443e296f96a2491c55b16c4046255a339f56024d Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:35:03 -0600 Subject: [PATCH 33/71] improve remote proposals --- miner/remote_proposals.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index daec80fcc1..8a2e7742eb 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -129,10 +129,11 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) proposal, nonce, err := pb.nodeSvc.Proposal(ctx, layer, signer.signer.NodeID()) if err != nil { pb.logger.Error("get partial proposal", zap.Error(err)) + continue } if proposal == nil { // this node signer isn't eligible this epoch, continue - pb.logger.Info("node not eligible on this layer. will try next") + pb.logger.Info("node not eligible on this layer. will try later") continue } @@ -160,7 +161,11 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) proposal.EligibilityProofs = eligibilities proposal.Ballot.Signature = signer.signer.Sign(signing.BALLOT, proposal.Ballot.SignedBytes()) proposal.Signature = signer.signer.Sign(signing.PROPOSAL, proposal.SignedBytes()) - proposal.MustInitialize() + err = proposal.Initialize() + if err != nil { + pb.logger.Error("failed to initialize proposal", zap.Error(err)) + continue + } pb.logger.Info("publishing proposal", zap.Inline(proposal)) if err := pb.publisher.Publish(ctx, pubsub.ProposalProtocol, codec.MustEncode(proposal)); err != nil { pb.logger.Error("failed to publish proposal", From 20c59918c9a7b689b7d5140b90a0846fef2f0875 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:11:37 -0600 Subject: [PATCH 34/71] remove concurrency for BuildFor --- miner/proposal_builder.go | 121 ++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 71 deletions(-) diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index acbac45d7f..b0f9a73455 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -653,90 +653,69 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, return nil }) - eligible := make(chan *signerSession, 2) - var eg errgroup.Group - eg.Go(func() error { - if err := pb.initSignerDataFor(ctx, signer, lid, nodeID); err != nil { - if errors.Is(err, errAtxNotAvailable) { - pb.logger.Debug("smesher doesn't have atx that targets this epoch", - log.ZContext(ctx), - zap.Uint32("epoch_id", signer.session.epoch.Uint32()), - ) - } else { - return err - } - } - if lid <= signer.session.prev { - return fmt.Errorf("layer %d was already built by signer %s", lid, nodeID.ShortString()) - } - signer.session.prev = lid - proofs := signer.session.eligibilities.slots - if proofs == 0 { - pb.logger.Debug("not eligible for proposal in layer", + if err := pb.initSignerDataFor(ctx, signer, lid, nodeID); err != nil { + if errors.Is(err, errAtxNotAvailable) { + pb.logger.Debug("smesher doesn't have atx that targets this epoch", log.ZContext(ctx), - zap.Uint32("layer_id", lid.Uint32()), - zap.Uint32("epoch_id", lid.GetEpoch().Uint32()), + zap.Uint32("epoch_id", signer.session.epoch.Uint32()), ) - return nil + return nil, 0, errors.New("no atx in epoch") + } else { + return nil, 0, err } - pb.logger.Debug("eligible for proposals in layer", + } + if lid <= signer.session.prev { + return nil, 0, fmt.Errorf("layer %d was already built by signer %s", lid, nodeID.ShortString()) + } + signer.session.prev = lid + proofs := signer.session.eligibilities.slots + if proofs == 0 { + pb.logger.Debug("not eligible for proposal in layer", log.ZContext(ctx), zap.Uint32("layer_id", lid.Uint32()), zap.Uint32("epoch_id", lid.GetEpoch().Uint32()), - zap.Int("num proposals", int(proofs)), ) - eligible <- signer // won't block - return nil - }) + return nil, 0, nil + } + pb.logger.Debug("eligible for proposals in layer", + log.ZContext(ctx), + zap.Uint32("layer_id", lid.Uint32()), + zap.Uint32("epoch_id", lid.GetEpoch().Uint32()), + zap.Int("num proposals", int(proofs)), + ) - var stage1Err error - go func() { - stage1Err = eg.Wait() - close(eligible) - }() + opinion, err := encodeVotesOnce() + if err != nil { + return nil, 0, err + } - var prop *types.Proposal - // Stage 2 - eg2 := errgroup.Group{} - for ss := range eligible { - opinion, err := encodeVotesOnce() - if err != nil { + meshHash := calcMeshHashOnce() + + if signer.session.ref == types.EmptyBallotID { + if err := persistActiveSetOnce(); err != nil { return nil, 0, err } + } + slots := signer.session.eligibilities.slots - meshHash := calcMeshHashOnce() + txs := pb.conState.PredictBlock(lid, int(slots)) - eg2.Go(func() error { - // needs to be saved before publishing, as we will query it in handler - if ss.session.ref == types.EmptyBallotID { - if err := persistActiveSetOnce(); err != nil { - return err - } - } - slots := ss.session.eligibilities.slots - - txs := pb.conState.PredictBlock(lid, int(slots)) - - prop = createPartialProposal( - &ss.session, - pb.shared.beacon, - pb.shared.active.set, - nodeID, - lid, - txs, - opinion, - meshHash, - ) - pb.logger.Info("proposal created", - log.ZContext(ctx), - zap.Inline(prop), - zap.Object("latency", &ss.latency), - ) - return nil - }) - } - err := errors.Join(stage1Err, eg2.Wait()) - return prop, 0, err + prop := createPartialProposal( + &signer.session, + pb.shared.beacon, + pb.shared.active.set, + nodeID, + lid, + txs, + opinion, + meshHash, + ) + pb.logger.Info("proposal created", + log.ZContext(ctx), + zap.Inline(prop), + zap.Object("latency", &signer.latency), + ) + return prop, signer.session.nonce, nil } func (pb *ProposalBuilder) build(ctx context.Context, lid types.LayerID) error { From 0095c5c607c1f52c8fa2afec6edd5eb2111b15e1 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:27:37 -0600 Subject: [PATCH 35/71] add logging --- api/node/server/server.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/node/server/server.go b/api/node/server/server.go index 132f2758ee..dae13e3ff1 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -265,6 +265,7 @@ func (s *Server) GetHareTotalWeightLayer(ctx context.Context, ) (GetHareTotalWeightLayerResponseObject, error) { weight, err := s.hare.TotalWeight(ctx, types.LayerID(req.Layer)) if err != nil { + fmt.Println("total weight error", err) return nil, err } return &totalWeightResp{weight}, nil @@ -291,6 +292,7 @@ func (s *Server) GetHareWeightNodeIdLayer(ctx context.Context, id := types.BytesToNodeID(hexBuf) weight, err := s.hare.MinerWeight(ctx, id, types.LayerID(request.Layer)) if err != nil { + fmt.Println("miner weight error", err) return nil, fmt.Errorf("miner weight: %w", err) } return &nodeWeightResp{val: weight}, nil @@ -310,6 +312,7 @@ func (s *Server) GetHareBeaconEpoch(ctx context.Context, ) (GetHareBeaconEpochResponseObject, error) { beacon, err := s.hare.Beacon(ctx, types.EpochID(request.Epoch)) if err != nil { + fmt.Println("get hare beacon epoch error", err) return nil, err } return &beaconResp{b: beacon}, nil @@ -337,15 +340,18 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa ) { hexBuf, err := hex.DecodeString(request.Node) if err != nil { + fmt.Println("hex decode error", err) return &proposalResp{}, err } id := types.BytesToNodeID(hexBuf) proposal, nonce, err := s.proposals.BuildFor(ctx, types.LayerID(request.Layer), id) if err != nil { + fmt.Println("build for error", err) return &proposalResp{}, err } if proposal.Ballot.EpochData.EligibilityCount == 0 { + fmt.Println("no eligibility") return &proposalResp{}, nil } return &proposalResp{buf: codec.MustEncode(proposal), nonce: nonce}, err From 7882b98173a73b18626220baadf8296cb777002a Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:44:35 -0600 Subject: [PATCH 36/71] compose-setup --- .../config.standalone.client2.json | 32 ------------------- activation_service_poc/start.sh | 4 +-- 2 files changed, 2 insertions(+), 34 deletions(-) delete mode 100644 activation_service_poc/config.standalone.client2.json diff --git a/activation_service_poc/config.standalone.client2.json b/activation_service_poc/config.standalone.client2.json deleted file mode 100644 index 9988c0789b..0000000000 --- a/activation_service_poc/config.standalone.client2.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "preset": "standalone", - "api": { - "grpc-public-listener": "0.0.0.0:9082", - "grpc-private-listener": "0.0.0.0:9083" - }, - "genesis": { - "genesis-time": "2024-09-25T13:00:00.000Z" - }, - "logging": { - "trtl": "WARN", - "beacon": "ERROR", - "proposalBuilder": "DEBUG", - "atxBuilder": "ERROR", - "hare": "ERROR" - }, - "main": { - "node-service-address": "http://0.0.0.0:9099", - "data-folder": "/tmp/spacemesh-client2", - "filelock": "/tmp/spacemesh-client2/node.lock", - "poet-servers": [ - { - "address": "http://127.0.0.1:10011" - } - ] - }, - "smeshing": { - "smeshing-opts": { - "smeshing-opts-datadir": "/tmp/spacemesh-client2/post-data" - } - } -} diff --git a/activation_service_poc/start.sh b/activation_service_poc/start.sh index 9b80184e3a..c5e7209221 100755 --- a/activation_service_poc/start.sh +++ b/activation_service_poc/start.sh @@ -7,8 +7,8 @@ cd activation_service_poc export IMAGE=$(docker images | head -n 2 | tail -n 1 | awk '{print $3}') TIME=$(date -u -d '2 minutes' "+%Y-%m-%dT%H:%M:%S%:z") -jq ".genesis.\"genesis-time\" |= \"$TIME\"" config.standalone.client.json -jq ".genesis.\"genesis-time\" |= \"$TIME\"" config.standalone.node-service.json +jq ".genesis.\"genesis-time\" |= \"$TIME\"" config.standalone.client.json > config.standalone.client.json +jq ".genesis.\"genesis-time\" |= \"$TIME\"" config.standalone.node-service.json > config.standalone.node-service.json rm -rf /tmp/space* docker compose up From 7ec811484f50719bf18afbc45a8f1b02f0b5b4ef Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 09:57:52 -0600 Subject: [PATCH 37/71] start.sh --- activation_service_poc/start.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/activation_service_poc/start.sh b/activation_service_poc/start.sh index c5e7209221..64573038d6 100755 --- a/activation_service_poc/start.sh +++ b/activation_service_poc/start.sh @@ -7,8 +7,10 @@ cd activation_service_poc export IMAGE=$(docker images | head -n 2 | tail -n 1 | awk '{print $3}') TIME=$(date -u -d '2 minutes' "+%Y-%m-%dT%H:%M:%S%:z") -jq ".genesis.\"genesis-time\" |= \"$TIME\"" config.standalone.client.json > config.standalone.client.json -jq ".genesis.\"genesis-time\" |= \"$TIME\"" config.standalone.node-service.json > config.standalone.node-service.json +for file in config.standalone.client.json config.standalone.node-service.json;do + # Run your command on each file + jq ".genesis.\"genesis-time\" |= \"$TIME\"" "$file" > temp.json && mv temp.json "$file" +done rm -rf /tmp/space* docker compose up From 1f0486d5700753151561dba3de65d63aa2a31f37 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:31:33 -0600 Subject: [PATCH 38/71] remove context --- hare3/remote.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hare3/remote.go b/hare3/remote.go index 871b943f06..d9be36a1dd 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -33,7 +33,6 @@ type RemoteHare struct { oracle *legacyOracle sessions map[types.LayerID]*protocol eg errgroup.Group - ctx context.Context svc NodeService log *zap.Logger @@ -175,8 +174,8 @@ func (h *RemoteHare) run(ctx context.Context, session *session) error { h.log.Debug("active in preround. waiting for preround delay", zap.Uint32("lid", session.lid.Uint32())) select { case <-h.wallClock.After(walltime.Sub(h.wallClock.Now())): - case <-h.ctx.Done(): - return h.ctx.Err() + case <-ctx.Done(): + return ctx.Err() } } msgBytes, err := h.svc.GetHareMessage(ctx, session.lid, session.proto.IterRound) @@ -235,7 +234,7 @@ func (h *RemoteHare) run(ctx context.Context, session *session) error { } onRound(session.proto) // advance the protocol state before continuing - case <-h.ctx.Done(): + case <-ctx.Done(): return nil } } From b6e8d4cc4b234bc9aa9bc177c456af5177fbbd81 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:32:33 -0600 Subject: [PATCH 39/71] start --- activation_service_poc/start.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/activation_service_poc/start.sh b/activation_service_poc/start.sh index 64573038d6..86d7e7598c 100755 --- a/activation_service_poc/start.sh +++ b/activation_service_poc/start.sh @@ -8,7 +8,6 @@ export IMAGE=$(docker images | head -n 2 | tail -n 1 | awk '{print $3}') TIME=$(date -u -d '2 minutes' "+%Y-%m-%dT%H:%M:%S%:z") for file in config.standalone.client.json config.standalone.node-service.json;do - # Run your command on each file jq ".genesis.\"genesis-time\" |= \"$TIME\"" "$file" > temp.json && mv temp.json "$file" done From 931acbb3cd1e808fc33b2264a7dbf6443f4f3caf Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:33:29 -0600 Subject: [PATCH 40/71] cleanup --- activation_service_poc/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activation_service_poc/docker-compose.yml b/activation_service_poc/docker-compose.yml index 4a03d95c2e..14156cf5b7 100644 --- a/activation_service_poc/docker-compose.yml +++ b/activation_service_poc/docker-compose.yml @@ -12,7 +12,7 @@ services: node-service: image: ${IMAGE} - command: ["-c", "/config.json", "--smeshing-opts-datadir", "/tmp/spacemesh-node-post"] + command: ["-c", "/config.json" ] volumes: - /tmp/spacemesh-node-service:/tmp/spacemesh-node-service - ./config.standalone.node-service.json:/config.json From 13cb81f5319ecf7663ff4f6f9269c4d1d94e4abd Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:11:23 -0600 Subject: [PATCH 41/71] improve logging hopefully, improve hare error handling when theres nothing to do --- .../config.standalone.client.json | 3 +- api/node/client/client.go | 20 +++-- api/node/server/server.go | 6 -- hare3/remote.go | 6 ++ node/node.go | 73 ++++++++++--------- 5 files changed, 59 insertions(+), 49 deletions(-) diff --git a/activation_service_poc/config.standalone.client.json b/activation_service_poc/config.standalone.client.json index e475c1b491..9f6a316cc1 100644 --- a/activation_service_poc/config.standalone.client.json +++ b/activation_service_poc/config.standalone.client.json @@ -12,7 +12,8 @@ "beacon": "ERROR", "proposalBuilder": "DEBUG", "atxBuilder": "ERROR", - "hare": "ERROR" + "hare": "ERROR", + "nodeServiceClient": "ERROR" }, "main": { "node-service-address": "http://0.0.0.0:9099", diff --git a/api/node/client/client.go b/api/node/client/client.go index d54c6a7e32..10d07c8d26 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -136,14 +136,22 @@ func (s *NodeService) GetHareMessage(ctx context.Context, layer types.LayerID, r if err != nil { return nil, fmt.Errorf("get hare message: %w", err) } - if resp.StatusCode != http.StatusOK { + switch resp.StatusCode { + case http.StatusOK: + bytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read all: %w", err) + } + return bytes, nil + + case http.StatusNoContent: + // no message to return, special case, return nil,nil + // and the caller should assume there's no message to process, + // therefore hare probably terminated. + return nil, nil + default: 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) TotalWeight(ctx context.Context, layer types.LayerID) (uint64, error) { diff --git a/api/node/server/server.go b/api/node/server/server.go index dae13e3ff1..132f2758ee 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -265,7 +265,6 @@ func (s *Server) GetHareTotalWeightLayer(ctx context.Context, ) (GetHareTotalWeightLayerResponseObject, error) { weight, err := s.hare.TotalWeight(ctx, types.LayerID(req.Layer)) if err != nil { - fmt.Println("total weight error", err) return nil, err } return &totalWeightResp{weight}, nil @@ -292,7 +291,6 @@ func (s *Server) GetHareWeightNodeIdLayer(ctx context.Context, id := types.BytesToNodeID(hexBuf) weight, err := s.hare.MinerWeight(ctx, id, types.LayerID(request.Layer)) if err != nil { - fmt.Println("miner weight error", err) return nil, fmt.Errorf("miner weight: %w", err) } return &nodeWeightResp{val: weight}, nil @@ -312,7 +310,6 @@ func (s *Server) GetHareBeaconEpoch(ctx context.Context, ) (GetHareBeaconEpochResponseObject, error) { beacon, err := s.hare.Beacon(ctx, types.EpochID(request.Epoch)) if err != nil { - fmt.Println("get hare beacon epoch error", err) return nil, err } return &beaconResp{b: beacon}, nil @@ -340,18 +337,15 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa ) { hexBuf, err := hex.DecodeString(request.Node) if err != nil { - fmt.Println("hex decode error", err) return &proposalResp{}, err } id := types.BytesToNodeID(hexBuf) proposal, nonce, err := s.proposals.BuildFor(ctx, types.LayerID(request.Layer), id) if err != nil { - fmt.Println("build for error", err) return &proposalResp{}, err } if proposal.Ballot.EpochData.EligibilityCount == 0 { - fmt.Println("no eligibility") return &proposalResp{}, nil } return &proposalResp{buf: codec.MustEncode(proposal), nonce: nonce}, err diff --git a/hare3/remote.go b/hare3/remote.go index d9be36a1dd..74c589e453 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -221,6 +221,12 @@ func (h *RemoteHare) run(ctx context.Context, session *session) error { ) msgBytes, err := h.svc.GetHareMessage(ctx, session.lid, session.proto.IterRound) + if msgBytes == nil && err == nil { + // special case - no message to process, we're either too early or hare terminated. + // do the onRound and then continue + onRound(session.proto) // advance the protocol state before continuing + continue + } if err != nil { h.log.Error("get hare message", zap.Error(err)) onRound(session.proto) // advance the protocol state before continuing diff --git a/node/node.go b/node/node.go index 798b23a9c9..7f22a0228d 100644 --- a/node/node.go +++ b/node/node.go @@ -106,41 +106,42 @@ const ( // Logger names. const ( - ClockLogger = "clock" - P2PLogger = "p2p" - PostLogger = "post" - PostServiceLogger = "postService" - PostInfoServiceLogger = "postInfoService" - StateDbLogger = "stateDb" - ApiStateDBLogger = "apiStateDB" - BeaconLogger = "beacon" - CachedDBLogger = "cachedDB" - PoetDbLogger = "poetDb" - TrtlLogger = "trtl" - ATXHandlerLogger = "atxHandler" - ATXBuilderLogger = "atxBuilder" - MeshLogger = "mesh" - SyncLogger = "sync" - HareOracleLogger = "hareOracle" - HareLogger = "hare" - BlockCertLogger = "blockCert" - BlockGenLogger = "blockGenerator" - BlockHandlerLogger = "blockHandler" - TxHandlerLogger = "txHandler" - ProposalStoreLogger = "proposalStore" - ProposalBuilderLogger = "proposalBuilder" - ProposalListenerLogger = "proposalListener" - NipostBuilderLogger = "nipostBuilder" - NipostValidatorLogger = "nipostValidator" - Fetcher = "fetcher" - TimeSyncLogger = "timesync" - VMLogger = "vm" - GRPCLogger = "grpc" - ConStateLogger = "conState" - ExecutorLogger = "executor" - MalfeasanceLogger = "malfeasance" - BootstrapLogger = "bootstrap" - NodeServiceLogger = "nodeService" + ClockLogger = "clock" + P2PLogger = "p2p" + PostLogger = "post" + PostServiceLogger = "postService" + PostInfoServiceLogger = "postInfoService" + StateDbLogger = "stateDb" + ApiStateDBLogger = "apiStateDB" + BeaconLogger = "beacon" + CachedDBLogger = "cachedDB" + PoetDbLogger = "poetDb" + TrtlLogger = "trtl" + ATXHandlerLogger = "atxHandler" + ATXBuilderLogger = "atxBuilder" + MeshLogger = "mesh" + SyncLogger = "sync" + HareOracleLogger = "hareOracle" + HareLogger = "hare" + BlockCertLogger = "blockCert" + BlockGenLogger = "blockGenerator" + BlockHandlerLogger = "blockHandler" + TxHandlerLogger = "txHandler" + ProposalStoreLogger = "proposalStore" + ProposalBuilderLogger = "proposalBuilder" + ProposalListenerLogger = "proposalListener" + NipostBuilderLogger = "nipostBuilder" + NipostValidatorLogger = "nipostValidator" + Fetcher = "fetcher" + TimeSyncLogger = "timesync" + VMLogger = "vm" + GRPCLogger = "grpc" + ConStateLogger = "conState" + ExecutorLogger = "executor" + MalfeasanceLogger = "malfeasance" + BootstrapLogger = "bootstrap" + NodeServiceLogger = "nodeService" + NodeServiceClientLogger = "nodeServiceClient" ) func GetCommand() *cobra.Command { @@ -594,7 +595,7 @@ func (app *App) initServices(ctx context.Context) error { var nodeServiceClient *client.NodeService if server := app.Config.BaseConfig.NodeServiceAddress; server != "" { - logger := app.log.Zap().Named("node-svc-client") + logger := app.addLogger(NodeServiceClientLogger, lg).Zap() cfg := &nodeclient.Config{ RetryWaitMin: time.Millisecond * 500, RetryWaitMax: time.Second, From ee6f033c3d612cd1d2ff2114a9f8722ede7f8a26 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:17:20 -0600 Subject: [PATCH 42/71] wip --- config/logging.go | 69 ++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/config/logging.go b/config/logging.go index d71313568e..3fe37cbee6 100644 --- a/config/logging.go +++ b/config/logging.go @@ -17,40 +17,41 @@ const ( type LoggerConfig struct { Encoder LogEncoder `mapstructure:"log-encoder"` - AppLoggerLevel string `mapstructure:"app"` - ClockLoggerLevel string `mapstructure:"clock"` - P2PLoggerLevel string `mapstructure:"p2p"` - PostLoggerLevel string `mapstructure:"post"` - PostServiceLoggerLevel string `mapstructure:"postService"` - StateDbLoggerLevel string `mapstructure:"stateDb"` - BeaconLoggerLevel string `mapstructure:"beacon"` - CachedDBLoggerLevel string `mapstructure:"cachedDb"` - PoetDbLoggerLevel string `mapstructure:"poetDb"` - TrtlLoggerLevel string `mapstructure:"trtl"` - AtxHandlerLevel string `mapstructure:"atxHandler"` - AtxBuilderLoggerLevel string `mapstructure:"atxBuilder"` - MeshLoggerLevel string `mapstructure:"mesh"` - SyncLoggerLevel string `mapstructure:"sync"` - HareOracleLoggerLevel string `mapstructure:"hareOracle"` - HareLoggerLevel string `mapstructure:"hare"` - BlockCertLoggerLevel string `mapstructure:"blockCert"` - BlockGenLoggerLevel string `mapstructure:"blockGenerator"` - BlockHandlerLoggerLevel string `mapstructure:"blockHandler"` - TxHandlerLoggerLevel string `mapstructure:"txHandler"` - ProposalStoreLoggerLevel string `mapstructure:"proposalStore"` - ProposalBuilderLoggerLevel string `mapstructure:"proposalBuilder"` - ProposalListenerLevel string `mapstructure:"proposalListener"` - NipostBuilderLoggerLevel string `mapstructure:"nipostBuilder"` - NipostValidatorLoggerLevel string `mapstructure:"nipostValidator"` - FetcherLoggerLevel string `mapstructure:"fetcher"` - TimeSyncLoggerLevel string `mapstructure:"timesync"` - VMLogLevel string `mapstructure:"vm"` - GrpcLoggerLevel string `mapstructure:"grpc"` - ConStateLoggerLevel string `mapstructure:"conState"` - ExecutorLoggerLevel string `mapstructure:"executor"` - MalfeasanceLoggerLevel string `mapstructure:"malfeasance"` - BootstrapLoggerLevel string `mapstructure:"bootstrap"` - NodeServiceLoggerLevel string `mapstructure:"nodeService"` + AppLoggerLevel string `mapstructure:"app"` + ClockLoggerLevel string `mapstructure:"clock"` + P2PLoggerLevel string `mapstructure:"p2p"` + PostLoggerLevel string `mapstructure:"post"` + PostServiceLoggerLevel string `mapstructure:"postService"` + StateDbLoggerLevel string `mapstructure:"stateDb"` + BeaconLoggerLevel string `mapstructure:"beacon"` + CachedDBLoggerLevel string `mapstructure:"cachedDb"` + PoetDbLoggerLevel string `mapstructure:"poetDb"` + TrtlLoggerLevel string `mapstructure:"trtl"` + AtxHandlerLevel string `mapstructure:"atxHandler"` + AtxBuilderLoggerLevel string `mapstructure:"atxBuilder"` + MeshLoggerLevel string `mapstructure:"mesh"` + SyncLoggerLevel string `mapstructure:"sync"` + HareOracleLoggerLevel string `mapstructure:"hareOracle"` + HareLoggerLevel string `mapstructure:"hare"` + BlockCertLoggerLevel string `mapstructure:"blockCert"` + BlockGenLoggerLevel string `mapstructure:"blockGenerator"` + BlockHandlerLoggerLevel string `mapstructure:"blockHandler"` + TxHandlerLoggerLevel string `mapstructure:"txHandler"` + ProposalStoreLoggerLevel string `mapstructure:"proposalStore"` + ProposalBuilderLoggerLevel string `mapstructure:"proposalBuilder"` + ProposalListenerLevel string `mapstructure:"proposalListener"` + NipostBuilderLoggerLevel string `mapstructure:"nipostBuilder"` + NipostValidatorLoggerLevel string `mapstructure:"nipostValidator"` + FetcherLoggerLevel string `mapstructure:"fetcher"` + TimeSyncLoggerLevel string `mapstructure:"timesync"` + VMLogLevel string `mapstructure:"vm"` + GrpcLoggerLevel string `mapstructure:"grpc"` + ConStateLoggerLevel string `mapstructure:"conState"` + ExecutorLoggerLevel string `mapstructure:"executor"` + MalfeasanceLoggerLevel string `mapstructure:"malfeasance"` + BootstrapLoggerLevel string `mapstructure:"bootstrap"` + NodeServiceLoggerLevel string `mapstructure:"nodeService"` + NodeServiceClientLoggerLevel string `mapstructure:"nodeServiceClient"` } func DefaultLoggingConfig() LoggerConfig { From bcf20efc071e31faa58a0995ecb03831caeb2f50 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:23:58 -0600 Subject: [PATCH 43/71] log --- hare3/remote.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hare3/remote.go b/hare3/remote.go index 74c589e453..3e8785533b 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -256,6 +256,8 @@ func (h *RemoteHare) signPub(ctx context.Context, session *session, message *Mes msg.Eligibility = *vrf msg.Sender = session.signers[i].NodeID() msg.Signature = session.signers[i].Sign(signing.HARE, msg.ToMetadata().ToBytes()) + h.log.Info("publishing hare message", zap.Uint32("layer", session.lid.Uint32()), + zap.Stringer("beacon", session.beacon)) if err := h.svc.Publish(ctx, h.config.ProtocolName, msg.ToBytes()); err != nil { h.log.Error("failed to publish", zap.Inline(&msg), zap.Error(err)) } From cae688646923e4f16e5801ee8c6541a2c4b95a44 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:26:38 -0600 Subject: [PATCH 44/71] dont initialize twice --- api/node/client/client.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/api/node/client/client.go b/api/node/client/client.go index 10d07c8d26..677e3d23f2 100644 --- a/api/node/client/client.go +++ b/api/node/client/client.go @@ -228,11 +228,6 @@ func (s *NodeService) Proposal(ctx context.Context, layer types.LayerID, node ty if err != nil { return nil, 0, fmt.Errorf("decode proposal: %w", err) } - err = prop.Initialize() - if err != nil { - return nil, 0, fmt.Errorf("proposal initialize: %w", err) - } - atxNonce := resp.Header.Get("X-Spacemesh-Atx-Nonce") if atxNonce == "" { return nil, 0, errors.New("missing atx nonce") From b9a0079a8c3606ba9a85b4da8a10c2dd168e1a4b Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:42:22 -0600 Subject: [PATCH 45/71] handle no eligiblities --- api/node/server/server.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/node/server/server.go b/api/node/server/server.go index 132f2758ee..6ff7c75530 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -345,6 +345,9 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa if err != nil { return &proposalResp{}, err } + if proposal == nil && err == nil { + return &proposalResp{}, nil + } if proposal.Ballot.EpochData.EligibilityCount == 0 { return &proposalResp{}, nil } From 71cd5f4b4687020a8778985ddade1e9bc73b0a32 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:17:29 -0600 Subject: [PATCH 46/71] wip --- api/node/server/server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/node/server/server.go b/api/node/server/server.go index 6ff7c75530..8e44dd4a76 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -348,6 +348,10 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa if proposal == nil && err == nil { return &proposalResp{}, nil } + // we have to explicitly check this case otherwise the next line may panic + if proposal.Ballot.RefBallot != types.EmptyBallotID { + return &proposalResp{buf: codec.MustEncode(proposal), nonce: nonce}, nil + } if proposal.Ballot.EpochData.EligibilityCount == 0 { return &proposalResp{}, nil } From 37b05e95ec5dd04ed739960eda4eacfda70e1b15 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:39:03 -0600 Subject: [PATCH 47/71] add a map to check for the elgibilities --- miner/remote_proposals.go | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 8a2e7742eb..aa0ead74f1 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -20,6 +20,7 @@ import ( type nodeService interface { Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, uint64, error) + Beacon(ctx context.Context, epoch types.EpochID) (types.Beacon, error) } type RemoteProposalBuilder struct { logger *zap.Logger @@ -32,6 +33,7 @@ type RemoteProposalBuilder struct { mu sync.Mutex signers map[types.NodeID]*signerSession } + epochEligibilities map[types.EpochID]uint32 } // New creates a struct of block builder type. @@ -50,10 +52,11 @@ func NewRemoteBuilder( layerSize: layerSize, layersPerEpoch: layersPerEpoch, }, - logger: logger, - clock: clock, - publisher: publisher, - nodeSvc: svc, + logger: logger, + clock: clock, + publisher: publisher, + nodeSvc: svc, + epochEligibilities: make(map[types.EpochID]uint32), signers: struct { mu sync.Mutex signers map[types.NodeID]*signerSession @@ -136,13 +139,34 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) pb.logger.Info("node not eligible on this layer. will try later") continue } + bcn, err := pb.nodeSvc.Beacon(ctx, layer.GetEpoch()) + if err != nil { + pb.logger.Error("get beacon", zap.Error(err)) + continue + } + var ( + elig uint32 + ok bool + ) + if proposal.Ballot.EpochData != nil { + elig, ok = pb.epochEligibilities[layer.GetEpoch()] + if !ok { + pb.epochEligibilities[layer.GetEpoch()] = proposal.Ballot.EpochData.EligibilityCount + elig = proposal.Ballot.EpochData.EligibilityCount + } + } else { + elig, ok = pb.epochEligibilities[layer.GetEpoch()] + if !ok { + panic("missing epoch eligibilities") + } + } proofs := calcEligibilityProofs( signer.signer.VRFSigner(), layer.GetEpoch(), - proposal.Ballot.EpochData.Beacon, + bcn, types.VRFPostIndex(nonce), - proposal.Ballot.EpochData.EligibilityCount, + elig, pb.cfg.layersPerEpoch, ) From 480929079601c0825a6e4ae13d274e8846e06114 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:44:16 -0600 Subject: [PATCH 48/71] wip --- miner/remote_proposals.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index aa0ead74f1..16c0d3f8e5 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -120,8 +120,23 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { zap.Error(err), ) } + + pb.clean(current) + } + } +} + +func (pb *RemoteProposalBuilder) clean(layer types.LayerID) { + var vals []types.EpochID + lim := layer.GetEpoch() - 2 + for k := range pb.epochEligibilities { + if k <= lim { + vals = append(vals, k) } } + for _, v := range vals { + delete(pb.epochEligibilities, v) + } } func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) error { From 1b4d4e68a8802968e1c0b6c1e12ad661de99da3a Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:39:30 -0600 Subject: [PATCH 49/71] wip --- node/node.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/node/node.go b/node/node.go index 7f22a0228d..5e185b24c7 100644 --- a/node/node.go +++ b/node/node.go @@ -809,12 +809,14 @@ func (app *App) initServices(ctx context.Context) error { eligibility.WithConfig(app.Config.HareEligibility), eligibility.WithLogger(app.addLogger(HareOracleLogger, lg).Zap()), } + var bcnGetter system.BeaconGetter = beaconProtocol if nodeServiceClient != nil { + bcnGetter = &beaconGetter{client: nodeServiceClient} extraOpts = append(extraOpts, eligibility.WithTotalWeightFunc(nodeServiceClient.TotalWeight)) extraOpts = append(extraOpts, eligibility.WithMinerWeightFunc(nodeServiceClient.MinerWeight)) } app.hOracle = eligibility.New( - beaconProtocol, + bcnGetter, app.db, app.atxsdata, vrfVerifier, @@ -2461,3 +2463,13 @@ func (p *proposalConsumerHare) OnProposal(proposal *types.Proposal) error { } return p.hare4.OnProposal(proposal) } + +type beaconGetter struct { + client *client.NodeService +} + +func (b *beaconGetter) GetBeacon(e types.EpochID) (types.Beacon, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + return b.client.Beacon(ctx, e) +} From 51f5fc0627c5739e25c5d45e0686aabb11079e7b Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:54:10 -0600 Subject: [PATCH 50/71] handle nilnil --- hare3/remote.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hare3/remote.go b/hare3/remote.go index 3e8785533b..1654b57f83 100644 --- a/hare3/remote.go +++ b/hare3/remote.go @@ -181,10 +181,12 @@ func (h *RemoteHare) run(ctx context.Context, session *session) error { msgBytes, err := h.svc.GetHareMessage(ctx, session.lid, session.proto.IterRound) if err != nil && active { h.log.Error("get hare message on preround", zap.Error(err)) + } else if msgBytes == nil && err == nil { + // do nothing, there's no message to process } else { msg := &Message{} if err := codec.Decode(msgBytes, msg); err != nil { - h.log.Error("decode remote hare message", zap.Error(err)) + h.log.Error("preround decode remote hare message", zap.Error(err)) } else { h.signPub(ctx, session, msg) } From d6bb8584246a3f98c471661eb81a733cacad43e8 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:24:10 -0600 Subject: [PATCH 51/71] fix test --- api/node/client/client_e2e_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/api/node/client/client_e2e_test.go b/api/node/client/client_e2e_test.go index 1bbad1a14e..c04847021d 100644 --- a/api/node/client/client_e2e_test.go +++ b/api/node/client/client_e2e_test.go @@ -199,6 +199,7 @@ func TestProposals(t *testing.T) { p := createProposal(t) mock.proposals.EXPECT().BuildFor(gomock.Any(), gomock.Any(), gomock.Any()).Return(p, 0, nil) prop, _, err := svc.Proposal(context.Background(), types.LayerID(112), types.NodeID{}) + prop.MustInitialize() require.NoError(t, err) require.Equal(t, p, prop) }) From a5cdb63f4737d0e712e9a8d8f8c56cf4507f4d33 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:50:44 -0600 Subject: [PATCH 52/71] Update node/node.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartosz Różański --- node/node.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/node/node.go b/node/node.go index 5e185b24c7..7587da53d4 100644 --- a/node/node.go +++ b/node/node.go @@ -1520,12 +1520,11 @@ func (app *App) startServices(ctx context.Context) error { } return nil }) - app.eg.Go(func() error { - if app.remoteProposalBuilder != nil { + if app.remoteProposalBuilder != nil { + app.eg.Go(func() error { return app.remoteProposalBuilder.Run(ctx) - } - return nil - }) + }) + } if app.Config.SMESHING.CoinbaseAccount != "" { coinbaseAddr, err := types.StringToAddress(app.Config.SMESHING.CoinbaseAccount) From e434cfd22f06b990726572dda7bdef89db4d86c6 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:14:23 -0600 Subject: [PATCH 53/71] wip --- activation_service_poc/config.standalone.node-service.json | 5 ----- activation_service_poc/start.sh | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index d27f3597d4..0d4fea883e 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -20,10 +20,5 @@ "main": { "data-folder": "/tmp/spacemesh-node-service", "filelock": "/tmp/spacemesh-node-service/node.lock" - }, - "smeshing": { - "smeshing-opts": { - "smeshing-opts-datadir": "/tmp/spacemesh-node-service/post-data" - } } } diff --git a/activation_service_poc/start.sh b/activation_service_poc/start.sh index 86d7e7598c..207d98d369 100755 --- a/activation_service_poc/start.sh +++ b/activation_service_poc/start.sh @@ -11,5 +11,5 @@ for file in config.standalone.client.json config.standalone.node-service.json;do jq ".genesis.\"genesis-time\" |= \"$TIME\"" "$file" > temp.json && mv temp.json "$file" done -rm -rf /tmp/space* +rm -rf /tmp/spacemesh* docker compose up From 2288bf6feedcd9d948e3af6b2012763b21bb17f8 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:29:58 -0600 Subject: [PATCH 54/71] Update api/node/server/server.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartosz Różański --- api/node/server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/node/server/server.go b/api/node/server/server.go index 8e44dd4a76..28381e4350 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -345,7 +345,7 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa if err != nil { return &proposalResp{}, err } - if proposal == nil && err == nil { + if proposal == nil { return &proposalResp{}, nil } // we have to explicitly check this case otherwise the next line may panic From 02653fb7ce48f4b29dde6c9a2f26eb678abee3d1 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:30:15 -0600 Subject: [PATCH 55/71] wip --- txs/conservative_state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/txs/conservative_state.go b/txs/conservative_state.go index f89700b007..aaee8e4ce9 100644 --- a/txs/conservative_state.go +++ b/txs/conservative_state.go @@ -101,7 +101,7 @@ func (cs *ConservativeState) PredictBlock(lid types.LayerID, numEligibility int) predictedBlock, _ := mi.PopAll() numTXs := numEligibility * cs.cfg.NumTXsPerProposal n := min(numTXs, len(predictedBlock)) - txs := make([]types.TransactionID, 0, n) + txs := make([]types.TransactionID, n) for i, tx := range predictedBlock[:n] { txs[i] = tx.ID } From 835adbced8c9b4bea94961cd1e1ab672171f3a00 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:05:39 -0600 Subject: [PATCH 56/71] comments --- miner/proposal_builder.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index b0f9a73455..f16d13f417 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -628,21 +628,21 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, } signer := &signerSession{} - encodeVotesOnce := sync.OnceValues(func() (*types.Opinion, error) { + encodeVotes := func() (*types.Opinion, error) { pb.tortoise.TallyVotes(lid) opinion, err := pb.tortoise.EncodeVotes(ctx, tortoise.EncodeVotesWithCurrent(lid)) if err != nil { return nil, fmt.Errorf("encoding votes: %w", err) } return opinion, nil - }) + } - calcMeshHashOnce := sync.OnceValue(func() types.Hash32 { + calcMeshHash := func() types.Hash32 { meshHash := pb.decideMeshHash(ctx, lid) return meshHash - }) + } - persistActiveSetOnce := sync.OnceValue(func() error { + persistActiveSet := func() error { err := activesets.Add(pb.db, pb.shared.active.id, &types.EpochActiveSet{ Epoch: pb.shared.epoch, Set: pb.shared.active.set, @@ -651,7 +651,7 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, return err } return nil - }) + } if err := pb.initSignerDataFor(ctx, signer, lid, nodeID); err != nil { if errors.Is(err, errAtxNotAvailable) { @@ -684,15 +684,15 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, zap.Int("num proposals", int(proofs)), ) - opinion, err := encodeVotesOnce() + opinion, err := encodeVotes() if err != nil { return nil, 0, err } - meshHash := calcMeshHashOnce() + meshHash := calcMeshHash() if signer.session.ref == types.EmptyBallotID { - if err := persistActiveSetOnce(); err != nil { + if err := persistActiveSet(); err != nil { return nil, 0, err } } From 7019b843156ffbccc19c554e64c67454cae56796 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:58:42 -0600 Subject: [PATCH 57/71] calculate proofs once --- miner/remote_proposals.go | 56 ++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 16c0d3f8e5..48899e85fe 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -33,7 +33,12 @@ type RemoteProposalBuilder struct { mu sync.Mutex signers map[types.NodeID]*signerSession } - epochEligibilities map[types.EpochID]uint32 + epochEligibilities map[types.EpochID]map[types.NodeID]eligibilities +} + +type eligibilities struct { + slots uint32 + proofs map[types.LayerID][]types.VotingEligibility } // New creates a struct of block builder type. @@ -56,7 +61,7 @@ func NewRemoteBuilder( clock: clock, publisher: publisher, nodeSvc: svc, - epochEligibilities: make(map[types.EpochID]uint32), + epochEligibilities: make(map[types.EpochID]map[types.NodeID]eligibilities), signers: struct { mu sync.Mutex signers map[types.NodeID]*signerSession @@ -140,11 +145,13 @@ func (pb *RemoteProposalBuilder) clean(layer types.LayerID) { } func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) error { + epoch := layer.GetEpoch() pb.signers.mu.Lock() signers := maps.Values(pb.signers.signers) pb.signers.mu.Unlock() for _, signer := range signers { - proposal, nonce, err := pb.nodeSvc.Proposal(ctx, layer, signer.signer.NodeID()) + nodeId := signer.signer.NodeID() + proposal, nonce, err := pb.nodeSvc.Proposal(ctx, layer, nodeId) if err != nil { pb.logger.Error("get partial proposal", zap.Error(err)) continue @@ -154,37 +161,50 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) pb.logger.Info("node not eligible on this layer. will try later") continue } - bcn, err := pb.nodeSvc.Beacon(ctx, layer.GetEpoch()) + bcn, err := pb.nodeSvc.Beacon(ctx, epoch) if err != nil { pb.logger.Error("get beacon", zap.Error(err)) continue } var ( - elig uint32 - ok bool + elig uint32 + proofs map[types.LayerID][]types.VotingEligibility + ok bool ) if proposal.Ballot.EpochData != nil { - elig, ok = pb.epochEligibilities[layer.GetEpoch()] + _, ok := pb.epochEligibilities[epoch] + if !ok { + pb.epochEligibilities[epoch] = make(map[types.NodeID]eligibilities) + } + nodeElig, ok := pb.epochEligibilities[epoch][nodeId] if !ok { - pb.epochEligibilities[layer.GetEpoch()] = proposal.Ballot.EpochData.EligibilityCount elig = proposal.Ballot.EpochData.EligibilityCount + proofs = calcEligibilityProofs( + signer.signer.VRFSigner(), + epoch, + bcn, + types.VRFPostIndex(nonce), + elig, + pb.cfg.layersPerEpoch, + ) + pb.epochEligibilities[epoch][nodeId] = eligibilities{slots: elig, proofs: proofs} + } else { + elig = nodeElig.slots + proofs = nodeElig.proofs } } else { - elig, ok = pb.epochEligibilities[layer.GetEpoch()] + nodeElig, ok := pb.epochEligibilities[epoch] if !ok { panic("missing epoch eligibilities") } + eligibilities, ok := nodeElig[nodeId] + if !ok { + panic("missing node epoch eligibilities") + } + elig = eligibilities.slots + proofs = eligibilities.proofs } - proofs := calcEligibilityProofs( - signer.signer.VRFSigner(), - layer.GetEpoch(), - bcn, - types.VRFPostIndex(nonce), - elig, - pb.cfg.layersPerEpoch, - ) - eligibilities, ok := proofs[layer] if !ok { // not eligible in this layer, continue From 6849740496c691df6e298cc24637eb486376ad9d Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:25:01 -0600 Subject: [PATCH 58/71] Update api/node/server/server.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartosz Różański --- api/node/server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/node/server/server.go b/api/node/server/server.go index 28381e4350..71ef6888f0 100644 --- a/api/node/server/server.go +++ b/api/node/server/server.go @@ -355,5 +355,5 @@ func (s *Server) GetProposalLayerNode(ctx context.Context, request GetProposalLa if proposal.Ballot.EpochData.EligibilityCount == 0 { return &proposalResp{}, nil } - return &proposalResp{buf: codec.MustEncode(proposal), nonce: nonce}, err + return &proposalResp{buf: codec.MustEncode(proposal), nonce: nonce}, nil } From 6b194c352799cd09f2dcc580887f431a9f2b80df Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:04:44 -0600 Subject: [PATCH 59/71] remove predict block method --- miner/proposal_builder.go | 3 +-- miner/remote_proposals.go | 24 +++++++----------------- txs/conservative_state.go | 13 ------------- 3 files changed, 8 insertions(+), 32 deletions(-) diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index f16d13f417..ce100aa249 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -41,7 +41,6 @@ var errAtxNotAvailable = errors.New("atx not available") type conservativeState interface { SelectProposalTXs(types.LayerID, int) []types.TransactionID - PredictBlock(types.LayerID, int) []types.TransactionID } type votesEncoder interface { @@ -698,7 +697,7 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, } slots := signer.session.eligibilities.slots - txs := pb.conState.PredictBlock(lid, int(slots)) + txs := pb.conState.SelectProposalTXs(lid, int(slots)) prop := createPartialProposal( &signer.session, diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 48899e85fe..47bce72376 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -33,12 +33,7 @@ type RemoteProposalBuilder struct { mu sync.Mutex signers map[types.NodeID]*signerSession } - epochEligibilities map[types.EpochID]map[types.NodeID]eligibilities -} - -type eligibilities struct { - slots uint32 - proofs map[types.LayerID][]types.VotingEligibility + epochEligibilities map[types.EpochID]map[types.NodeID]map[types.LayerID][]types.VotingEligibility } // New creates a struct of block builder type. @@ -61,7 +56,7 @@ func NewRemoteBuilder( clock: clock, publisher: publisher, nodeSvc: svc, - epochEligibilities: make(map[types.EpochID]map[types.NodeID]eligibilities), + epochEligibilities: make(map[types.EpochID]map[types.NodeID]map[types.LayerID][]types.VotingEligibility), signers: struct { mu sync.Mutex signers map[types.NodeID]*signerSession @@ -167,42 +162,37 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) continue } var ( - elig uint32 proofs map[types.LayerID][]types.VotingEligibility ok bool ) if proposal.Ballot.EpochData != nil { _, ok := pb.epochEligibilities[epoch] if !ok { - pb.epochEligibilities[epoch] = make(map[types.NodeID]eligibilities) + pb.epochEligibilities[epoch] = make(map[types.NodeID]map[types.LayerID][]types.VotingEligibility) } nodeElig, ok := pb.epochEligibilities[epoch][nodeId] if !ok { - elig = proposal.Ballot.EpochData.EligibilityCount proofs = calcEligibilityProofs( signer.signer.VRFSigner(), epoch, bcn, types.VRFPostIndex(nonce), - elig, + proposal.Ballot.EpochData.EligibilityCount, pb.cfg.layersPerEpoch, ) - pb.epochEligibilities[epoch][nodeId] = eligibilities{slots: elig, proofs: proofs} + pb.epochEligibilities[epoch][nodeId] = proofs } else { - elig = nodeElig.slots - proofs = nodeElig.proofs + proofs = nodeElig } } else { nodeElig, ok := pb.epochEligibilities[epoch] if !ok { panic("missing epoch eligibilities") } - eligibilities, ok := nodeElig[nodeId] + proofs, ok = nodeElig[nodeId] if !ok { panic("missing node epoch eligibilities") } - elig = eligibilities.slots - proofs = eligibilities.proofs } eligibilities, ok := proofs[layer] diff --git a/txs/conservative_state.go b/txs/conservative_state.go index aaee8e4ce9..edfa66afa8 100644 --- a/txs/conservative_state.go +++ b/txs/conservative_state.go @@ -95,19 +95,6 @@ func (cs *ConservativeState) SelectProposalTXs(lid types.LayerID, numEligibility return getProposalTXs(logger, numTXs, predictedBlock, byAddrAndNonce) } -func (cs *ConservativeState) PredictBlock(lid types.LayerID, numEligibility int) []types.TransactionID { - logger := cs.logger.With(zap.Uint32("layer_id", lid.Uint32())) - mi := newMempoolIterator(logger, cs.cache, cs.cfg.BlockGasLimit) - predictedBlock, _ := mi.PopAll() - numTXs := numEligibility * cs.cfg.NumTXsPerProposal - n := min(numTXs, len(predictedBlock)) - txs := make([]types.TransactionID, n) - for i, tx := range predictedBlock[:n] { - txs[i] = tx.ID - } - return txs -} - func getProposalTXs( logger *zap.Logger, numTXs int, From efc8bb7b75e0db17191d4571754eda6af6c1279c Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:47:52 -0600 Subject: [PATCH 60/71] remove previous epoch --- miner/remote_proposals.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 47bce72376..4824cfb26e 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -128,7 +128,7 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { func (pb *RemoteProposalBuilder) clean(layer types.LayerID) { var vals []types.EpochID - lim := layer.GetEpoch() - 2 + lim := layer.GetEpoch() - 1 for k := range pb.epochEligibilities { if k <= lim { vals = append(vals, k) From 62054fd8b77a7768c0f062c8a9ac478f0a2d0c33 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:09:30 -0600 Subject: [PATCH 61/71] change beacon context ordering --- hare3/eligibility/interface.go | 4 +++ hare3/eligibility/mocks.go | 63 ++++++++++++++++++++++++++++++++++ hare3/eligibility/oracle.go | 14 ++++---- miner/mocks/mocks.go | 38 -------------------- node/node.go | 14 ++++---- 5 files changed, 81 insertions(+), 52 deletions(-) diff --git a/hare3/eligibility/interface.go b/hare3/eligibility/interface.go index e87f959dc5..7f84e065cf 100644 --- a/hare3/eligibility/interface.go +++ b/hare3/eligibility/interface.go @@ -24,3 +24,7 @@ type Rolacle interface { CalcEligibility(context.Context, types.LayerID, uint32, int, types.NodeID, types.VrfSignature) (uint16, error) Proof(context.Context, *signing.VRFSigner, types.LayerID, uint32) (types.VrfSignature, error) } + +type BeaconProvider interface { + Beacon(context.Context, types.EpochID) (types.Beacon, error) +} diff --git a/hare3/eligibility/mocks.go b/hare3/eligibility/mocks.go index 94735f9f09..80c93c896e 100644 --- a/hare3/eligibility/mocks.go +++ b/hare3/eligibility/mocks.go @@ -321,3 +321,66 @@ func (c *MockRolacleValidateCall) DoAndReturn(f func(context.Context, types.Laye c.Call = c.Call.DoAndReturn(f) return c } + +// MockBeaconProvider is a mock of BeaconProvider interface. +type MockBeaconProvider struct { + ctrl *gomock.Controller + recorder *MockBeaconProviderMockRecorder + isgomock struct{} +} + +// MockBeaconProviderMockRecorder is the mock recorder for MockBeaconProvider. +type MockBeaconProviderMockRecorder struct { + mock *MockBeaconProvider +} + +// NewMockBeaconProvider creates a new mock instance. +func NewMockBeaconProvider(ctrl *gomock.Controller) *MockBeaconProvider { + mock := &MockBeaconProvider{ctrl: ctrl} + mock.recorder = &MockBeaconProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBeaconProvider) EXPECT() *MockBeaconProviderMockRecorder { + return m.recorder +} + +// Beacon mocks base method. +func (m *MockBeaconProvider) Beacon(arg0 context.Context, arg1 types.EpochID) (types.Beacon, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Beacon", arg0, arg1) + ret0, _ := ret[0].(types.Beacon) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Beacon indicates an expected call of Beacon. +func (mr *MockBeaconProviderMockRecorder) Beacon(arg0, arg1 any) *MockBeaconProviderBeaconCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Beacon", reflect.TypeOf((*MockBeaconProvider)(nil).Beacon), arg0, arg1) + return &MockBeaconProviderBeaconCall{Call: call} +} + +// MockBeaconProviderBeaconCall wrap *gomock.Call +type MockBeaconProviderBeaconCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockBeaconProviderBeaconCall) Return(arg0 types.Beacon, arg1 error) *MockBeaconProviderBeaconCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockBeaconProviderBeaconCall) Do(f func(context.Context, types.EpochID) (types.Beacon, error)) *MockBeaconProviderBeaconCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockBeaconProviderBeaconCall) DoAndReturn(f func(context.Context, types.EpochID) (types.Beacon, error)) *MockBeaconProviderBeaconCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/hare3/eligibility/oracle.go b/hare3/eligibility/oracle.go index 6503c56ead..61226e6396 100644 --- a/hare3/eligibility/oracle.go +++ b/hare3/eligibility/oracle.go @@ -92,7 +92,7 @@ type Oracle struct { // until graded oracle is implemented synced bool - beacons system.BeaconGetter + beacons BeaconProvider atxsdata *atxsdata.Data db sql.Executor vrfVerifier vrfVerifier @@ -130,7 +130,7 @@ func WithLogger(logger *zap.Logger) Opt { // New returns a new eligibility oracle instance. func New( - beacons system.BeaconGetter, + beacons BeaconProvider, db sql.Executor, atxsdata *atxsdata.Data, vrfVerifier vrfVerifier, @@ -192,7 +192,7 @@ func (o *Oracle) resetCacheOnSynced(ctx context.Context) { // buildVRFMessage builds the VRF message used as input for hare eligibility validation. func (o *Oracle) buildVRFMessage(ctx context.Context, layer types.LayerID, round uint32) ([]byte, error) { - beacon, err := o.beacons.GetBeacon(layer.GetEpoch()) + beacon, err := o.beacons.Beacon(ctx, layer.GetEpoch()) if err != nil { return nil, fmt.Errorf("get beacon: %w", err) } @@ -398,7 +398,7 @@ func (o *Oracle) Proof( layer types.LayerID, round uint32, ) (types.VrfSignature, error) { - beacon, err := o.beacons.GetBeacon(layer.GetEpoch()) + beacon, err := o.beacons.Beacon(ctx, layer.GetEpoch()) if err != nil { return types.EmptyVrfSignature, fmt.Errorf("get beacon: %w", err) } @@ -488,7 +488,7 @@ func (o *Oracle) computeActiveSet(ctx context.Context, targetEpoch types.EpochID return nil, err } if len(activeSet) == 0 { - return o.activeSetFromRefBallots(targetEpoch) + return o.activeSetFromRefBallots(ctx, targetEpoch) } return activeSet, nil } @@ -508,8 +508,8 @@ func (o *Oracle) computeActiveWeights( return identities, nil } -func (o *Oracle) activeSetFromRefBallots(epoch types.EpochID) ([]types.ATXID, error) { - beacon, err := o.beacons.GetBeacon(epoch) +func (o *Oracle) activeSetFromRefBallots(ctx context.Context, epoch types.EpochID) ([]types.ATXID, error) { + beacon, err := o.beacons.Beacon(ctx, epoch) if err != nil { return nil, fmt.Errorf("get beacon: %w", err) } diff --git a/miner/mocks/mocks.go b/miner/mocks/mocks.go index c144b67db0..55415cd438 100644 --- a/miner/mocks/mocks.go +++ b/miner/mocks/mocks.go @@ -44,44 +44,6 @@ func (m *MockconservativeState) EXPECT() *MockconservativeStateMockRecorder { return m.recorder } -// PredictBlock mocks base method. -func (m *MockconservativeState) PredictBlock(arg0 types.LayerID, arg1 int) []types.TransactionID { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PredictBlock", arg0, arg1) - ret0, _ := ret[0].([]types.TransactionID) - return ret0 -} - -// PredictBlock indicates an expected call of PredictBlock. -func (mr *MockconservativeStateMockRecorder) PredictBlock(arg0, arg1 any) *MockconservativeStatePredictBlockCall { - mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PredictBlock", reflect.TypeOf((*MockconservativeState)(nil).PredictBlock), arg0, arg1) - return &MockconservativeStatePredictBlockCall{Call: call} -} - -// MockconservativeStatePredictBlockCall wrap *gomock.Call -type MockconservativeStatePredictBlockCall struct { - *gomock.Call -} - -// Return rewrite *gomock.Call.Return -func (c *MockconservativeStatePredictBlockCall) Return(arg0 []types.TransactionID) *MockconservativeStatePredictBlockCall { - c.Call = c.Call.Return(arg0) - return c -} - -// Do rewrite *gomock.Call.Do -func (c *MockconservativeStatePredictBlockCall) Do(f func(types.LayerID, int) []types.TransactionID) *MockconservativeStatePredictBlockCall { - c.Call = c.Call.Do(f) - return c -} - -// DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockconservativeStatePredictBlockCall) DoAndReturn(f func(types.LayerID, int) []types.TransactionID) *MockconservativeStatePredictBlockCall { - c.Call = c.Call.DoAndReturn(f) - return c -} - // SelectProposalTXs mocks base method. func (m *MockconservativeState) SelectProposalTXs(arg0 types.LayerID, arg1 int) []types.TransactionID { m.ctrl.T.Helper() diff --git a/node/node.go b/node/node.go index 7587da53d4..962164fe05 100644 --- a/node/node.go +++ b/node/node.go @@ -809,11 +809,13 @@ func (app *App) initServices(ctx context.Context) error { eligibility.WithConfig(app.Config.HareEligibility), eligibility.WithLogger(app.addLogger(HareOracleLogger, lg).Zap()), } - var bcnGetter system.BeaconGetter = beaconProtocol + var bcnGetter eligibility.BeaconProvider if nodeServiceClient != nil { - bcnGetter = &beaconGetter{client: nodeServiceClient} + bcnGetter = nodeServiceClient extraOpts = append(extraOpts, eligibility.WithTotalWeightFunc(nodeServiceClient.TotalWeight)) extraOpts = append(extraOpts, eligibility.WithMinerWeightFunc(nodeServiceClient.MinerWeight)) + } else { + bcnGetter = &beaconGetter{beaconProtocol} } app.hOracle = eligibility.New( bcnGetter, @@ -2464,11 +2466,9 @@ func (p *proposalConsumerHare) OnProposal(proposal *types.Proposal) error { } type beaconGetter struct { - client *client.NodeService + provider *beacon.ProtocolDriver } -func (b *beaconGetter) GetBeacon(e types.EpochID) (types.Beacon, error) { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - return b.client.Beacon(ctx, e) +func (b *beaconGetter) Beacon(_ context.Context, e types.EpochID) (types.Beacon, error) { + return b.provider.GetBeacon(e) } From e9337054915b3e9354061173eee5f29868c27f80 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:19:20 -0600 Subject: [PATCH 62/71] split interfaces --- miner/remote_proposals.go | 25 ++++++++++++++++--------- node/node.go | 4 +++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 4824cfb26e..f63b9ed243 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -18,18 +18,23 @@ import ( "github.com/spacemeshos/go-spacemesh/signing" ) -type nodeService interface { +type proposalService interface { Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, uint64, error) +} + +type beaconService interface { Beacon(ctx context.Context, epoch types.EpochID) (types.Beacon, error) } + type RemoteProposalBuilder struct { logger *zap.Logger cfg config - clock layerClock - publisher pubsub.Publisher - nodeSvc nodeService - signers struct { + clock layerClock + publisher pubsub.Publisher + beaconSvc beaconService + proposalSvc proposalService + signers struct { mu sync.Mutex signers map[types.NodeID]*signerSession } @@ -40,7 +45,8 @@ type RemoteProposalBuilder struct { func NewRemoteBuilder( clock layerClock, publisher pubsub.Publisher, - svc nodeService, + bcn beaconService, + prop proposalService, layerSize uint32, layersPerEpoch uint32, logger *zap.Logger, @@ -55,7 +61,8 @@ func NewRemoteBuilder( logger: logger, clock: clock, publisher: publisher, - nodeSvc: svc, + beaconSvc: bcn, + proposalSvc: prop, epochEligibilities: make(map[types.EpochID]map[types.NodeID]map[types.LayerID][]types.VotingEligibility), signers: struct { mu sync.Mutex @@ -146,7 +153,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) pb.signers.mu.Unlock() for _, signer := range signers { nodeId := signer.signer.NodeID() - proposal, nonce, err := pb.nodeSvc.Proposal(ctx, layer, nodeId) + proposal, nonce, err := pb.proposalSvc.Proposal(ctx, layer, nodeId) if err != nil { pb.logger.Error("get partial proposal", zap.Error(err)) continue @@ -156,7 +163,7 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) pb.logger.Info("node not eligible on this layer. will try later") continue } - bcn, err := pb.nodeSvc.Beacon(ctx, epoch) + bcn, err := pb.beaconSvc.Beacon(ctx, epoch) if err != nil { pb.logger.Error("get beacon", zap.Error(err)) continue diff --git a/node/node.go b/node/node.go index 962164fe05..b7283c2472 100644 --- a/node/node.go +++ b/node/node.go @@ -1048,7 +1048,9 @@ func (app *App) initServices(ctx context.Context) error { var proposalBuilder *miner.ProposalBuilder var remoteProposalBuilder *miner.RemoteProposalBuilder if nodeServiceClient != nil { - remoteProposalBuilder = miner.NewRemoteBuilder(app.clock, + remoteProposalBuilder = miner.NewRemoteBuilder( + app.clock, + nodeServiceClient, nodeServiceClient, nodeServiceClient, layerSize, From 9dd74e8833ec1d4a066d3b2bbacbf0a6d3d5fb2e Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:51:54 -0600 Subject: [PATCH 63/71] removed remote proposal shuffling on the smesher side --- hare3/eligibility/oracle_test.go | 48 ++++++++++++++++---------------- hare3/hare_test.go | 9 +++--- miner/remote_proposals.go | 7 ----- 3 files changed, 29 insertions(+), 35 deletions(-) diff --git a/hare3/eligibility/oracle_test.go b/hare3/eligibility/oracle_test.go index 916f34432d..fb9562a6cf 100644 --- a/hare3/eligibility/oracle_test.go +++ b/hare3/eligibility/oracle_test.go @@ -49,7 +49,7 @@ type testOracle struct { tb testing.TB db sql.StateDatabase atxsdata *atxsdata.Data - mBeacon *mocks.MockBeaconGetter + mBeacon *MockBeaconProvider mVerifier *MockvrfVerifier } @@ -58,7 +58,7 @@ func defaultOracle(tb testing.TB) *testOracle { atxsdata := atxsdata.New() ctrl := gomock.NewController(tb) - mBeacon := mocks.NewMockBeaconGetter(ctrl) + mBeacon := NewMockBeaconProvider(ctrl) mVerifier := NewMockvrfVerifier(ctrl) to := &testOracle{ @@ -183,7 +183,7 @@ func TestCalcEligibility(t *testing.T) { t.Run("empty active set", func(t *testing.T) { o := defaultOracle(t) - o.mBeacon.EXPECT().GetBeacon(gomock.Any()) + o.mBeacon.EXPECT().Beacon(gomock.Any(), gomock.Any()) lid := types.EpochID(5).FirstLayer() res, err := o.CalcEligibility(context.Background(), lid, 1, 1, nid, types.EmptyVrfSignature) require.ErrorIs(t, err, errEmptyActiveSet) @@ -204,7 +204,7 @@ func TestCalcEligibility(t *testing.T) { layer := types.EpochID(5).FirstLayer() miners := o.createLayerData(layer.Sub(defLayersPerEpoch), 5) errUnknown := errors.New("unknown") - o.mBeacon.EXPECT().GetBeacon(layer.GetEpoch()).Return(types.EmptyBeacon, errUnknown).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), layer.GetEpoch()).Return(types.EmptyBeacon, errUnknown).Times(1) res, err := o.CalcEligibility(context.Background(), layer, 0, 1, miners[0], types.EmptyVrfSignature) require.ErrorIs(t, err, errUnknown) @@ -215,7 +215,7 @@ func TestCalcEligibility(t *testing.T) { o := defaultOracle(t) layer := types.EpochID(5).FirstLayer() miners := o.createLayerData(layer.Sub(defLayersPerEpoch), 5) - o.mBeacon.EXPECT().GetBeacon(layer.GetEpoch()).Return(types.RandomBeacon(), nil).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), layer.GetEpoch()).Return(types.RandomBeacon(), nil).Times(1) o.mVerifier.EXPECT().Verify(gomock.Any(), gomock.Any(), gomock.Any()).Return(false).Times(1) res, err := o.CalcEligibility(context.Background(), layer, 0, 1, miners[0], types.EmptyVrfSignature) @@ -225,7 +225,7 @@ func TestCalcEligibility(t *testing.T) { t.Run("empty active with fallback", func(t *testing.T) { o := defaultOracle(t) - o.mBeacon.EXPECT().GetBeacon(gomock.Any()) + o.mBeacon.EXPECT().Beacon(gomock.Any(), gomock.Any()) lid := types.EpochID(5).FirstLayer().Add(o.cfg.ConfidenceParam) res, err := o.CalcEligibility(context.Background(), lid, 1, 1, nid, types.EmptyVrfSignature) require.ErrorIs(t, err, errEmptyActiveSet) @@ -234,7 +234,7 @@ func TestCalcEligibility(t *testing.T) { activeSet := types.RandomActiveSet(111) miners := o.createActiveSet(types.EpochID(4).FirstLayer(), activeSet) o.UpdateActiveSet(5, activeSet) - o.mBeacon.EXPECT().GetBeacon(lid.GetEpoch()).Return(types.RandomBeacon(), nil) + o.mBeacon.EXPECT().Beacon(gomock.Any(), lid.GetEpoch()).Return(types.RandomBeacon(), nil) o.mVerifier.EXPECT().Verify(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) _, err = o.CalcEligibility(context.Background(), lid, 1, 1, miners[0], types.EmptyVrfSignature) require.NoError(t, err) @@ -264,7 +264,7 @@ func TestCalcEligibility(t *testing.T) { var vrfSig types.VrfSignature copy(vrfSig[:], sig) - o.mBeacon.EXPECT().GetBeacon(lid.GetEpoch()).Return(beacon, nil).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), lid.GetEpoch()).Return(beacon, nil).Times(1) o.mVerifier.EXPECT().Verify(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).Times(1) res, err := o.CalcEligibility(context.Background(), lid, 1, 10, miners[0], vrfSig) require.NoError(t, err, vrf) @@ -301,7 +301,7 @@ func TestCalcEligibilityWithSpaceUnit(t *testing.T) { for _, nodeID := range miners { sig := types.RandomVrfSignature() - o.mBeacon.EXPECT().GetBeacon(lid.GetEpoch()).Return(beacon, nil).Times(2) + o.mBeacon.EXPECT().Beacon(gomock.Any(), lid.GetEpoch()).Return(beacon, nil).Times(2) res, err := o.CalcEligibility(context.Background(), lid, 1, committeeSize, nodeID, sig) require.NoError(t, err) @@ -324,7 +324,7 @@ func BenchmarkOracle_CalcEligibility(b *testing.B) { r := require.New(b) o := defaultOracle(b) - o.mBeacon.EXPECT().GetBeacon(gomock.Any()).Return(types.RandomBeacon(), nil).AnyTimes() + o.mBeacon.EXPECT().Beacon(gomock.Any(), gomock.Any()).Return(types.RandomBeacon(), nil).AnyTimes() o.mVerifier.EXPECT().Verify(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() numOfMiners := 2000 committeeSize := 800 @@ -361,7 +361,7 @@ func Test_VrfSignVerify(t *testing.T) { lid := types.EpochID(5).FirstLayer().Add(confidenceParam) first := types.EpochID(5).FirstLayer() prevEpoch := lid.GetEpoch() - 1 - o.mBeacon.EXPECT().GetBeacon(lid.GetEpoch()).Return(types.Beacon{1, 0, 0, 0}, nil).AnyTimes() + o.mBeacon.EXPECT().Beacon(gomock.Any(), lid.GetEpoch()).Return(types.Beacon{1, 0, 0, 0}, nil).AnyTimes() numMiners := 2 activeSet := types.RandomActiveSet(numMiners) @@ -413,7 +413,7 @@ func Test_Proof_BeaconError(t *testing.T) { layer := types.LayerID(2) errUnknown := errors.New("unknown") - o.mBeacon.EXPECT().GetBeacon(layer.GetEpoch()).Return(types.EmptyBeacon, errUnknown).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), layer.GetEpoch()).Return(types.EmptyBeacon, errUnknown).Times(1) _, err = o.Proof(context.Background(), signer.VRFSigner(), layer, 3) require.ErrorIs(t, err, errUnknown) @@ -422,7 +422,7 @@ func Test_Proof_BeaconError(t *testing.T) { func Test_Proof(t *testing.T) { o := defaultOracle(t) layer := types.LayerID(2) - o.mBeacon.EXPECT().GetBeacon(layer.GetEpoch()).Return(types.Beacon{1, 0, 0, 0}, nil) + o.mBeacon.EXPECT().Beacon(gomock.Any(), layer.GetEpoch()).Return(types.Beacon{1, 0, 0, 0}, nil) signer, err := signing.NewEdSigner() require.NoError(t, err) @@ -450,7 +450,7 @@ func TestOracle_IsIdentityActive(t *testing.T) { func TestBuildVRFMessage_BeaconError(t *testing.T) { o := defaultOracle(t) errUnknown := errors.New("unknown") - o.mBeacon.EXPECT().GetBeacon(gomock.Any()).Return(types.EmptyBeacon, errUnknown).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), gomock.Any()).Return(types.EmptyBeacon, errUnknown).Times(1) msg, err := o.buildVRFMessage(context.Background(), types.LayerID(1), 1) require.ErrorIs(t, err, errUnknown) require.Nil(t, msg) @@ -461,24 +461,24 @@ func TestBuildVRFMessage(t *testing.T) { firstLayer := types.LayerID(1) secondLayer := firstLayer.Add(1) beacon := types.RandomBeacon() - o.mBeacon.EXPECT().GetBeacon(firstLayer.GetEpoch()).Return(beacon, nil).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), firstLayer.GetEpoch()).Return(beacon, nil).Times(1) m1, err := o.buildVRFMessage(context.Background(), firstLayer, 2) require.NoError(t, err) // check not same for different round - o.mBeacon.EXPECT().GetBeacon(firstLayer.GetEpoch()).Return(beacon, nil).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), firstLayer.GetEpoch()).Return(beacon, nil).Times(1) m3, err := o.buildVRFMessage(context.Background(), firstLayer, 3) require.NoError(t, err) require.NotEqual(t, m1, m3) // check not same for different layer - o.mBeacon.EXPECT().GetBeacon(firstLayer.GetEpoch()).Return(beacon, nil).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), firstLayer.GetEpoch()).Return(beacon, nil).Times(1) m4, err := o.buildVRFMessage(context.Background(), secondLayer, 2) require.NoError(t, err) require.NotEqual(t, m1, m4) // check same call returns same result - o.mBeacon.EXPECT().GetBeacon(firstLayer.GetEpoch()).Return(beacon, nil).Times(1) + o.mBeacon.EXPECT().Beacon(gomock.Any(), firstLayer.GetEpoch()).Return(beacon, nil).Times(1) m5, err := o.buildVRFMessage(context.Background(), firstLayer, 2) require.NoError(t, err) require.Equal(t, m1, m5) // check same result @@ -491,7 +491,7 @@ func TestBuildVRFMessage_Concurrency(t *testing.T) { expectAdd := 10 wg := sync.WaitGroup{} firstLayer := types.LayerID(1) - o.mBeacon.EXPECT().GetBeacon(firstLayer.GetEpoch()).Return(types.RandomBeacon(), nil).AnyTimes() + o.mBeacon.EXPECT().Beacon(gomock.Any(), firstLayer.GetEpoch()).Return(types.RandomBeacon(), nil).AnyTimes() for i := 0; i < total; i++ { wg.Add(1) go func(x int) { @@ -557,7 +557,7 @@ func TestActives(t *testing.T) { t.Run("steady state", func(t *testing.T) { numMiners++ o := defaultOracle(t) - o.mBeacon.EXPECT().GetBeacon(gomock.Any()) + o.mBeacon.EXPECT().Beacon(gomock.Any(), gomock.Any()) layer := types.EpochID(4).FirstLayer() o.createLayerData(layer, numMiners) @@ -585,7 +585,7 @@ func TestActives(t *testing.T) { t.Run("use fallback despite block", func(t *testing.T) { numMiners++ o := defaultOracle(t) - o.mBeacon.EXPECT().GetBeacon(gomock.Any()).AnyTimes() + o.mBeacon.EXPECT().Beacon(gomock.Any(), gomock.Any()).AnyTimes() layer := types.EpochID(4).FirstLayer() end := layer.Add(o.cfg.ConfidenceParam) o.createLayerData(layer, numMiners) @@ -610,7 +610,7 @@ func TestActives(t *testing.T) { t.Run("recover at epoch start", func(t *testing.T) { numMiners++ o := defaultOracle(t) - o.mBeacon.EXPECT().GetBeacon(gomock.Any()).AnyTimes() + o.mBeacon.EXPECT().Beacon(gomock.Any(), gomock.Any()).AnyTimes() layer := types.EpochID(4).FirstLayer() old := types.GetEffectiveGenesis() types.SetEffectiveGenesis(layer.Uint32() - 1) @@ -907,9 +907,9 @@ func TestActiveSetMatrix(t *testing.T) { oracle.atxsdata.AddFromAtx(atx, false) } if tc.beacon != types.EmptyBeacon { - oracle.mBeacon.EXPECT().GetBeacon(target).Return(tc.beacon, nil) + oracle.mBeacon.EXPECT().Beacon(gomock.Any(), target).Return(tc.beacon, nil) } else { - oracle.mBeacon.EXPECT().GetBeacon(target).Return(types.EmptyBeacon, sql.ErrNotFound) + oracle.mBeacon.EXPECT().Beacon(gomock.Any(), target).Return(types.EmptyBeacon, sql.ErrNotFound) } rst, err := oracle.ActiveSet(context.TODO(), target) diff --git a/hare3/hare_test.go b/hare3/hare_test.go index 193f7ae0fc..1133d51617 100644 --- a/hare3/hare_test.go +++ b/hare3/hare_test.go @@ -188,10 +188,11 @@ func (n *node) withSyncer() *node { } func (n *node) withOracle() *node { - beaconget := smocks.NewMockBeaconGetter(n.ctrl) - beaconget.EXPECT().GetBeacon(gomock.Any()).DoAndReturn(func(epoch types.EpochID) (types.Beacon, error) { - return beacons.Get(n.db, epoch) - }).AnyTimes() + beaconget := eligibility.NewMockBeaconProvider(n.ctrl) + beaconget.EXPECT().Beacon(gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, epoch types.EpochID) (types.Beacon, error) { + return beacons.Get(n.db, epoch) + }).AnyTimes() n.oracle = eligibility.New( beaconget, n.db, diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index f63b9ed243..638f9af27a 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -2,10 +2,8 @@ package miner import ( "context" - "math/rand" "runtime" "sync" - "time" "go.uber.org/zap" "golang.org/x/exp/maps" @@ -209,11 +207,6 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) continue } - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - rng.Shuffle(len(proposal.TxIDs), func(i, j int) { - proposal.TxIDs[i], proposal.TxIDs[j] = proposal.TxIDs[j], proposal.TxIDs[i] - }) - proposal.EligibilityProofs = eligibilities proposal.Ballot.Signature = signer.signer.Sign(signing.BALLOT, proposal.Ballot.SignedBytes()) proposal.Signature = signer.signer.Sign(signing.PROPOSAL, proposal.SignedBytes()) From 4e90a51bf5f64483d02370c725260db6ef00627c Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:41:35 -0600 Subject: [PATCH 64/71] reuse signer session reinit --- miner/proposal_builder.go | 93 +++++++++++---------------------------- 1 file changed, 26 insertions(+), 67 deletions(-) diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index ce100aa249..6cb9a60634 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -499,50 +499,9 @@ func (pb *ProposalBuilder) initSharedData(ctx context.Context, current types.Lay }) } -func (pb *ProposalBuilder) initSignerData(ss *signerSession, lid types.LayerID) error { - if ss.session.epoch != lid.GetEpoch() { - ss.session = session{epoch: lid.GetEpoch()} - } - if ss.session.atx == types.EmptyATXID { - id, atx := pb.atxsdata.GetByEpochAndNodeID(ss.session.epoch, ss.signer.NodeID()) - if id == types.EmptyATXID { - return errAtxNotAvailable - } - ss.session.atx = id - ss.session.atxWeight = atx.Weight - ss.session.nonce = atx.Nonce - } - if ss.session.prev == 0 { - prev, err := ballots.LastInEpoch(pb.db, ss.session.atx, ss.session.epoch) - if err != nil && !errors.Is(err, sql.ErrNotFound) { - return err - } - if err == nil { - ss.session.prev = prev.Layer - } - } - if ss.session.ref == types.EmptyBallotID { - ballot, err := ballots.FirstInEpoch(pb.db, ss.session.atx, ss.session.epoch) - if err != nil && !errors.Is(err, sql.ErrNotFound) { - return fmt.Errorf("get refballot %w", err) - } - if errors.Is(err, sql.ErrNotFound) { - ss.session.beacon = pb.shared.beacon - ss.session.eligibilities.slots = proposals.MustGetNumEligibleSlots( - ss.session.atxWeight, - minweight.Select(lid.GetEpoch(), pb.cfg.minActiveSetWeight), - pb.shared.active.weight, - pb.cfg.layerSize, - pb.cfg.layersPerEpoch, - ) - } else { - if ballot.EpochData == nil { - return fmt.Errorf("atx %d created invalid first ballot", ss.session.atx) - } - ss.session.ref = ballot.ID() - ss.session.beacon = ballot.EpochData.Beacon - ss.session.eligibilities.slots = ballot.EpochData.EligibilityCount - } +func (pb *ProposalBuilder) initSignerData(ctx context.Context, ss *signerSession, lid types.LayerID) error { + if err := pb.initSignerSessionData(ctx, &ss.session, lid, ss.signer.NodeID()); err != nil { + return fmt.Errorf("init signer session data: %w", err) } if ss.session.eligibilities.proofs == nil { ss.session.eligibilities.proofs = calcEligibilityProofs( @@ -566,41 +525,41 @@ func (pb *ProposalBuilder) initSignerData(ss *signerSession, lid types.LayerID) return nil } -func (pb *ProposalBuilder) initSignerDataFor(ctx context.Context, - ss *signerSession, +func (pb *ProposalBuilder) initSignerSessionData(ctx context.Context, + s *session, lid types.LayerID, nodeID types.NodeID, ) error { - if ss.session.epoch != lid.GetEpoch() { - ss.session = session{epoch: lid.GetEpoch()} + if s.epoch != lid.GetEpoch() { + *s = session{epoch: lid.GetEpoch()} } - if ss.session.atx == types.EmptyATXID { - id, atx := pb.atxsdata.GetByEpochAndNodeID(ss.session.epoch, nodeID) + if s.atx == types.EmptyATXID { + id, atx := pb.atxsdata.GetByEpochAndNodeID(s.epoch, nodeID) if id == types.EmptyATXID { return errAtxNotAvailable } - ss.session.atx = id - ss.session.atxWeight = atx.Weight - ss.session.nonce = atx.Nonce + s.atx = id + s.atxWeight = atx.Weight + s.nonce = atx.Nonce } - if ss.session.prev == 0 { - prev, err := ballots.LastInEpoch(pb.db, ss.session.atx, ss.session.epoch) + if s.prev == 0 { + prev, err := ballots.LastInEpoch(pb.db, s.atx, s.epoch) if err != nil && !errors.Is(err, sql.ErrNotFound) { return err } if err == nil { - ss.session.prev = prev.Layer + s.prev = prev.Layer } } - if ss.session.ref == types.EmptyBallotID { - ballot, err := ballots.FirstInEpoch(pb.db, ss.session.atx, ss.session.epoch) + if s.ref == types.EmptyBallotID { + ballot, err := ballots.FirstInEpoch(pb.db, s.atx, s.epoch) if err != nil && !errors.Is(err, sql.ErrNotFound) { return fmt.Errorf("get refballot %w", err) } if errors.Is(err, sql.ErrNotFound) { - ss.session.beacon = pb.shared.beacon - ss.session.eligibilities.slots = proposals.MustGetNumEligibleSlots( - ss.session.atxWeight, + s.beacon = pb.shared.beacon + s.eligibilities.slots = proposals.MustGetNumEligibleSlots( + s.atxWeight, minweight.Select(lid.GetEpoch(), pb.cfg.minActiveSetWeight), pb.shared.active.weight, pb.cfg.layerSize, @@ -608,11 +567,11 @@ func (pb *ProposalBuilder) initSignerDataFor(ctx context.Context, ) } else { if ballot.EpochData == nil { - return fmt.Errorf("atx %d created invalid first ballot", ss.session.atx) + return fmt.Errorf("atx %d created invalid first ballot", s.atx) } - ss.session.ref = ballot.ID() - ss.session.beacon = ballot.EpochData.Beacon - ss.session.eligibilities.slots = ballot.EpochData.EligibilityCount + s.ref = ballot.ID() + s.beacon = ballot.EpochData.Beacon + s.eligibilities.slots = ballot.EpochData.EligibilityCount } } return nil @@ -652,7 +611,7 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, return nil } - if err := pb.initSignerDataFor(ctx, signer, lid, nodeID); err != nil { + if err := pb.initSignerSessionData(ctx, &signer.session, lid, nodeID); err != nil { if errors.Is(err, errAtxNotAvailable) { pb.logger.Debug("smesher doesn't have atx that targets this epoch", log.ZContext(ctx), @@ -771,7 +730,7 @@ func (pb *ProposalBuilder) build(ctx context.Context, lid types.LayerID) error { start := time.Now() ss.latency.start = buildStartTime - if err := pb.initSignerData(ss, lid); err != nil { + if err := pb.initSignerData(ctx, ss, lid); err != nil { if errors.Is(err, errAtxNotAvailable) { ss.log.Debug("smesher doesn't have atx that targets this epoch", log.ZContext(ctx), From 67ec0356b14d629406daa0e0f1dbcad2b639d3f8 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:36:08 -0600 Subject: [PATCH 65/71] pass locally scoped variable\ --- miner/remote_proposals.go | 61 +++++++++++++++------------------------ 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 638f9af27a..2a369d7e52 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -36,7 +36,6 @@ type RemoteProposalBuilder struct { mu sync.Mutex signers map[types.NodeID]*signerSession } - epochEligibilities map[types.EpochID]map[types.NodeID]map[types.LayerID][]types.VotingEligibility } // New creates a struct of block builder type. @@ -56,12 +55,11 @@ func NewRemoteBuilder( layerSize: layerSize, layersPerEpoch: layersPerEpoch, }, - logger: logger, - clock: clock, - publisher: publisher, - beaconSvc: bcn, - proposalSvc: prop, - epochEligibilities: make(map[types.EpochID]map[types.NodeID]map[types.LayerID][]types.VotingEligibility), + logger: logger, + clock: clock, + publisher: publisher, + beaconSvc: bcn, + proposalSvc: prop, signers: struct { mu sync.Mutex signers map[types.NodeID]*signerSession @@ -90,10 +88,13 @@ func (pb *RemoteProposalBuilder) Register(sig *signing.EdSigner) { // Start the loop that listens to layers and build proposals. func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { - current := pb.clock.CurrentLayer() - next := current + 1 + var ( + eg errgroup.Group + current = pb.clock.CurrentLayer() + next = current + 1 + eligibilities = make(map[types.NodeID]map[types.LayerID][]types.VotingEligibility) + ) pb.logger.Info("started", zap.Inline(&pb.cfg), zap.Uint32("next", next.Uint32())) - var eg errgroup.Group prepareDisabled := pb.cfg.activeSet.Tries == 0 || pb.cfg.activeSet.RetryInterval == 0 if prepareDisabled { pb.logger.Warn("activeset will not be prepared in advance") @@ -118,7 +119,10 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { if current <= types.GetEffectiveGenesis() { continue } - if err := pb.build(ctx, current); err != nil { + if current.FirstInEpoch() { + eligibilities = make(map[types.NodeID]map[types.LayerID][]types.VotingEligibility) + } + if err := pb.build(ctx, current, eligibilities); err != nil { pb.logger.Warn("failed to build proposal", log.ZContext(ctx), zap.Uint32("lid", current.Uint32()), @@ -126,25 +130,15 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { ) } - pb.clean(current) - } - } -} - -func (pb *RemoteProposalBuilder) clean(layer types.LayerID) { - var vals []types.EpochID - lim := layer.GetEpoch() - 1 - for k := range pb.epochEligibilities { - if k <= lim { - vals = append(vals, k) } } - for _, v := range vals { - delete(pb.epochEligibilities, v) - } } -func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) error { +func (pb *RemoteProposalBuilder) build( + ctx context.Context, + layer types.LayerID, + eligibilities map[types.NodeID]map[types.LayerID][]types.VotingEligibility, +) error { epoch := layer.GetEpoch() pb.signers.mu.Lock() signers := maps.Values(pb.signers.signers) @@ -171,11 +165,8 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) ok bool ) if proposal.Ballot.EpochData != nil { - _, ok := pb.epochEligibilities[epoch] - if !ok { - pb.epochEligibilities[epoch] = make(map[types.NodeID]map[types.LayerID][]types.VotingEligibility) - } - nodeElig, ok := pb.epochEligibilities[epoch][nodeId] + + nodeElig, ok := eligibilities[nodeId] if !ok { proofs = calcEligibilityProofs( signer.signer.VRFSigner(), @@ -185,16 +176,12 @@ func (pb *RemoteProposalBuilder) build(ctx context.Context, layer types.LayerID) proposal.Ballot.EpochData.EligibilityCount, pb.cfg.layersPerEpoch, ) - pb.epochEligibilities[epoch][nodeId] = proofs + eligibilities[nodeId] = proofs } else { proofs = nodeElig } } else { - nodeElig, ok := pb.epochEligibilities[epoch] - if !ok { - panic("missing epoch eligibilities") - } - proofs, ok = nodeElig[nodeId] + proofs, ok = eligibilities[nodeId] if !ok { panic("missing node epoch eligibilities") } From 38b8367811abff33807131815d83cbc1fe94aac0 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:49:55 -0600 Subject: [PATCH 66/71] fix key problem --- activation_service_poc/config.standalone.node-service.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index 0d4fea883e..d27f3597d4 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -20,5 +20,10 @@ "main": { "data-folder": "/tmp/spacemesh-node-service", "filelock": "/tmp/spacemesh-node-service/node.lock" + }, + "smeshing": { + "smeshing-opts": { + "smeshing-opts-datadir": "/tmp/spacemesh-node-service/post-data" + } } } From 7db4ad9660ac0e68ea6c24c67d75b88617ce8889 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:32:46 -0600 Subject: [PATCH 67/71] address more pr comments --- .../config.standalone.node-service.json | 6 +-- miner/proposal_builder.go | 39 +++++-------------- miner/remote_proposals.go | 4 +- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/activation_service_poc/config.standalone.node-service.json b/activation_service_poc/config.standalone.node-service.json index d27f3597d4..81dcc343c3 100644 --- a/activation_service_poc/config.standalone.node-service.json +++ b/activation_service_poc/config.standalone.node-service.json @@ -21,9 +21,5 @@ "data-folder": "/tmp/spacemesh-node-service", "filelock": "/tmp/spacemesh-node-service/node.lock" }, - "smeshing": { - "smeshing-opts": { - "smeshing-opts-datadir": "/tmp/spacemesh-node-service/post-data" - } - } + "smeshing-start": false } diff --git a/miner/proposal_builder.go b/miner/proposal_builder.go index 6cb9a60634..e8a5df5d2c 100644 --- a/miner/proposal_builder.go +++ b/miner/proposal_builder.go @@ -586,30 +586,6 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, } signer := &signerSession{} - encodeVotes := func() (*types.Opinion, error) { - pb.tortoise.TallyVotes(lid) - opinion, err := pb.tortoise.EncodeVotes(ctx, tortoise.EncodeVotesWithCurrent(lid)) - if err != nil { - return nil, fmt.Errorf("encoding votes: %w", err) - } - return opinion, nil - } - - calcMeshHash := func() types.Hash32 { - meshHash := pb.decideMeshHash(ctx, lid) - return meshHash - } - - persistActiveSet := func() error { - err := activesets.Add(pb.db, pb.shared.active.id, &types.EpochActiveSet{ - Epoch: pb.shared.epoch, - Set: pb.shared.active.set, - }) - if err != nil && !errors.Is(err, sql.ErrObjectExists) { - return err - } - return nil - } if err := pb.initSignerSessionData(ctx, &signer.session, lid, nodeID); err != nil { if errors.Is(err, errAtxNotAvailable) { @@ -642,16 +618,21 @@ func (pb *ProposalBuilder) BuildFor(ctx context.Context, zap.Int("num proposals", int(proofs)), ) - opinion, err := encodeVotes() + pb.tortoise.TallyVotes(lid) + opinion, err := pb.tortoise.EncodeVotes(ctx, tortoise.EncodeVotesWithCurrent(lid)) if err != nil { - return nil, 0, err + return nil, 0, fmt.Errorf("encoding votes: %w", err) } - meshHash := calcMeshHash() + meshHash := pb.decideMeshHash(ctx, lid) if signer.session.ref == types.EmptyBallotID { - if err := persistActiveSet(); err != nil { - return nil, 0, err + err := activesets.Add(pb.db, pb.shared.active.id, &types.EpochActiveSet{ + Epoch: pb.shared.epoch, + Set: pb.shared.active.set, + }) + if err != nil && !errors.Is(err, sql.ErrObjectExists) { + return nil, 0, fmt.Errorf("persist activeset: %w", err) } } slots := signer.session.eligibilities.slots diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 2a369d7e52..65b59621bc 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -91,6 +91,7 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { var ( eg errgroup.Group current = pb.clock.CurrentLayer() + epoch = current.GetEpoch() next = current + 1 eligibilities = make(map[types.NodeID]map[types.LayerID][]types.VotingEligibility) ) @@ -119,8 +120,9 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { if current <= types.GetEffectiveGenesis() { continue } - if current.FirstInEpoch() { + if e := current.GetEpoch(); e > epoch { eligibilities = make(map[types.NodeID]map[types.LayerID][]types.VotingEligibility) + epoch = e } if err := pb.build(ctx, current, eligibilities); err != nil { pb.logger.Warn("failed to build proposal", From 7da6a88f428a732d70334f62e69f90caf47be8b8 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:37:17 -0600 Subject: [PATCH 68/71] add mocks and tests --- miner/mocks/remote_mocks.go | 145 +++++++++++++++++++++++++++++++++ miner/remote_proposals.go | 2 + miner/remote_proposals_test.go | 114 ++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 miner/mocks/remote_mocks.go create mode 100644 miner/remote_proposals_test.go diff --git a/miner/mocks/remote_mocks.go b/miner/mocks/remote_mocks.go new file mode 100644 index 0000000000..c063425a51 --- /dev/null +++ b/miner/mocks/remote_mocks.go @@ -0,0 +1,145 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./remote_proposals.go +// +// Generated by this command: +// +// mockgen -typed -package=mocks -destination=./mocks/remote_mocks.go -source=./remote_proposals.go +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + types "github.com/spacemeshos/go-spacemesh/common/types" + gomock "go.uber.org/mock/gomock" +) + +// MockproposalService is a mock of proposalService interface. +type MockproposalService struct { + ctrl *gomock.Controller + recorder *MockproposalServiceMockRecorder + isgomock struct{} +} + +// MockproposalServiceMockRecorder is the mock recorder for MockproposalService. +type MockproposalServiceMockRecorder struct { + mock *MockproposalService +} + +// NewMockproposalService creates a new mock instance. +func NewMockproposalService(ctrl *gomock.Controller) *MockproposalService { + mock := &MockproposalService{ctrl: ctrl} + mock.recorder = &MockproposalServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockproposalService) EXPECT() *MockproposalServiceMockRecorder { + return m.recorder +} + +// Proposal mocks base method. +func (m *MockproposalService) Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Proposal", ctx, layer, node) + ret0, _ := ret[0].(*types.Proposal) + ret1, _ := ret[1].(uint64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Proposal indicates an expected call of Proposal. +func (mr *MockproposalServiceMockRecorder) Proposal(ctx, layer, node any) *MockproposalServiceProposalCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Proposal", reflect.TypeOf((*MockproposalService)(nil).Proposal), ctx, layer, node) + return &MockproposalServiceProposalCall{Call: call} +} + +// MockproposalServiceProposalCall wrap *gomock.Call +type MockproposalServiceProposalCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockproposalServiceProposalCall) Return(arg0 *types.Proposal, arg1 uint64, arg2 error) *MockproposalServiceProposalCall { + c.Call = c.Call.Return(arg0, arg1, arg2) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockproposalServiceProposalCall) Do(f func(context.Context, types.LayerID, types.NodeID) (*types.Proposal, uint64, error)) *MockproposalServiceProposalCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockproposalServiceProposalCall) DoAndReturn(f func(context.Context, types.LayerID, types.NodeID) (*types.Proposal, uint64, error)) *MockproposalServiceProposalCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockbeaconService is a mock of beaconService interface. +type MockbeaconService struct { + ctrl *gomock.Controller + recorder *MockbeaconServiceMockRecorder + isgomock struct{} +} + +// MockbeaconServiceMockRecorder is the mock recorder for MockbeaconService. +type MockbeaconServiceMockRecorder struct { + mock *MockbeaconService +} + +// NewMockbeaconService creates a new mock instance. +func NewMockbeaconService(ctrl *gomock.Controller) *MockbeaconService { + mock := &MockbeaconService{ctrl: ctrl} + mock.recorder = &MockbeaconServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockbeaconService) EXPECT() *MockbeaconServiceMockRecorder { + return m.recorder +} + +// Beacon mocks base method. +func (m *MockbeaconService) Beacon(ctx context.Context, epoch types.EpochID) (types.Beacon, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Beacon", ctx, epoch) + ret0, _ := ret[0].(types.Beacon) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Beacon indicates an expected call of Beacon. +func (mr *MockbeaconServiceMockRecorder) Beacon(ctx, epoch any) *MockbeaconServiceBeaconCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Beacon", reflect.TypeOf((*MockbeaconService)(nil).Beacon), ctx, epoch) + return &MockbeaconServiceBeaconCall{Call: call} +} + +// MockbeaconServiceBeaconCall wrap *gomock.Call +type MockbeaconServiceBeaconCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockbeaconServiceBeaconCall) Return(arg0 types.Beacon, arg1 error) *MockbeaconServiceBeaconCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockbeaconServiceBeaconCall) Do(f func(context.Context, types.EpochID) (types.Beacon, error)) *MockbeaconServiceBeaconCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockbeaconServiceBeaconCall) DoAndReturn(f func(context.Context, types.EpochID) (types.Beacon, error)) *MockbeaconServiceBeaconCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 65b59621bc..35667610a6 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -16,6 +16,8 @@ import ( "github.com/spacemeshos/go-spacemesh/signing" ) +//go:generate mockgen -typed -package=mocks -destination=./mocks/remote_mocks.go -source=./remote_proposals.go + type proposalService interface { Proposal(ctx context.Context, layer types.LayerID, node types.NodeID) (*types.Proposal, uint64, error) } diff --git a/miner/remote_proposals_test.go b/miner/remote_proposals_test.go new file mode 100644 index 0000000000..689b7d6a7d --- /dev/null +++ b/miner/remote_proposals_test.go @@ -0,0 +1,114 @@ +package miner + +import ( + "context" + "math/rand" + "testing" + "time" + + "github.com/spacemeshos/go-spacemesh/common/types" + "github.com/spacemeshos/go-spacemesh/miner/mocks" + pmocks "github.com/spacemeshos/go-spacemesh/p2p/pubsub/mocks" + "github.com/spacemeshos/go-spacemesh/signing" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "go.uber.org/zap/zaptest" +) + +func TestRemoteProposals(t *testing.T) { + signers := make([]*signing.EdSigner, 4) + rng := rand.New(rand.NewSource(10101)) + for i := range signers { + signer, err := signing.NewEdSigner(signing.WithKeyFromRand(rng)) + require.NoError(t, err) + signers[i] = signer + } + var ( + ctx, cancel = context.WithCancel(context.Background()) + ctrl = gomock.NewController(t) + clock = mocks.NewMocklayerClock(ctrl) + publisher = pmocks.NewMockPublisher(ctrl) + beacon = mocks.NewMockbeaconService(ctrl) + prop = mocks.NewMockproposalService(ctrl) + beaconVal = types.Beacon{1} + ) + + clock.EXPECT().LayerToTime(gomock.Any()).Return(time.Unix(0, 0)).AnyTimes() + + builder := NewRemoteBuilder(clock, publisher, beacon, prop, 5, layersPerEpoch, zaptest.NewLogger(t)) + for _, signer := range signers { + builder.Register(signer) + } + var ( + activeSet = types.ATXIDList{types.ATXID{1}} + lid = types.LayerID(1) + meshHash = types.Hash32{11} + atxId = types.ATXID{1} + txIds = []types.TransactionID{{1}} + nrTicks = 10 + ticks = make(chan struct{}, nrTicks) + closed = make(chan struct{}) + done = make(chan struct{}) + ) + close(closed) + clock.EXPECT().CurrentLayer().DoAndReturn(func() types.LayerID { + return lid + }).AnyTimes() + clock.EXPECT().AwaitLayer(gomock.Any()).DoAndReturn(func(l types.LayerID) <-chan struct{} { + lid = l + select { + case ticks <- struct{}{}: + return closed + default: + close(done) + return make(chan struct{}) + } + }).AnyTimes() + beacon.EXPECT().Beacon(gomock.Any(), gomock.Any()).Return(types.Beacon{1}, nil).AnyTimes() + publisher.EXPECT().Publish(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(2) + prop.EXPECT().Proposal(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, lid types.LayerID, nodeId types.NodeID) (*types.Proposal, uint64, error) { + prop := createTestProposal(t, activeSet, lid, meshHash, atxId, nodeId, txIds, beaconVal, 1) + return prop, 11, nil + }).AnyTimes() + go builder.Run(ctx) + defer cancel() + select { + case <-done: + case <-time.After(10 * time.Second): + t.Fatal("test timeout") + } +} + +func createTestProposal( + tb testing.TB, + activeSet types.ATXIDList, + lid types.LayerID, + meshHash types.Hash32, + atxID types.ATXID, + nodeId types.NodeID, + txIDs []types.TransactionID, + beacon types.Beacon, + numEligibility int, +) *types.Proposal { + tb.Helper() + p := &types.Proposal{ + InnerProposal: types.InnerProposal{ + Ballot: types.Ballot{ + InnerBallot: types.InnerBallot{ + Layer: lid, + AtxID: atxID, + EpochData: &types.EpochData{ + Beacon: beacon, + EligibilityCount: uint32(numEligibility), + ActiveSetHash: activeSet.Hash(), + }, + }, + }, + TxIDs: txIDs, + MeshHash: meshHash, + }, + } + p.SmesherID = nodeId + return p +} From a5e19830a6405de54896c1dc3991dbb621214275 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:57:45 -0600 Subject: [PATCH 69/71] add in-epoch beacon caching --- miner/remote_proposals.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/miner/remote_proposals.go b/miner/remote_proposals.go index 35667610a6..e792ce3fbc 100644 --- a/miner/remote_proposals.go +++ b/miner/remote_proposals.go @@ -96,6 +96,7 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { epoch = current.GetEpoch() next = current + 1 eligibilities = make(map[types.NodeID]map[types.LayerID][]types.VotingEligibility) + epochBeacon = make(map[types.EpochID]types.Beacon) // cache beacon values within an epoch ) pb.logger.Info("started", zap.Inline(&pb.cfg), zap.Uint32("next", next.Uint32())) prepareDisabled := pb.cfg.activeSet.Tries == 0 || pb.cfg.activeSet.RetryInterval == 0 @@ -118,15 +119,15 @@ func (pb *RemoteProposalBuilder) Run(ctx context.Context) error { } next = current.Add(1) ctx := log.WithNewSessionID(ctx) - if current <= types.GetEffectiveGenesis() { continue } if e := current.GetEpoch(); e > epoch { eligibilities = make(map[types.NodeID]map[types.LayerID][]types.VotingEligibility) + epochBeacon = make(map[types.EpochID]types.Beacon) epoch = e } - if err := pb.build(ctx, current, eligibilities); err != nil { + if err := pb.build(ctx, current, eligibilities, epochBeacon); err != nil { pb.logger.Warn("failed to build proposal", log.ZContext(ctx), zap.Uint32("lid", current.Uint32()), @@ -142,6 +143,7 @@ func (pb *RemoteProposalBuilder) build( ctx context.Context, layer types.LayerID, eligibilities map[types.NodeID]map[types.LayerID][]types.VotingEligibility, + beacons map[types.EpochID]types.Beacon, ) error { epoch := layer.GetEpoch() pb.signers.mu.Lock() @@ -159,17 +161,19 @@ func (pb *RemoteProposalBuilder) build( pb.logger.Info("node not eligible on this layer. will try later") continue } - bcn, err := pb.beaconSvc.Beacon(ctx, epoch) - if err != nil { - pb.logger.Error("get beacon", zap.Error(err)) - continue + + bcn, ok := beacons[epoch] + if !ok { + bcn, err = pb.beaconSvc.Beacon(ctx, epoch) + if err != nil { + pb.logger.Error("get beacon", zap.Error(err)) + continue + } + beacons[epoch] = bcn } - var ( - proofs map[types.LayerID][]types.VotingEligibility - ok bool - ) - if proposal.Ballot.EpochData != nil { + var proofs map[types.LayerID][]types.VotingEligibility + if proposal.Ballot.EpochData != nil { nodeElig, ok := eligibilities[nodeId] if !ok { proofs = calcEligibilityProofs( From 3b71d57f7beae31e06f589c9a11ebbb9c1f1ec71 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:59:41 -0600 Subject: [PATCH 70/71] check nil --- node/node.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/node/node.go b/node/node.go index b7283c2472..23caf2a7c7 100644 --- a/node/node.go +++ b/node/node.go @@ -1483,7 +1483,10 @@ func (app *App) listenToUpdates(ctx context.Context) { } app.hOracle.UpdateActiveSet(epoch, set) - app.proposalBuilder.UpdateActiveSet(epoch, set) + + if app.proposalBuilder != nil { + app.proposalBuilder.UpdateActiveSet(epoch, set) + } app.eg.Go(func() error { select { From a4e998394be0d1763cd0070a4fd921adaf519f36 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:48:01 -0600 Subject: [PATCH 71/71] lint --- miner/remote_proposals_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/miner/remote_proposals_test.go b/miner/remote_proposals_test.go index 689b7d6a7d..b4ef5f99d6 100644 --- a/miner/remote_proposals_test.go +++ b/miner/remote_proposals_test.go @@ -6,13 +6,14 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "go.uber.org/zap/zaptest" + "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/miner/mocks" pmocks "github.com/spacemeshos/go-spacemesh/p2p/pubsub/mocks" "github.com/spacemeshos/go-spacemesh/signing" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "go.uber.org/zap/zaptest" ) func TestRemoteProposals(t *testing.T) {