Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/answer hypothetical membership #4493

Draft
wants to merge 3 commits into
base: feat/parachain
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dot/parachain/prospective-parachains/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
70 changes: 70 additions & 0 deletions dot/parachain/prospective-parachains/prospective-parachains.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -53,6 +58,71 @@ func (pp *ProspectiveParachains) Run(ctx context.Context, overseerToSubsystem <-
}
}

func (pp *ProspectiveParachains) answerHypotheticalMembershipRequest(
view *view,
request HypotheticalMembershipRequest,
tx chan []HypotheticalCandidateMembership,
) {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 := &parachaintypes.HypotheticalCandidateComplete{
CandidateHash: parachaintypes.CandidateHash{Value: candidate1Hash},
CommittedCandidateReceipt: candidate1Receipt,
PersistedValidationData: dummyPVD(parentHead1, 0),
}
candidate2 := &parachaintypes.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)
}
38 changes: 38 additions & 0 deletions dot/parachain/types/overseer_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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 {
Expand Down