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(prospective-parachains): Implement HandleIntroduceSecondedCandidate #4350

Open
wants to merge 72 commits into
base: feat/parachain
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
afa3e68
feat: starting the fragment chain implementation
EclesioMeloJunior Nov 11, 2024
404f5fe
feat: implemented `CandidateStorage`
EclesioMeloJunior Nov 12, 2024
6648464
feat: create `Scope`, working on inclusionemulator.Fragment
EclesioMeloJunior Nov 13, 2024
5119649
wip: implementing `validateAgainstConstraints`
EclesioMeloJunior Nov 14, 2024
e072160
feat: implement `BackedChain`
EclesioMeloJunior Nov 14, 2024
0de059a
wip: `FragmentChain` struct implementation
EclesioMeloJunior Nov 15, 2024
0388b02
feat: full `FragmentChain` implementation done
EclesioMeloJunior Nov 16, 2024
1b796b8
chore: wip fragment chain tests
EclesioMeloJunior Nov 19, 2024
f11ee4f
chore: `TestPopulateAndCheckPotential` done
EclesioMeloJunior Nov 21, 2024
6a001c8
feat: bring polkadot-sdk test coverage to fragment chain
EclesioMeloJunior Nov 21, 2024
6b30bc6
chore: remove dsstore
EclesioMeloJunior Nov 26, 2024
5f01ccb
chore: change loopt
EclesioMeloJunior Nov 28, 2024
987c880
Merge branch 'feat/parachain' into eclesio/fragment-chain-impl
EclesioMeloJunior Nov 28, 2024
e9b4852
feature: implement HandleIntroduceSecondedCandidate
rebonat0 Nov 28, 2024
15feb1b
progress: started tests
rebonat0 Nov 28, 2024
fe27ee3
added test case example
rebonat0 Nov 28, 2024
a8960d8
implement HandleIntroduceSecondedCandidate on processMessage
rebonat0 Nov 28, 2024
a53f150
added tests
rebonat0 Nov 28, 2024
038dfce
added new view instance on NewProspectiveParachains
rebonat0 Nov 28, 2024
0465a41
added more test scenarios
rebonat0 Nov 28, 2024
5abd1e3
chore: fix loop conditional
EclesioMeloJunior Dec 3, 2024
a4e8f34
Merge branch 'eclesio/fragment-chain-impl' into feature/handle_introd…
EclesioMeloJunior Dec 3, 2024
d344eb5
Update dot/parachain/prospective-parachains/prospective-parachains.go
rebonat0 Dec 3, 2024
2ac2d82
Update dot/parachain/prospective-parachains/prospective-parachains.go
rebonat0 Dec 3, 2024
44b74ea
Update dot/parachain/prospective-parachains/prospective-parachains.go
rebonat0 Dec 3, 2024
cdf03de
Update dot/parachain/prospective-parachains/prospective-parachains.go
rebonat0 Dec 3, 2024
44a1ad1
Update dot/parachain/prospective-parachains/prospective-parachains.go
rebonat0 Dec 3, 2024
75f979b
Update dot/parachain/prospective-parachains/prospective-parachains.go
rebonat0 Dec 3, 2024
cecbc74
Update dot/parachain/prospective-parachains/prospective-parachains.go
rebonat0 Dec 3, 2024
fdfb811
Update dot/parachain/prospective-parachains/prospective_parachains_te…
rebonat0 Dec 3, 2024
4ccca75
Update dot/parachain/prospective-parachains/prospective_parachains_te…
rebonat0 Dec 3, 2024
16d94c1
refactor
rebonat0 Dec 3, 2024
3809bec
fix tests
rebonat0 Dec 3, 2024
ea78d43
change variable
rebonat0 Dec 3, 2024
54e0adb
chore: address comment
EclesioMeloJunior Dec 5, 2024
78dfef1
chore: address comments
EclesioMeloJunior Dec 6, 2024
e55b6ce
chore: test snakecase
EclesioMeloJunior Dec 6, 2024
2f60868
wip: fixing failing tests
EclesioMeloJunior Dec 6, 2024
d2b6df7
refactor and fix lint
rebonat0 Dec 9, 2024
5999e88
Merge branch 'eclesio/fragment-chain-impl' into feature/handle_introd…
rebonat0 Dec 9, 2024
041f161
chore: fix `TestScopeOnlyTakesAncestorsUpToMin` test
EclesioMeloJunior Dec 9, 2024
082bb9c
chore: loop until maxDepth + 1
EclesioMeloJunior Dec 9, 2024
153b7d6
Merge branch 'feat/parachain' into eclesio/fragment-chain-impl
EclesioMeloJunior Dec 9, 2024
7d716c7
chore: gofmt
EclesioMeloJunior Dec 9, 2024
1dfeb76
chore: address lint warns
EclesioMeloJunior Dec 9, 2024
ba8ede8
chore: removed `Unconnected` method
EclesioMeloJunior Dec 10, 2024
ef35ba9
chore: remove `candidates` method
EclesioMeloJunior Dec 10, 2024
6e28164
chore: added logs
EclesioMeloJunior Dec 10, 2024
b65841c
Merge branch 'feat/parachain' into eclesio/fragment-chain-impl
EclesioMeloJunior Dec 10, 2024
30062cf
chore: address comments
EclesioMeloJunior Dec 12, 2024
8d80d2d
Merge branch 'eclesio/fragment-chain-impl' of github.com:ChainSafe/go…
EclesioMeloJunior Dec 12, 2024
e484624
Merge branch 'feat/parachain' into eclesio/fragment-chain-impl
EclesioMeloJunior Dec 12, 2024
42a7345
chore: fix misspelling
EclesioMeloJunior Dec 12, 2024
87e096c
Merge branch 'eclesio/fragment-chain-impl' of github.com:ChainSafe/go…
EclesioMeloJunior Dec 12, 2024
71085e7
chore: make types unexported
EclesioMeloJunior Dec 12, 2024
da18a0b
Trigger Build
EclesioMeloJunior Dec 13, 2024
d16b056
chore: addressing comments
EclesioMeloJunior Dec 14, 2024
e5d8019
chore: small comments
EclesioMeloJunior Dec 14, 2024
1835388
Merge branch 'feat/parachain' into eclesio/fragment-chain-impl
EclesioMeloJunior Jan 6, 2025
ee66827
chore: adjust types
EclesioMeloJunior Jan 6, 2025
3ba5603
chore: nolint unused funcs
EclesioMeloJunior Jan 6, 2025
38aefbd
Merge branch 'eclesio/fragment-chain-impl' into feature/handle_introd…
EclesioMeloJunior Jan 7, 2025
df24773
Merge branch 'feat/parachain' into feature/handle_introduce_seconded_…
EclesioMeloJunior Jan 7, 2025
1e2a74b
Merge branch 'feat/parachain' into feature/handle_introduce_seconded_…
rebonat0 Jan 15, 2025
2db086d
fix import
rebonat0 Jan 15, 2025
3985deb
fix tests conflicts
rebonat0 Jan 15, 2025
f8e5e9e
fix usage
rebonat0 Jan 15, 2025
bc7d201
fix lint
rebonat0 Jan 15, 2025
f6f4a27
fix lint
rebonat0 Jan 15, 2025
c4917eb
fix lint
rebonat0 Jan 15, 2025
35a714d
fix lint
rebonat0 Jan 15, 2025
3a83846
run gofmt
rebonat0 Jan 15, 2025
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
12 changes: 7 additions & 5 deletions dot/parachain/prospective-parachains/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import (
)

