-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(dot/parachain/backing): handle
second
incoming overseer message (
#3682) The `Second` message is a message received from the overseer. Candidate Backing subsystem should second the given candidate in the context of the given relay parent. This candidate must be validated. Sign and dispatch a `Seconded` statement only if we have not signed a Valid statement for the requested candidate.
- Loading branch information
1 parent
d093b34
commit 40ae213
Showing
5 changed files
with
236 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright 2024 ChainSafe Systems (ON) | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
|
||
package backing | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" | ||
) | ||
|
||
var ( | ||
errWrongPVDForSecondingCandidate = errors.New( | ||
"incorrect persisted validation data provided for seconding candidate") | ||
errUnknownRelayParentForSecondingCandidate = errors.New( | ||
"attempted to second a candidate with an unknown relay parent") | ||
errParaOutsideAssignmentForSeconding = errors.New( | ||
"subsystem requested to second for parachain beyond our assignment scope") | ||
errAlreadySignedValidStatement = errors.New("already signed a valid statement for this candidate") | ||
) | ||
|
||
func (cb *CandidateBacking) handleSecondMessage( | ||
candidateReceipt parachaintypes.CandidateReceipt, | ||
pvd parachaintypes.PersistedValidationData, | ||
pov parachaintypes.PoV, | ||
chRelayParentAndCommand chan relayParentAndCommand, | ||
) error { | ||
hash, err := candidateReceipt.Hash() | ||
if err != nil { | ||
return fmt.Errorf("hashing candidate receipt: %w", err) | ||
} | ||
candidateHash := parachaintypes.CandidateHash{Value: hash} | ||
|
||
pvdHash, err := pvd.Hash() | ||
if err != nil { | ||
return fmt.Errorf("hashing persisted validation data: %w", err) | ||
} | ||
|
||
if candidateReceipt.Descriptor.PersistedValidationDataHash != pvdHash { | ||
return fmt.Errorf("%w; mismatch between persisted validation data hash in candidate descriptor: %s "+ | ||
"and calculated hash of given persisted validation data: %s", | ||
errWrongPVDForSecondingCandidate, | ||
candidateReceipt.Descriptor.PersistedValidationDataHash, | ||
pvdHash, | ||
) | ||
} | ||
|
||
rpState, ok := cb.perRelayParent[candidateReceipt.Descriptor.RelayParent] | ||
if !ok { | ||
return fmt.Errorf("%w; candidate hash: %s; relay parent: %s", | ||
errUnknownRelayParentForSecondingCandidate, | ||
candidateHash, | ||
candidateReceipt.Descriptor.RelayParent, | ||
) | ||
} | ||
|
||
// Sanity check that candidate is from our assignment. | ||
if candidateReceipt.Descriptor.ParaID != uint32(rpState.assignment) { | ||
return fmt.Errorf("%w: candidate hash: %s; candidate paraID: %d; assignment: %d", | ||
errParaOutsideAssignmentForSeconding, | ||
candidateHash, | ||
candidateReceipt.Descriptor.ParaID, | ||
rpState.assignment, | ||
) | ||
} | ||
|
||
// If the message is a `CandidateBackingMessage::Second`, sign and dispatch a | ||
// Seconded statement only if we have not signed a Valid statement for the requested candidate. | ||
if rpState.issuedStatements[candidateHash] { | ||
return fmt.Errorf("%w: candidate hash: %s", errAlreadySignedValidStatement, candidateHash) | ||
} | ||
|
||
// Kick off background validation with intent to second. | ||
logger.Debugf("validate and second candidate: %s", candidateHash) | ||
return rpState.validateAndMakeAvailable( | ||
executorParamsAtRelayParent, | ||
cb.SubSystemToOverseer, | ||
chRelayParentAndCommand, | ||
candidateReceipt, | ||
rpState.relayParent, | ||
pvd, | ||
pov, | ||
uint32(len(rpState.tableContext.validators)), | ||
second, | ||
candidateHash, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// Copyright 2024 ChainSafe Systems (ON) | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
|
||
package backing | ||
|
||
import ( | ||
"testing" | ||
|
||
parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" | ||
"github.com/ChainSafe/gossamer/lib/common" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestHandleSecondMessage(t *testing.T) { | ||
t.Parallel() | ||
|
||
testCases := []struct { | ||
description string | ||
cb *CandidateBacking | ||
candidateReceipt parachaintypes.CandidateReceipt | ||
pvd parachaintypes.PersistedValidationData | ||
err error | ||
}{ | ||
{ | ||
description: "wrong_persisted_validation_data_for_seconding_candidate", | ||
cb: &CandidateBacking{}, | ||
candidateReceipt: parachaintypes.CandidateReceipt{}, | ||
pvd: parachaintypes.PersistedValidationData{}, | ||
err: errWrongPVDForSecondingCandidate, | ||
}, | ||
{ | ||
description: "unknown_relay_parent_for_seconding_candidate", | ||
cb: &CandidateBacking{}, | ||
candidateReceipt: dummyCandidateReceipt(t), | ||
pvd: dummyPVD(t), | ||
err: errUnknownRelayParentForSecondingCandidate, | ||
}, | ||
{ | ||
description: "parachain_outside_assignment_for_seconding", | ||
cb: &CandidateBacking{ | ||
perRelayParent: map[common.Hash]*perRelayParentState{ | ||
getDummyHash(t, 6): { | ||
assignment: 10, | ||
}, | ||
}, | ||
}, | ||
candidateReceipt: dummyCandidateReceipt(t), | ||
pvd: dummyPVD(t), | ||
err: errParaOutsideAssignmentForSeconding, | ||
}, | ||
{ | ||
description: "already_signed_valid_statement_for_candidate", | ||
cb: &CandidateBacking{ | ||
perRelayParent: map[common.Hash]*perRelayParentState{ | ||
getDummyHash(t, 6): { | ||
assignment: 1, | ||
issuedStatements: map[parachaintypes.CandidateHash]bool{ | ||
dummyCandidateHash(t): true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
candidateReceipt: dummyCandidateReceipt(t), | ||
pvd: dummyPVD(t), | ||
err: errAlreadySignedValidStatement, | ||
}, | ||
{ | ||
description: "kick_off_background_validation_with_intent_to_second", | ||
cb: &CandidateBacking{ | ||
perRelayParent: map[common.Hash]*perRelayParentState{ | ||
getDummyHash(t, 6): { | ||
assignment: 1, | ||
awaitingValidation: map[parachaintypes.CandidateHash]bool{ | ||
dummyCandidateHash(t): true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
candidateReceipt: dummyCandidateReceipt(t), | ||
pvd: dummyPVD(t), | ||
err: nil, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
tc := tc | ||
t.Run(tc.description, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
err := tc.cb.handleSecondMessage(tc.candidateReceipt, tc.pvd, parachaintypes.PoV{}, nil) | ||
if err == nil { | ||
require.NoError(t, err) | ||
} else { | ||
require.ErrorIs(t, err, tc.err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func dummyPVD(t *testing.T) parachaintypes.PersistedValidationData { | ||
t.Helper() | ||
|
||
return parachaintypes.PersistedValidationData{ | ||
ParentHead: parachaintypes.HeadData{ | ||
Data: []byte{1, 2, 3}, | ||
}, | ||
RelayParentNumber: 5, | ||
RelayParentStorageRoot: getDummyHash(t, 5), | ||
MaxPovSize: 3, | ||
} | ||
} | ||
|
||
func dummyCandidateReceipt(t *testing.T) parachaintypes.CandidateReceipt { | ||
t.Helper() | ||
|
||
cr := getDummyCommittedCandidateReceipt(t).ToPlain() | ||
|
||
// blake2bhash of PVD in dummyPVD(t *testing.T) function | ||
cr.Descriptor.PersistedValidationDataHash = | ||
common.MustHexToHash("0x3544fbcdcb094751a5e044a30b994b2586ffc0b50e8b88c381461fe023a7242f") | ||
|
||
return cr | ||
} | ||
|
||
func dummyCandidateHash(t *testing.T) parachaintypes.CandidateHash { | ||
t.Helper() | ||
|
||
cr := dummyCandidateReceipt(t) | ||
hash, err := cr.Hash() | ||
require.NoError(t, err) | ||
|
||
return parachaintypes.CandidateHash{Value: hash} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters