From 50c6e8888810cf340d4a2e40f64e7e60d141c270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rebonato?= Date: Thu, 16 Jan 2025 11:41:54 -0300 Subject: [PATCH 1/3] started answerHypotheticalMembershipRequest --- .../prospective-parachains/messages.go | 1 + .../prospective-parachains.go | 74 +++++++++++++++++++ dot/parachain/types/overseer_message.go | 38 ++++++++++ 3 files changed, 113 insertions(+) diff --git a/dot/parachain/prospective-parachains/messages.go b/dot/parachain/prospective-parachains/messages.go index acc9a8e079..9b03543df7 100644 --- a/dot/parachain/prospective-parachains/messages.go +++ b/dot/parachain/prospective-parachains/messages.go @@ -89,6 +89,7 @@ type HypotheticalMembershipResponseItem struct { // HypotheticalMembershipRequest Request specifying which candidates are either already included // or might become included in fragment chain under a given active leaf (or any active leaf if // `FragmentChainRelayParent` is `nil`). + type HypotheticalMembershipRequest struct { // Candidates, in arbitrary order, which should be checked for // hypothetical/actual membership in fragment chains. diff --git a/dot/parachain/prospective-parachains/prospective-parachains.go b/dot/parachain/prospective-parachains/prospective-parachains.go index 13c0a396c3..eca19da86d 100644 --- a/dot/parachain/prospective-parachains/prospective-parachains.go +++ b/dot/parachain/prospective-parachains/prospective-parachains.go @@ -11,6 +11,11 @@ import ( var logger = log.NewFromGlobal(log.AddContext("pkg", "prospective_parachains"), log.SetLevel(log.Debug)) +type HypotheticalCandidateMembership struct { + Candidate parachaintypes.HypotheticalCandidate + Membership HypotheticalMembership +} + type ProspectiveParachains struct { SubsystemToOverseer chan<- any View *view @@ -53,6 +58,75 @@ func (pp *ProspectiveParachains) Run(ctx context.Context, overseerToSubsystem <- } } +func (pp *ProspectiveParachains) answerHypotheticalMembershipRequest( + view *view, + request HypotheticalMembershipRequest, + tx chan []HypotheticalCandidateMembership, + metrics *Metrics, +) { + timer := metrics.timeHypotheticalMembershipRequest() + defer timer.Stop() + + response := make([]HypotheticalCandidateMembership, 0, len(request.Candidates)) + for _, candidate := range request.Candidates { + response = append(response, HypotheticalCandidateMembership{Candidate: candidate, Membership: []common.Hash{}}) + } + + requiredActiveLeaf := request.FragmentChainRelayParent + for activeLeaf := range view.activeLeaves { + if requiredActiveLeaf != nil && *requiredActiveLeaf != activeLeaf { + continue + } + + leafView, found := view.perRelayParent[activeLeaf] + if !found { + continue + } + + for i := range response { + candidate := &response[i].Candidate + membership := &response[i].Membership + + paraID := (*candidate).CandidatePara() + fragmentChain, found := leafView.fragmentChains[paraID] + if !found { + continue + } + + candidateHash := (*candidate).GetCandidateHash() + + candidateEntry, err := newCandidateEntry( + candidateHash, + (*candidate).GetCommittedCandidateReceipt(), + (*candidate).GetPersistedValidationData(), + seconded, + ) + + if err != nil { + logger.Debugf( + "Candidate is not a hypothetical member: %v, para: %v, leaf: %v, candidate: %v", + err, paraID, activeLeaf, candidateHash, + ) + continue + } + + err = fragmentChain.canAddCandidateAsPotential(candidateEntry) + + switch err { + case nil, errCandidateAlreadyKnown: + *membership = append(*membership, activeLeaf) + default: + logger.Debugf( + "Candidate is not a hypothetical member: %v, para: %v, leaf: %v, candidate: %v", + err, paraID, activeLeaf, candidateHash, + ) + } + } + } + + tx <- response +} + func (*ProspectiveParachains) Stop() {} func (pp *ProspectiveParachains) processMessage(msg any) { diff --git a/dot/parachain/types/overseer_message.go b/dot/parachain/types/overseer_message.go index 327942f202..562eecac54 100644 --- a/dot/parachain/types/overseer_message.go +++ b/dot/parachain/types/overseer_message.go @@ -111,6 +111,10 @@ type FragmentTreeMembership struct { // would have and are evaluated less strictly. type HypotheticalCandidate interface { isHypotheticalCandidate() + CandidatePara() ParaID + GetCandidateHash() CandidateHash + GetCommittedCandidateReceipt() CommittedCandidateReceipt + GetPersistedValidationData() PersistedValidationData } // HypotheticalCandidateIncomplete represents an incomplete hypothetical candidate. @@ -126,6 +130,23 @@ type HypotheticalCandidateIncomplete struct { RelayParent common.Hash } +// / Get the `ParaId` of the hypothetical candidate. +func (c HypotheticalCandidateIncomplete) CandidatePara() ParaID { + return c.CandidateParaID +} + +func (c HypotheticalCandidateIncomplete) GetCandidateHash() CandidateHash { + return c.CandidateHash +} + +func (c HypotheticalCandidateIncomplete) GetCommittedCandidateReceipt() CommittedCandidateReceipt { + return CommittedCandidateReceipt{} +} + +func (c HypotheticalCandidateIncomplete) GetPersistedValidationData() PersistedValidationData { + return PersistedValidationData{} +} + func (HypotheticalCandidateIncomplete) isHypotheticalCandidate() {} // HypotheticalCandidateComplete represents a complete candidate, including its hash, committed candidate receipt, @@ -138,6 +159,23 @@ type HypotheticalCandidateComplete struct { func (HypotheticalCandidateComplete) isHypotheticalCandidate() {} +// / Get the `ParaId` of the hypothetical candidate. +func (c HypotheticalCandidateComplete) CandidatePara() ParaID { + return c.CommittedCandidateReceipt.Descriptor.ParaID +} + +func (c HypotheticalCandidateComplete) GetCandidateHash() CandidateHash { + return c.CandidateHash +} + +func (c HypotheticalCandidateComplete) GetCommittedCandidateReceipt() CommittedCandidateReceipt { + return c.CommittedCandidateReceipt +} + +func (c HypotheticalCandidateComplete) GetPersistedValidationData() PersistedValidationData { + return c.PersistedValidationData +} + // AvailabilityDistributionMessageFetchPoV represents a message instructing // availability distribution to fetch a remote Proof of Validity (PoV). type AvailabilityDistributionMessageFetchPoV struct { From 1f9a5836a357839d2e26a5c0a60f81bd7ddb5e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rebonato?= Date: Thu, 16 Jan 2025 12:04:15 -0300 Subject: [PATCH 2/3] remove metrics --- .../prospective-parachains/prospective-parachains.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dot/parachain/prospective-parachains/prospective-parachains.go b/dot/parachain/prospective-parachains/prospective-parachains.go index eca19da86d..bc2417ab50 100644 --- a/dot/parachain/prospective-parachains/prospective-parachains.go +++ b/dot/parachain/prospective-parachains/prospective-parachains.go @@ -62,11 +62,7 @@ func (pp *ProspectiveParachains) answerHypotheticalMembershipRequest( view *view, request HypotheticalMembershipRequest, tx chan []HypotheticalCandidateMembership, - metrics *Metrics, ) { - timer := metrics.timeHypotheticalMembershipRequest() - defer timer.Stop() - response := make([]HypotheticalCandidateMembership, 0, len(request.Candidates)) for _, candidate := range request.Candidates { response = append(response, HypotheticalCandidateMembership{Candidate: candidate, Membership: []common.Hash{}}) From d1968bd7925046a9d76839c44a4c7de589a4a143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Rebonato?= Date: Tue, 21 Jan 2025 16:20:36 -0300 Subject: [PATCH 3/3] started tests --- .../prospective_parachains_test.go | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/dot/parachain/prospective-parachains/prospective_parachains_test.go b/dot/parachain/prospective-parachains/prospective_parachains_test.go index 8b5b8bb7d0..a21eaece92 100644 --- a/dot/parachain/prospective-parachains/prospective_parachains_test.go +++ b/dot/parachain/prospective-parachains/prospective_parachains_test.go @@ -461,3 +461,89 @@ func TestGetBackableCandidates(t *testing.T) { }) } } + +func TestFailedMatchAnswerHypotheticalMembershipRequest(t *testing.T) { + candidateRelayParent1 := common.Hash{0x01} + parentHead1 := parachaintypes.HeadData{Data: bytes.Repeat([]byte{0x01}, 32)} + headData1 := parachaintypes.HeadData{Data: bytes.Repeat([]byte{0x01}, 32)} + + candidateRelayParent2 := common.Hash{0x02} + parentHead2 := parachaintypes.HeadData{Data: bytes.Repeat([]byte{0x02}, 32)} + headData2 := parachaintypes.HeadData{Data: bytes.Repeat([]byte{0x02}, 32)} + + validationCodeHash := parachaintypes.ValidationCodeHash{} + + candidate1Receipt := makeCandidate( + candidateRelayParent1, + uint32(10), + parachaintypes.ParaID(1), + parentHead1, + headData1, + validationCodeHash, + ) + + candidate1Hash, err := candidate1Receipt.Hash() + + if err != nil { + panic(err) + } + + candidate2Receipt := makeCandidate( + candidateRelayParent2, + uint32(9), + parachaintypes.ParaID(2), + parentHead2, + headData2, + validationCodeHash, + ) + + candidate2Hash, err := candidate2Receipt.Hash() + + if err != nil { + panic(err) + } + + // Mock data + candidate1 := ¶chaintypes.HypotheticalCandidateComplete{ + CandidateHash: parachaintypes.CandidateHash{Value: candidate1Hash}, + CommittedCandidateReceipt: candidate1Receipt, + PersistedValidationData: dummyPVD(parentHead1, 0), + } + candidate2 := ¶chaintypes.HypotheticalCandidateComplete{ + CandidateHash: parachaintypes.CandidateHash{Value: candidate2Hash}, + CommittedCandidateReceipt: candidate2Receipt, + PersistedValidationData: dummyPVD(parentHead2, 0), + } + + request := HypotheticalMembershipRequest{ + Candidates: []parachaintypes.HypotheticalCandidate{candidate1, candidate2}, + } + + activeLeaf := common.Hash{0x01} + view := &view{ + activeLeaves: map[common.Hash]bool{ + activeLeaf: true, + }, + perRelayParent: map[common.Hash]*relayParentData{ + activeLeaf: { + fragmentChains: map[parachaintypes.ParaID]*fragmentChain{ + candidate1.CandidatePara(): {}, + candidate2.CandidatePara(): {}, + }, + }, + }, + } + + tx := make(chan []HypotheticalCandidateMembership, 1) + + pp := &ProspectiveParachains{} + pp.answerHypotheticalMembershipRequest(view, request, tx) + + response := <-tx + + assert.Len(t, response, 2) + assert.Equal(t, candidate1, response[0].Candidate) + assert.Equal(t, candidate2, response[1].Candidate) + assert.Contains(t, response[0].Membership, activeLeaf) + assert.Contains(t, response[1].Membership, activeLeaf) +}