var (
errCandidateAlreadyKnown = errors.New("candidate already known")
errZeroLengthCycle = errors.New("candidate's parent head is equal to its output head. Would introduce a cycle") //nolint:lll
errCycle = errors.New("candidate would introduce a cycle")
errMultiplePaths = errors.New("candidate would introduce two paths to the same output state")
errIntroduceBackedCandidate = errors.New("attempting to directly introduce a Backed candidate. It should first be introduced as Seconded") //nolint:lll,unused
errCandidateAlreadyKnown = errors.New("candidate already known")
errZeroLengthCycle = errors.New("candidate's parent head is equal to its output head. Would introduce a cycle") //nolint:lll
errCycle = errors.New("candidate would introduce a cycle")
errMultiplePaths = errors.New("candidate would introduce two paths to the same output state")
errIntroduceBackedCandidate = errors.New(
"attempting to directly introduce a Backed candidate. It should first be introduced as Seconded",
)
errParentCandidateNotFound = errors.New("could not find parent of the candidate")
errRelayParentMovedBackwards = errors.New("relay parent would move backwards from the latest candidate in the chain") //nolint:lll
errPersistedValidationDataMismatch = errors.New("candidate does not match the persisted validation data provided alongside it") //nolint:lll
Expand Down
2 changes: 1 addition & 1 deletion dot/parachain/prospective-parachains/fragment_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ func (f *fragmentChain) canAddCandidateAsPotential(entry *candidateEntry) error
// tryAddingSecondedCandidate tries to add a candidate as a seconded candidate, if the
// candidate has potential. It will never be added to the chain directly in the seconded
// state, it will only be part of the unconnected storage
func (f *fragmentChain) tryAddingSecondedCandidate(entry *candidateEntry) error { //nolint:unused
func (f *fragmentChain) tryAddingSecondedCandidate(entry *candidateEntry) error {
if entry.state == backed {
return errIntroduceBackedCandidate
}
Expand Down
109 changes: 108 additions & 1 deletion dot/parachain/prospective-parachains/prospective-parachains.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@ import (
"context"
"errors"

"github.com/ChainSafe/gossamer/dot/parachain/backing"
parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/common"
)

var logger = log.NewFromGlobal(log.AddContext("pkg", "prospective_parachains"), log.SetLevel(log.Debug))

// Initialize with empty values.
func NewView() *view {
return &view{
perRelayParent: make(map[common.Hash]*relayParentData),
activeLeaves: make(map[common.Hash]bool),
implicitView: nil, // TODO: currently there's no implementation for ImplicitView, reference is:
// https://github.com/ChainSafe/gossamer/blob/main/lib/prospective_parachains/view.go#L10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

type ProspectiveParachains struct {
SubsystemToOverseer chan<- any
View *view
Expand All @@ -19,6 +30,7 @@ type ProspectiveParachains struct {
type view struct {
activeLeaves map[common.Hash]bool
perRelayParent map[common.Hash]*relayParentData
implicitView backing.ImplicitView
}

type relayParentData struct {
Expand All @@ -34,6 +46,7 @@ func (*ProspectiveParachains) Name() parachaintypes.SubSystemName {
func NewProspectiveParachains(overseerChan chan<- any) *ProspectiveParachains {
prospectiveParachain := ProspectiveParachains{
SubsystemToOverseer: overseerChan,
View: NewView(),
}
return &prospectiveParachain
}
Expand Down Expand Up @@ -64,7 +77,11 @@ func (pp *ProspectiveParachains) processMessage(msg any) {
case parachaintypes.BlockFinalizedSignal:
_ = pp.ProcessBlockFinalizedSignal(msg)
case IntroduceSecondedCandidate:
panic("not implemented yet: see issue #4308")
pp.introduceSecondedCandidate(
pp.View,
msg.IntroduceSecondedCandidateRequest,
msg.Response,
)
case CandidateBacked:
panic("not implemented yet: see issue #4309")
case GetBackableCandidates:
Expand All @@ -82,6 +99,96 @@ func (pp *ProspectiveParachains) processMessage(msg any) {

}

func (pp *ProspectiveParachains) introduceSecondedCandidate(
rebonat0 marked this conversation as resolved.
Show resolved Hide resolved
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("hashing candidate: %s", err.Error())
response <- false
Copy link
Contributor

@haikoschol haikoschol Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually the writer closes the channel when it is done. Will this channel be used somewhere else as well or should it be closed here?

If it should be closed here, I suggest adding defer close(response) at the top of the function.

If you are unsure we can leave it as is and figure it out later. In that case please add a TODO comment somewhere.

return
}

candidateHash := parachaintypes.CandidateHash{Value: hash}

entry, err := newCandidateEntry(
candidateHash,
candidate,
pvd,
seconded,
)

if err != nil {
logger.Tracef("adding 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]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change

if !exists {
continue
}

_, isActiveLeaf := view.activeLeaves[relayParent]

paraScheduled = true

err = chain.tryAddingSecondedCandidate(entry)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change

if err != nil {
if errors.Is(err, errCandidateAlreadyKnown) {
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(
"adding 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.Tracef("added seconded candidate to %d relay parents: %s", len(added), candidateHash)
}

response <- len(added) > 0
}

// ProcessActiveLeavesUpdateSignal processes active leaves update signal
func (pp *ProspectiveParachains) ProcessActiveLeavesUpdateSignal(parachaintypes.ActiveLeavesUpdateSignal) error {
panic("not implemented yet: see issue #4305")
Expand Down
193 changes: 193 additions & 0 deletions dot/parachain/prospective-parachains/prospective_parachains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,206 @@ package prospectiveparachains

import (
"bytes"
"context"
"testing"

parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/stretchr/testify/assert"
)

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 := relayChainBlockInfo{
Hash: candidateRelayParent,
Number: 0,
StorageRoot: common.Hash{0x00},
}

baseConstraints := &parachaintypes.Constraints{
RequiredParent: parachaintypes.HeadData{Data: []byte{byte(0)}},
MinRelayParentNumber: 0,
ValidationCodeHash: parachaintypes.ValidationCodeHash(common.Hash{0x03}),
}
scope, err := newScopeWithAncestors(relayParent, baseConstraints, nil, 10, nil)
assert.NoError(t, err)

prospectiveParachains.View.perRelayParent[candidateRelayParent] = &relayParentData{
fragmentChains: map[parachaintypes.ParaID]*fragmentChain{
paraId: newFragmentChain(scope, 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 := relayChainBlockInfo{
Hash: candidateRelayParent,
Number: 0,
StorageRoot: common.Hash{0x00},
}

baseConstraints := &parachaintypes.Constraints{
RequiredParent: parachaintypes.HeadData{Data: []byte{byte(0)}},
MinRelayParentNumber: 0,
ValidationCodeHash: parachaintypes.ValidationCodeHash(common.Hash{0x03}),
}

scope, err := newScopeWithAncestors(relayParent, baseConstraints, nil, 10, nil)
assert.NoError(t, err)

prospectiveParachains.View.perRelayParent[candidateRelayParent] = &relayParentData{
fragmentChains: map[parachaintypes.ParaID]*fragmentChain{
paraId: newFragmentChain(scope, newCandidateStorage()),
},
}
go prospectiveParachains.Run(context.Background(), overseerToSubsystem)

introduceSecondedCandidate(t, overseerToSubsystem, candidate, pvd)
}

const MaxPoVSize = 1_000_000

func dummyPVD(parentHead parachaintypes.HeadData, relayParentNumber uint32) parachaintypes.PersistedValidationData {
Expand Down
Loading