diff --git a/dot/parachain/prospective-parachains/fragment-chain/fragment_chain.go b/dot/parachain/prospective-parachains/fragment-chain/fragment_chain.go index e06816cc7b..48045bdbe6 100644 --- a/dot/parachain/prospective-parachains/fragment-chain/fragment_chain.go +++ b/dot/parachain/prospective-parachains/fragment-chain/fragment_chain.go @@ -1013,7 +1013,7 @@ func (f *FragmentChain) populateChain(storage *CandidateStorage) { return } - for len(f.bestChain.chain) > int(f.scope.maxDepth) { + for len(f.bestChain.chain) < int(f.scope.maxDepth) { childConstraints, err := f.scope.baseConstraints.ApplyModifications(cumulativeModifications) if err != nil { @@ -1121,7 +1121,6 @@ func (f *FragmentChain) populateChain(storage *CandidateStorage) { // update the cumulative constraint modifications cumulativeModifications.Stack(bestCandidate.fragment.ConstraintModifications()) - // update the earliest relay parent earliestRelayParent = &inclusionemulator.RelayChainBlockInfo{ Hash: bestCandidate.fragment.RelayParent().Hash, diff --git a/dot/parachain/prospective-parachains/prospective-parachains.go b/dot/parachain/prospective-parachains/prospective-parachains.go deleted file mode 100644 index effe9f02cb..0000000000 --- a/dot/parachain/prospective-parachains/prospective-parachains.go +++ /dev/null @@ -1,82 +0,0 @@ -package prospectiveparachains - -import ( - "context" - "errors" - - parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" - "github.com/ChainSafe/gossamer/internal/log" -) - -var logger = log.NewFromGlobal(log.AddContext("pkg", "prospective_parachains"), log.SetLevel(log.Debug)) - -type ProspectiveParachains struct { - SubsystemToOverseer chan<- any -} - -// Name returns the name of the subsystem -func (*ProspectiveParachains) Name() parachaintypes.SubSystemName { - return parachaintypes.ProspectiveParachains -} - -// NewProspectiveParachains creates a new ProspectiveParachain subsystem -func NewProspectiveParachains(overseerChan chan<- any) *ProspectiveParachains { - prospectiveParachain := ProspectiveParachains{ - SubsystemToOverseer: overseerChan, - } - return &prospectiveParachain -} - -// Run starts the ProspectiveParachains subsystem -func (pp *ProspectiveParachains) Run(ctx context.Context, overseerToSubsystem <-chan any) { - for { - select { - case msg := <-overseerToSubsystem: - pp.processMessage(msg) - case <-ctx.Done(): - if err := ctx.Err(); err != nil && !errors.Is(err, context.Canceled) { - logger.Errorf("ctx error: %s\n", err) - } - return - } - } -} - -func (*ProspectiveParachains) Stop() {} - -func (pp *ProspectiveParachains) processMessage(msg any) { - switch msg := msg.(type) { - case parachaintypes.Conclude: - pp.Stop() - case parachaintypes.ActiveLeavesUpdateSignal: - _ = pp.ProcessActiveLeavesUpdateSignal(msg) - case parachaintypes.BlockFinalizedSignal: - _ = pp.ProcessBlockFinalizedSignal(msg) - case IntroduceSecondedCandidate: - panic("not implemented yet: see issue #4308") - case CandidateBacked: - panic("not implemented yet: see issue #4309") - case GetBackableCandidates: - panic("not implemented yet: see issue #4310") - case GetHypotheticalMembership: - panic("not implemented yet: see issue #4311") - case GetMinimumRelayParents: - panic("not implemented yet: see issue #4312") - case GetProspectiveValidationData: - panic("not implemented yet: see issue #4313") - default: - logger.Errorf("%w: %T", parachaintypes.ErrUnknownOverseerMessage, msg) - } - -} - -// ProcessActiveLeavesUpdateSignal processes active leaves update signal -func (pp *ProspectiveParachains) ProcessActiveLeavesUpdateSignal(parachaintypes.ActiveLeavesUpdateSignal) error { - panic("not implemented yet: see issue #4305") -} - -// ProcessBlockFinalizedSignal processes block finalized signal -func (*ProspectiveParachains) ProcessBlockFinalizedSignal(parachaintypes.BlockFinalizedSignal) error { - // NOTE: this subsystem does not process block finalized signal - return nil -} diff --git a/dot/parachain/prospective-parachains/prospective_parachains.go b/dot/parachain/prospective-parachains/prospective_parachains.go new file mode 100644 index 0000000000..7a39696012 --- /dev/null +++ b/dot/parachain/prospective-parachains/prospective_parachains.go @@ -0,0 +1,264 @@ +package prospectiveparachains + +import ( + "context" + "errors" + + "github.com/ChainSafe/gossamer/dot/parachain/backing" + fragmentchain "github.com/ChainSafe/gossamer/dot/parachain/prospective-parachains/fragment-chain" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + "github.com/ChainSafe/gossamer/internal/log" + common "github.com/ChainSafe/gossamer/lib/common" +) + +var logger = log.NewFromGlobal(log.AddContext("pkg", "prospective_parachains"), log.SetLevel(log.Debug)) + +type RelayBlockViewData struct { + // The fragment chains for current and upcoming scheduled paras. + FragmentChains map[parachaintypes.ParaID]fragmentchain.FragmentChain +} + +type View struct { + // Per relay parent fragment chains. These includes all relay parents under the implicit view. + PerRelayParent map[common.Hash]RelayBlockViewData + // The hashes of the currently active leaves. This is a subset of the keys in + // `per_relay_parent`. + ActiveLeaves map[common.Hash]struct{} + // The backing implicit view. + ImplicitView backing.ImplicitView +} + +// Initialize with empty values. +func NewView() *View { + return &View{ + PerRelayParent: make(map[common.Hash]RelayBlockViewData), + ActiveLeaves: make(map[common.Hash]struct{}), + ImplicitView: nil, // TODO: currently there's no implementation for ImplicitView, reference is: https://github.com/paritytech/polkadot-sdk/blob/028e61be43f05f6f6c88c5cca94160f8db075585/polkadot/node/subsystem-util/src/backing_implicit_view.rs#L40 + } +} + +// Get the fragment chains of this leaf. +func (v *View) GetFragmentChains(leaf common.Hash) map[parachaintypes.ParaID]fragmentchain.FragmentChain { + if viewData, ok := v.PerRelayParent[leaf]; ok { + return viewData.FragmentChains + } + return nil +} + +func HandleIntroduceSecondedCandidate( + view *View, + request IntroduceSecondedCandidateRequest, + response chan bool, +) { + para := request.CandidateParaID + candidate := request.CandidateReceipt + pvd := request.PersistedValidationData + + hash, err := candidate.Hash() + + if err != nil { + logger.Tracef("Failed to get candidate hash: %s", err.Error()) + response <- false + return + } + + candidateHash := parachaintypes.CandidateHash{Value: hash} + + entry, err := fragmentchain.NewCandidateEntry( + candidateHash, + candidate, + pvd, + fragmentchain.Seconded, + ) + + if err != nil { + logger.Tracef("Failed to add seconded candidate error: %s para: %v", err.Error(), para) + response <- false + return + } + + added := make([]common.Hash, 0, len(view.PerRelayParent)) + paraScheduled := false + + for relayParent, rpData := range view.PerRelayParent { + chain, exists := rpData.FragmentChains[para] + + if !exists { + continue + } + + _, isActiveLeaf := view.ActiveLeaves[relayParent] + + paraScheduled = true + + err = chain.TryAddingSecondedCandidate(entry) + + if err != nil { + if errors.Is(err, fragmentchain.ErrCandidateAlradyKnown) { + logger.Tracef( + "Attempting to introduce an already known candidate with hash: %s, para: %v relayParent: %v isActiveLeaf: %v", + candidateHash, + para, + relayParent, + isActiveLeaf, + ) + added = append(added, relayParent) + } else { + logger.Tracef( + "Failed to add seconded candidate with hash: %s error: %s para: %v relayParent: %v isActiveLeaf: %v", + candidateHash, + err.Error(), + para, + relayParent, + isActiveLeaf, + ) + } + } else { + added = append(added, relayParent) + } + } + + if !paraScheduled { + logger.Warnf( + "Received seconded candidate with hash: %s for inactive para: %v", + candidateHash, + para, + ) + } + + if len(added) == 0 { + logger.Debugf("Newly-seconded candidate cannot be kept under any relay parent: %s", candidateHash) + } else { + logger.Debugf("Added seconded candidate to %d relay parents: %s", len(added), candidateHash) + } + + response <- len(added) > 0 +} + +type ProspectiveParachains struct { + SubsystemToOverseer chan<- any + View *View +} + +// Name returns the name of the subsystem +func (*ProspectiveParachains) Name() parachaintypes.SubSystemName { + return parachaintypes.ProspectiveParachains +} + +// NewProspectiveParachains creates a new ProspectiveParachain subsystem +func NewProspectiveParachains(overseerChan chan<- any) *ProspectiveParachains { + prospectiveParachain := ProspectiveParachains{ + SubsystemToOverseer: overseerChan, + View: NewView(), + } + return &prospectiveParachain +} + +// Run starts the ProspectiveParachains subsystem +func (pp *ProspectiveParachains) Run(ctx context.Context, overseerToSubsystem <-chan any) { + for { + select { + case msg := <-overseerToSubsystem: + shouldFinish := pp.processMessage(msg) + if shouldFinish { + return + } + case <-ctx.Done(): + if err := ctx.Err(); err != nil && !errors.Is(err, context.Canceled) { + logger.Errorf("ctx error: %s\n", err) + } + return + } + } +} + +func (pp *ProspectiveParachains) Stop() { + close(pp.SubsystemToOverseer) +} + +func (pp *ProspectiveParachains) processMessage(msg any) bool { + switch msg := msg.(type) { + case parachaintypes.Conclude: + pp.Stop() + return true + case parachaintypes.ActiveLeavesUpdateSignal: + _ = pp.ProcessActiveLeavesUpdateSignal(msg) + case parachaintypes.BlockFinalizedSignal: + _ = pp.ProcessBlockFinalizedSignal(msg) + case IntroduceSecondedCandidate: + HandleIntroduceSecondedCandidate( + pp.View, + msg.IntroduceSecondedCandidateRequest, + msg.Response, + ) + case CandidateBacked: + pp.HandleCandidateBacked(*pp.View, msg.ParaId, msg.CandidateHash) + case GetBackableCandidates: + panic("not implemented yet: see issue #4310") + case GetHypotheticalMembership: + panic("not implemented yet: see issue #4311") + case GetMinimumRelayParents: + panic("not implemented yet: see issue #4312") + case GetProspectiveValidationData: + panic("not implemented yet: see issue #4313") + default: + logger.Errorf("%w: %T", parachaintypes.ErrUnknownOverseerMessage, msg) + } + + return false +} + +// ProcessActiveLeavesUpdateSignal processes active leaves update signal +func (pp *ProspectiveParachains) ProcessActiveLeavesUpdateSignal(parachaintypes.ActiveLeavesUpdateSignal) error { + panic("not implemented yet: see issue #4305") +} + +// ProcessBlockFinalizedSignal processes block finalized signal +func (*ProspectiveParachains) ProcessBlockFinalizedSignal(parachaintypes.BlockFinalizedSignal) error { + // NOTE: this subsystem does not process block finalized signal + return nil +} + +func (*ProspectiveParachains) HandleCandidateBacked( + view View, + para parachaintypes.ParaID, + candidateHash parachaintypes.CandidateHash, + // metrics # to do +) { + foundCandidate := false + foundPara := false + + for relayParent, rpData := range view.PerRelayParent { + chain, ok := rpData.FragmentChains[para] + if !ok { + continue + } + + _, isActiveLeaf := view.ActiveLeaves[relayParent] + + foundPara = true + if chain.IsCandidateBacked(candidateHash) { + logger.Debugf("para = %s, candidateHash = %s, isActiveLeaf = %s, Received redundant instruction to mark as backed an already backed candidate", para, candidateHash, isActiveLeaf) + foundCandidate = true + } else if chain.ContainsUnconnectedCandidate(candidateHash) { + foundCandidate = true + chain.CandidateBacked(candidateHash) + + candidatedHashes := []parachaintypes.CandidateHash{} + for candidate := range chain.Unconnected() { + candidatedHashes = append(candidatedHashes, candidate.Hash()) + } + + logger.Tracef("relayParent = %s, para = %s, candidateHash = %s, isActiveLeaf = %s, Candidate backed. Candidate chain for para: %v", relayParent, para, candidateHash, isActiveLeaf, chain.BestChainVec()) + logger.Tracef("relayParent = %s, para = %s, candidateHash = %s, isActiveLeaf = %s, Potential candidate storage for para: %v", relayParent, para, candidateHash, isActiveLeaf, candidatedHashes) + } + + if !foundPara { + logger.Warnf("para = %s, candidateHash = %s, Received instruction to back a candidate for unscheduled para", para, candidateHash) + return + } + if !foundCandidate { + logger.Debugf("para = %s, candidateHash = %s, Received instruction to back unknown candidate", para, candidateHash) + } + } +} diff --git a/dot/parachain/prospective-parachains/prospective_parachains_test.go b/dot/parachain/prospective-parachains/prospective_parachains_test.go new file mode 100644 index 0000000000..99c7310459 --- /dev/null +++ b/dot/parachain/prospective-parachains/prospective_parachains_test.go @@ -0,0 +1,396 @@ +package prospectiveparachains + +import ( + "bytes" + "context" + "sync" + "testing" + + fragmentchain "github.com/ChainSafe/gossamer/dot/parachain/prospective-parachains/fragment-chain" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + inclusionemulator "github.com/ChainSafe/gossamer/dot/parachain/util/inclusion-emulator" + "github.com/ChainSafe/gossamer/lib/common" + "github.com/stretchr/testify/assert" +) + +const MAX_POV_SIZE = 1_000_000 + +func dummyPVD(parentHead parachaintypes.HeadData, relayParentNumber uint32) parachaintypes.PersistedValidationData { + return parachaintypes.PersistedValidationData{ + ParentHead: parentHead, + RelayParentNumber: relayParentNumber, + RelayParentStorageRoot: common.EmptyHash, + MaxPovSize: MAX_POV_SIZE, + } +} + +func dummyCandidateReceiptBadSig( + relayParentHash common.Hash, + commitments *common.Hash, +) parachaintypes.CandidateReceipt { + var commitmentsHash common.Hash + + if commitments != nil { + commitmentsHash = *commitments + } else { + commitmentsHash = common.EmptyHash // TODO: + } + + descriptor := parachaintypes.CandidateDescriptor{ + ParaID: parachaintypes.ParaID(0), + RelayParent: relayParentHash, + Collator: parachaintypes.CollatorID{}, + PovHash: common.EmptyHash, + ErasureRoot: common.EmptyHash, + Signature: parachaintypes.CollatorSignature{}, + ParaHead: common.EmptyHash, + ValidationCodeHash: parachaintypes.ValidationCodeHash{}, + PersistedValidationDataHash: common.EmptyHash, + } + + return parachaintypes.CandidateReceipt{ + CommitmentsHash: commitmentsHash, + Descriptor: descriptor, + } +} + +func MakeCandidate( + relayParent common.Hash, + relayParentNumber uint32, + paraID parachaintypes.ParaID, + parentHead parachaintypes.HeadData, + headData parachaintypes.HeadData, + validationCodeHash parachaintypes.ValidationCodeHash, +) parachaintypes.CommittedCandidateReceipt { + pvd := dummyPVD(parentHead, relayParentNumber) + + commitments := parachaintypes.CandidateCommitments{ + HeadData: headData, + HorizontalMessages: []parachaintypes.OutboundHrmpMessage{}, + UpwardMessages: []parachaintypes.UpwardMessage{}, + NewValidationCode: nil, + ProcessedDownwardMessages: 0, + HrmpWatermark: relayParentNumber, + } + + commitmentsHash := commitments.Hash() + + candidate := dummyCandidateReceiptBadSig(relayParent, &commitmentsHash) + candidate.CommitmentsHash = commitments.Hash() + candidate.Descriptor.ParaID = paraID + + pvdh, err := pvd.Hash() + + if err != nil { + panic(err) + } + + candidate.Descriptor.PersistedValidationDataHash = pvdh + candidate.Descriptor.ValidationCodeHash = validationCodeHash + + result := parachaintypes.CommittedCandidateReceipt{ + Descriptor: candidate.Descriptor, + Commitments: commitments, + } + + return result +} + +func introduceSecondedCandidate( + t *testing.T, + overseerToSubsystem chan any, + candidate parachaintypes.CommittedCandidateReceipt, + pvd parachaintypes.PersistedValidationData, +) { + req := IntroduceSecondedCandidateRequest{ + CandidateParaID: candidate.Descriptor.ParaID, + CandidateReceipt: candidate, + PersistedValidationData: pvd, + } + + response := make(chan bool) + + msg := IntroduceSecondedCandidate{ + IntroduceSecondedCandidateRequest: req, + Response: response, + } + + overseerToSubsystem <- msg + + assert.True(t, <-response) +} + +func introduceSecondedCandidateFailed( + t *testing.T, + overseerToSubsystem chan any, + candidate parachaintypes.CommittedCandidateReceipt, + pvd parachaintypes.PersistedValidationData, +) { + req := IntroduceSecondedCandidateRequest{ + CandidateParaID: candidate.Descriptor.ParaID, + CandidateReceipt: candidate, + PersistedValidationData: pvd, + } + + response := make(chan bool) + + msg := IntroduceSecondedCandidate{ + IntroduceSecondedCandidateRequest: req, + Response: response, + } + + overseerToSubsystem <- msg + + assert.False(t, <-response) +} + +func TestFailedIntroduceSecondedCandidateWhenMissingViewPerRelayParent( + t *testing.T, +) { + candidateRelayParent := common.Hash{0x01} + paraId := parachaintypes.ParaID(1) + parentHead := parachaintypes.HeadData{ + Data: bytes.Repeat([]byte{0x01}, 32), + } + headData := parachaintypes.HeadData{ + Data: bytes.Repeat([]byte{0x02}, 32), + } + validationCodeHash := parachaintypes.ValidationCodeHash{0x01} + candidateRelayParentNumber := uint32(1) + + candidate := MakeCandidate( + candidateRelayParent, + candidateRelayParentNumber, + paraId, + parentHead, + headData, + validationCodeHash, + ) + + pvd := dummyPVD(parentHead, candidateRelayParentNumber) + + subsystemToOverseer := make(chan any) + overseerToSubsystem := make(chan any) + + prospectiveParachains := NewProspectiveParachains(subsystemToOverseer) + + go prospectiveParachains.Run(context.Background(), overseerToSubsystem) + + introduceSecondedCandidateFailed(t, overseerToSubsystem, candidate, pvd) +} + +func TestFailedIntroduceSecondedCandidateWhenParentHeadAndHeadDataEquals( + t *testing.T, +) { + candidateRelayParent := common.Hash{0x01} + paraId := parachaintypes.ParaID(1) + parentHead := parachaintypes.HeadData{ + Data: bytes.Repeat([]byte{0x01}, 32), + } + headData := parachaintypes.HeadData{ + Data: bytes.Repeat([]byte{0x01}, 32), + } + validationCodeHash := parachaintypes.ValidationCodeHash{0x01} + candidateRelayParentNumber := uint32(1) + + candidate := MakeCandidate( + candidateRelayParent, + candidateRelayParentNumber, + paraId, + parentHead, + headData, + validationCodeHash, + ) + + pvd := dummyPVD(parentHead, candidateRelayParentNumber) + + subsystemToOverseer := make(chan any) + overseerToSubsystem := make(chan any) + + prospectiveParachains := NewProspectiveParachains(subsystemToOverseer) + + relayParent := inclusionemulator.RelayChainBlockInfo{ + Hash: candidateRelayParent, + Number: 0, + StorageRoot: common.Hash{0x00}, + } + + baseConstraints := &inclusionemulator.Constraints{ + RequiredParent: parachaintypes.HeadData{Data: []byte{byte(0)}}, + MinRelayParentNumber: 0, + ValidationCodeHash: parachaintypes.ValidationCodeHash(common.Hash{0x03}), + } + + scope, err := fragmentchain.NewScopeWithAncestors(relayParent, baseConstraints, nil, 10, nil) + assert.NoError(t, err) + + prospectiveParachains.View.PerRelayParent[candidateRelayParent] = RelayBlockViewData{ + FragmentChains: map[parachaintypes.ParaID]fragmentchain.FragmentChain{ + paraId: *fragmentchain.NewFragmentChain(scope, fragmentchain.NewCandidateStorage()), + }, + } + go prospectiveParachains.Run(context.Background(), overseerToSubsystem) + + introduceSecondedCandidateFailed(t, overseerToSubsystem, candidate, pvd) +} + +func TestHandleIntroduceSecondedCandidate( + t *testing.T, +) { + candidateRelayParent := common.Hash{0x01} + paraId := parachaintypes.ParaID(1) + parentHead := parachaintypes.HeadData{ + Data: bytes.Repeat([]byte{0x01}, 32), + } + headData := parachaintypes.HeadData{ + Data: bytes.Repeat([]byte{0x02}, 32), + } + validationCodeHash := parachaintypes.ValidationCodeHash{0x01} + candidateRelayParentNumber := uint32(1) + + candidate := MakeCandidate( + candidateRelayParent, + candidateRelayParentNumber, + paraId, + parentHead, + headData, + validationCodeHash, + ) + + pvd := dummyPVD(parentHead, candidateRelayParentNumber) + + subsystemToOverseer := make(chan any) + overseerToSubsystem := make(chan any) + + prospectiveParachains := NewProspectiveParachains(subsystemToOverseer) + + relayParent := inclusionemulator.RelayChainBlockInfo{ + Hash: candidateRelayParent, + Number: 0, + StorageRoot: common.Hash{0x00}, + } + + baseConstraints := &inclusionemulator.Constraints{ + RequiredParent: parachaintypes.HeadData{Data: []byte{byte(0)}}, + MinRelayParentNumber: 0, + ValidationCodeHash: parachaintypes.ValidationCodeHash(common.Hash{0x03}), + } + + scope, err := fragmentchain.NewScopeWithAncestors(relayParent, baseConstraints, nil, 10, nil) + assert.NoError(t, err) + + prospectiveParachains.View.PerRelayParent[candidateRelayParent] = RelayBlockViewData{ + FragmentChains: map[parachaintypes.ParaID]fragmentchain.FragmentChain{ + paraId: *fragmentchain.NewFragmentChain(scope, fragmentchain.NewCandidateStorage()), + }, + } + go prospectiveParachains.Run(context.Background(), overseerToSubsystem) + + introduceSecondedCandidate(t, overseerToSubsystem, candidate, pvd) +} + +func markCandidatedBacked( + t *testing.T, + overseerToSubsystem chan any, + candidate parachaintypes.CommittedCandidateReceipt, +) { + hash, err := candidate.Hash() + + assert.NoError(t, err) + + msg := CandidateBacked{ + ParaId: candidate.Descriptor.ParaID, + CandidateHash: parachaintypes.CandidateHash{Value: hash}, + } + + overseerToSubsystem <- msg +} + +func TestHandleBacked( + t *testing.T, +) { + candidateRelayParent := common.Hash{0x01} + paraId := parachaintypes.ParaID(1) + parentHead := parachaintypes.HeadData{ + Data: bytes.Repeat([]byte{0x01}, 32), + } + headData := parachaintypes.HeadData{ + Data: bytes.Repeat([]byte{0x02}, 32), + } + validationCodeHash := parachaintypes.ValidationCodeHash{0x01} + candidateRelayParentNumber := uint32(0) + + candidate := MakeCandidate( + candidateRelayParent, + candidateRelayParentNumber, + paraId, + parentHead, + headData, + validationCodeHash, + ) + + pvd := dummyPVD(parentHead, candidateRelayParentNumber) + + subsystemToOverseer := make(chan any) + overseerToSubsystem := make(chan any) + + prospectiveParachains := NewProspectiveParachains(subsystemToOverseer) + + relayParent := inclusionemulator.RelayChainBlockInfo{ + Hash: candidateRelayParent, + Number: 0, + StorageRoot: common.Hash{0x00}, + } + + baseConstraints := &inclusionemulator.Constraints{ + RequiredParent: parachaintypes.HeadData{Data: bytes.Repeat([]byte{0x01}, 32)}, + MinRelayParentNumber: 0, + ValidationCodeHash: validationCodeHash, + MaxPoVSize: 1000000, + } + + scope, err := fragmentchain.NewScopeWithAncestors(relayParent, baseConstraints, nil, 10, nil) + assert.NoError(t, err) + + prospectiveParachains.View.PerRelayParent[candidateRelayParent] = RelayBlockViewData{ + FragmentChains: map[parachaintypes.ParaID]fragmentchain.FragmentChain{ + paraId: *fragmentchain.NewFragmentChain(scope, fragmentchain.NewCandidateStorage()), + }, + } + + var wg sync.WaitGroup + + wg.Add(1) + go func(wg *sync.WaitGroup) { + defer wg.Done() + prospectiveParachains.Run(context.Background(), overseerToSubsystem) + }(&wg) + + introduceSecondedCandidate(t, overseerToSubsystem, candidate, pvd) + + markCandidatedBacked(t, overseerToSubsystem, candidate) + + overseerToSubsystem <- parachaintypes.Conclude{} + + wg.Wait() + + chains := prospectiveParachains.View.GetFragmentChains(candidateRelayParent) + + fragmentChain, exist := chains[paraId] + + assert.True(t, exist) + + hash, err := candidate.Hash() + assert.NoError(t, err) + + isCandidateBacked := fragmentChain.IsCandidateBacked(parachaintypes.CandidateHash{Value: hash}) + + assert.True(t, isCandidateBacked) + + hashes := fragmentChain.BestChainVec() + + assert.Len(t, hashes, 1) + + assert.Equal(t, hashes[0], parachaintypes.CandidateHash{Value: hash}) + +} diff --git a/go.mod b/go.mod index b624115e9a..5b5a5e2ef6 100644 --- a/go.mod +++ b/go.mod @@ -167,6 +167,7 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.11.0 // indirect diff --git a/go.sum b/go.sum index 03ec8f0ac3..69a113e42d 100644 --- a/go.sum +++ b/go.sum @@ -555,6 +555,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= @@ -758,6 +760,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=