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(dot/parachain): Confirm invalidity errors are implemented in candidate validation. #4163

Merged
merged 40 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
50cc3ab
add pvf worker pool skeleton
edwardmack Jul 10, 2024
3427da0
add test to validation host
edwardmack Jul 19, 2024
6537104
create worker pool skeleton for PVF host
edwardmack Jul 23, 2024
7e314f3
add validation logic to workers
edwardmack Aug 6, 2024
0b2b5fc
address lint issues.
edwardmack Aug 6, 2024
20fd4c4
add functionality for validate from chainstate to pvf host and tests
edwardmack Aug 10, 2024
6c05d82
add updated mock files
edwardmack Aug 10, 2024
a6b2652
clean-up unused comments
edwardmack Aug 12, 2024
e978c38
Merge branch 'feat/parachain' into ed/feat/candidate_validation_pvf_h…
edwardmack Aug 16, 2024
0655dd3
address merge conflicts
edwardmack Aug 16, 2024
674fefa
Merge branch 'feat/parachain' into ed/feat/candidate_validation_pvf_h…
edwardmack Aug 21, 2024
01905af
resolve merge conflicts
edwardmack Aug 21, 2024
afde6fd
refactor ValidationHost to Host, and validationWorkerPool to workerPool
edwardmack Aug 21, 2024
0f25072
Merge branch 'feat/parachain' into ed/feat/candidate_validation_pvf_h…
edwardmack Aug 23, 2024
dbf3cf8
refactor go concurrancy
edwardmack Aug 28, 2024
c95c3e7
Merge branch 'feat/parachain' into ed/feat/candidate_validation_pvf_h…
edwardmack Aug 29, 2024
79ba043
revert run to match interface
edwardmack Aug 29, 2024
ce721e7
remove redundant structs for simplicty, removed go routines to simpli…
edwardmack Aug 29, 2024
48faacd
removed pvf package, simplified code removed concurrency
edwardmack Aug 29, 2024
391c6b4
added missing mock file
edwardmack Aug 29, 2024
7988888
address deep source comments
edwardmack Aug 29, 2024
5820e3f
address PR comments
edwardmack Sep 3, 2024
3e5ac28
make private functions/structs private
edwardmack Sep 4, 2024
3e6f025
implement function timeout
edwardmack Sep 5, 2024
020fb6f
refactor submitRequest to executeRequest
edwardmack Sep 6, 2024
cdc3514
add test for candidate-validation timeout
edwardmack Sep 6, 2024
9b0f2e6
Merge branch 'ed/feat/candidate_validation_pvf_host_base' into ed/fea…
edwardmack Sep 6, 2024
31b83f1
handle validation errors InvalidOutputs and BadParent
edwardmack Sep 10, 2024
c96f6e8
add addition validation timeout tests
edwardmack Sep 11, 2024
b8bbeb2
address PR comments
edwardmack Sep 11, 2024
25d95bd
address PR comments, rename variables and functions
edwardmack Sep 13, 2024
6677479
fix test
edwardmack Sep 13, 2024
9429632
remove printf from debugging
edwardmack Sep 13, 2024
f869cba
address PR comments
edwardmack Sep 17, 2024
d71e539
Merge branch 'feat/parachain' into ed/feat/candidate_validation_pvf_h…
edwardmack Sep 18, 2024
1f9d339
Merge branch 'ed/feat/candidate_validation_pvf_host_base' into ed/fea…
edwardmack Sep 18, 2024
f1b7164
fix merge conflicts
edwardmack Sep 18, 2024
a430a3e
Merge branch 'feat/parachain' into ed/feat/candidate_validation_confi…
edwardmack Sep 19, 2024
42aaa84
address PR comments
edwardmack Sep 23, 2024
fb4ea93
fix if error check
edwardmack Sep 24, 2024
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
39 changes: 36 additions & 3 deletions dot/parachain/candidate-validation/candidate_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func (cv *CandidateValidation) validateFromChainState(msg ValidateFromChainState

persistedValidationData, validationCode, err := getValidationData(runtimeInstance,
msg.CandidateReceipt.Descriptor.ParaID)

kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
logger.Errorf("getting validation data: %w", err)
msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{
Expand All @@ -172,24 +173,56 @@ func (cv *CandidateValidation) validateFromChainState(msg ValidateFromChainState
return
}

if persistedValidationData == nil {
badParent := BadParent
reason := ValidationResult{
Invalid: &badParent,
}
msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{
Data: reason,
}
return
}
validationTask := &ValidationTask{
PersistedValidationData: *persistedValidationData,
ValidationCode: validationCode,
CandidateReceipt: &msg.CandidateReceipt,
PoV: msg.Pov,
ExecutorParams: msg.ExecutorParams,
// todo: implement PvfExecTimeoutKind, so that validate can be called with a timeout see issue: #3429
PvfExecTimeoutKind: parachaintypes.PvfExecTimeoutKind{},
axaysagathiya marked this conversation as resolved.
Show resolved Hide resolved
}

result, err := cv.pvfHost.validate(validationTask)
if err != nil {
msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{
Err: err,
}
} else {
return
}
if !result.IsValid() {
msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{
Data: *result,
}
return
}
valid, err := runtimeInstance.ParachainHostCheckValidationOutputs(parachaintypes.ParaID(msg.CandidateReceipt.
Descriptor.ParaID), result.Valid.CandidateCommitments)
if err != nil {
msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{
Err: fmt.Errorf("check validation outputs: Bad request: %w", err),
}
return
}
if !valid {
invalidOutput := InvalidOutputs
reason := &ValidationResult{
Invalid: &invalidOutput,
}
msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{
Data: *reason,
}
return
}
msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{
Data: *result,
}
}
71 changes: 58 additions & 13 deletions dot/parachain/candidate-validation/candidate_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ var (
paramsTooLarge = ParamsTooLarge
codeHashMismatch = CodeHashMismatch
badSignature = BadSignature
invalidOutputs = InvalidOutputs
badParent = BadParent
)

func createTestCandidateReceiptAndValidationCode(t *testing.T) (
func createTestCandidateReceiptAndValidationCodeWParaId(t *testing.T, id uint32) (
parachaintypes.CandidateReceipt, parachaintypes.ValidationCode) {
edwardmack marked this conversation as resolved.
Show resolved Hide resolved
// this wasm was achieved by building polkadot's adder test parachain
runtimeFilePath := "./testdata/test_parachain_adder.wasm"
Expand All @@ -38,7 +40,7 @@ func createTestCandidateReceiptAndValidationCode(t *testing.T) (
collatorKeypair, err := sr25519.GenerateKeypair()
require.NoError(t, err)

descriptor := makeValidCandidateDescriptor(t, 1000,
descriptor := makeValidCandidateDescriptor(t, id,
common.MustHexToHash("0xded542bacb3ca6c033a57676f94ae7c8f36834511deb44e3164256fd3b1c0de0"),
common.MustHexToHash("0x690d8f252ef66ab0f969c3f518f90012b849aa5ac94e1752c5e5ae5a8996de37"),
common.MustHexToHash("0xb608991ffc48dd405fd4b10e92eaebe2b5a2eedf44d0c3efb8997fdee8bebed9"),
Expand Down Expand Up @@ -107,7 +109,7 @@ func TestCandidateValidation_wasm_invalid_magic_number(t *testing.T) {
func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) {
t.Parallel()

candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t)
candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCodeWParaId(t, 1000)
candidateReceipt2 := candidateReceipt
candidateReceipt2.Descriptor.PovHash = common.MustHexToHash(
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
Expand Down Expand Up @@ -261,7 +263,7 @@ func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T)
func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) {
t.Parallel()

candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t)
candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCodeWParaId(t, 1000)
candidateReceipt2 := candidateReceipt
candidateReceipt2.Descriptor.PovHash = common.MustHexToHash(
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
Expand All @@ -278,6 +280,11 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T)
candidateReceipt5 := candidateReceipt
candidateReceipt5.Descriptor.ParaID = 5

candidateReceipt6, _ := createTestCandidateReceiptAndValidationCodeWParaId(t, 6)

candidateReceipt7 := candidateReceipt
candidateReceipt7.Descriptor.ParaID = 7

ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)

Expand Down Expand Up @@ -318,6 +325,16 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T)
mockInstance.EXPECT().
ParachainHostValidationCode(uint32(1000), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})).
Return(&validationCode, nil)
validCandidateCommitments := parachaintypes.CandidateCommitments{
HeadData: parachaintypes.HeadData{Data: []byte{2, 0, 0, 0, 0, 0, 0, 0, 123, 207, 206, 8, 219, 227,
136, 82, 236, 169, 14, 100, 45, 100, 31, 177, 154, 160, 220, 245, 59, 106, 76, 168, 122, 109,
164, 169, 22, 46, 144, 39, 103, 92, 31, 78, 66, 72, 252, 64, 24, 194, 129, 162, 128, 1, 77, 147,
200, 229, 189, 242, 111, 198, 236, 139, 16, 143, 19, 245, 113, 233, 138, 210}},
ProcessedDownwardMessages: 0,
HrmpWatermark: 1,
}
mockInstance.EXPECT().ParachainHostCheckValidationOutputs(parachaintypes.ParaID(1000),
validCandidateCommitments).Return(true, nil)

mockInstance.EXPECT().
ParachainHostPersistedValidationData(
Expand Down Expand Up @@ -355,9 +372,26 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T)
ParachainHostValidationCode(uint32(5), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})).
Return(&validationCode, nil)

mockInstance.EXPECT().
ParachainHostPersistedValidationData(uint32(6), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})).
Return(&expectedPersistedValidationData, nil)
mockInstance.EXPECT().
ParachainHostValidationCode(uint32(6), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})).
Return(&validationCode, nil)
mockInstance.EXPECT().ParachainHostCheckValidationOutputs(parachaintypes.ParaID(6),
validCandidateCommitments).Return(false, nil)

mockInstance.EXPECT().
ParachainHostPersistedValidationData(uint32(7), gomock.AssignableToTypeOf(parachaintypes.
OccupiedCoreAssumption{})).
Return(nil, nil)
mockInstance.EXPECT().
ParachainHostValidationCode(uint32(7), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})).
Return(&validationCode, nil)

mockBlockState := NewMockBlockState(ctrl)
mockBlockState.EXPECT().GetRuntime(common.MustHexToHash(
"0xded542bacb3ca6c033a57676f94ae7c8f36834511deb44e3164256fd3b1c0de0")).Return(mockInstance, nil).Times(5)
"0xded542bacb3ca6c033a57676f94ae7c8f36834511deb44e3164256fd3b1c0de0")).Return(mockInstance, nil).Times(7)

bd, err := scale.Marshal(BlockDataInAdderParachain{
State: uint64(1),
Expand Down Expand Up @@ -418,21 +452,32 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T)
Invalid: &badSignature,
},
},
"invalid_outputs": {
msg: ValidateFromChainState{
CandidateReceipt: candidateReceipt6,
Pov: pov,
},
want: &ValidationResult{
Invalid: &invalidOutputs,
},
},
"bad_parent": {
msg: ValidateFromChainState{
CandidateReceipt: candidateReceipt7,
Pov: pov,
},
want: &ValidationResult{
Invalid: &badParent,
},
},
"happy_path": {
msg: ValidateFromChainState{
CandidateReceipt: candidateReceipt,
Pov: pov,
},
want: &ValidationResult{
Valid: &Valid{
CandidateCommitments: parachaintypes.CandidateCommitments{
HeadData: parachaintypes.HeadData{Data: []byte{2, 0, 0, 0, 0, 0, 0, 0, 123, 207, 206, 8, 219, 227,
136, 82, 236, 169, 14, 100, 45, 100, 31, 177, 154, 160, 220, 245, 59, 106, 76, 168, 122, 109,
164, 169, 22, 46, 144, 39, 103, 92, 31, 78, 66, 72, 252, 64, 24, 194, 129, 162, 128, 1, 77, 147,
200, 229, 189, 242, 111, 198, 236, 139, 16, 143, 19, 245, 113, 233, 138, 210}},
ProcessedDownwardMessages: 0,
HrmpWatermark: 1,
},
CandidateCommitments: validCandidateCommitments,
PersistedValidationData: parachaintypes.PersistedValidationData{
ParentHead: parachaintypes.HeadData{Data: []byte{1, 0, 0, 0, 0, 0, 0, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
Expand Down
2 changes: 1 addition & 1 deletion dot/parachain/candidate-validation/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

func TestHost_validate(t *testing.T) {
t.Parallel()
candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t)
candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCodeWParaId(t, 1000)
candidateReceipt2 := candidateReceipt
candidateReceipt2.Descriptor.PovHash = common.MustHexToHash(
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ package candidatevalidation
//go:generate mockgen -destination=mocks_test.go -package=$GOPACKAGE . PoVRequestor
//go:generate mockgen -destination=mocks_blockstate_test.go -package=$GOPACKAGE . BlockState
//go:generate mockgen -destination=mocks_instance_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/lib/runtime Instance
//go:generate mockgen -destination=mocks_validation_instance_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/dot/parachain/runtime ValidatorInstance

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 54 additions & 4 deletions dot/parachain/candidate-validation/worker.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package candidatevalidation

import (
"fmt"
"time"

parachainruntime "github.com/ChainSafe/gossamer/dot/parachain/runtime"
parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types"
)
Expand All @@ -16,6 +19,7 @@ type workerTask struct {
work parachainruntime.ValidationParameters
maxPoVSize uint32
candidateReceipt *parachaintypes.CandidateReceipt
timeoutKind parachaintypes.PvfExecTimeoutKind
}

func newWorker(validationCode parachaintypes.ValidationCode) (*worker, error) {
Expand All @@ -31,7 +35,30 @@ func newWorker(validationCode parachaintypes.ValidationCode) (*worker, error) {
}, nil
}

type resultWithError struct {
result *parachainruntime.ValidationResult
err error
}

func determineTimeout(timeoutKind parachaintypes.PvfExecTimeoutKind) time.Duration {
value, err := timeoutKind.Value()
if err != nil {
return 2 * time.Second
}
switch value.(type) {
case parachaintypes.Backing:
return 2 * time.Second
case parachaintypes.Approval:
return 12 * time.Second
default:
return 2 * time.Second
edwardmack marked this conversation as resolved.
Show resolved Hide resolved
}
}

func (w *worker) executeRequest(task *workerTask) (*ValidationResult, error) {
if task == nil {
return nil, fmt.Errorf("task is nil")
}
logger.Debugf("[EXECUTING] worker %x task %v", w.workerID, task.work)
candidateHash, err := parachaintypes.GetCandidateHash(task.candidateReceipt)
if err != nil {
Expand All @@ -42,10 +69,33 @@ func (w *worker) executeRequest(task *workerTask) (*ValidationResult, error) {
logger.Debugf("candidate %x already processed", candidateHash)
return processed, nil
}
validationResult, err := w.instance.ValidateBlock(task.work)
if err != nil {
logger.Errorf("executing validate_block: %w", err)
reasonForInvalidity := ExecutionError

var validationResult *parachainruntime.ValidationResult
validationResultCh := make(chan (*resultWithError))
timeoutDuration := determineTimeout(task.timeoutKind)

go func() {
result, err := w.instance.ValidateBlock(task.work)
if err != nil {
validationResultCh <- &resultWithError{result: nil, err: err}
}
validationResultCh <- &resultWithError{
result: result,
}
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
}()

select {
case validationResultWErr := <-validationResultCh:
if validationResultWErr.err != nil {
logger.Errorf("executing validate_block: %w", err)
reasonForInvalidity := ExecutionError
return &ValidationResult{Invalid: &reasonForInvalidity}, nil //nolint
}
validationResult = validationResultWErr.result

case <-time.After(timeoutDuration):
logger.Errorf("validation timed out")
reasonForInvalidity := Timeout
return &ValidationResult{Invalid: &reasonForInvalidity}, nil
}

Expand Down
2 changes: 2 additions & 0 deletions dot/parachain/candidate-validation/worker_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,12 @@ func (v *workerPool) executeRequest(msg *ValidationTask) (*ValidationResult, err
RelayParentNumber: msg.PersistedValidationData.RelayParentNumber,
RelayParentStorageRoot: msg.PersistedValidationData.RelayParentStorageRoot,
}

workTask := &workerTask{
work: validationParams,
maxPoVSize: msg.PersistedValidationData.MaxPovSize,
candidateReceipt: msg.CandidateReceipt,
timeoutKind: msg.PvfExecTimeoutKind,
}
return worker.executeRequest(workTask)

Expand Down
Loading
Loading