diff --git a/dot/parachain/candidate-validation/candidate_validation.go b/dot/parachain/candidate-validation/candidate_validation.go index ef6674d979..e38c71a161 100644 --- a/dot/parachain/candidate-validation/candidate_validation.go +++ b/dot/parachain/candidate-validation/candidate_validation.go @@ -172,14 +172,23 @@ 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{}, + PvfExecTimeoutKind: msg.ExecKind, } result, err := cv.pvfHost.validate(validationTask) @@ -187,9 +196,33 @@ func (cv *CandidateValidation) validateFromChainState(msg ValidateFromChainState 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, } } diff --git a/dot/parachain/candidate-validation/candidate_validation_test.go b/dot/parachain/candidate-validation/candidate_validation_test.go index 9e2050ddcc..a62cd57c1e 100644 --- a/dot/parachain/candidate-validation/candidate_validation_test.go +++ b/dot/parachain/candidate-validation/candidate_validation_test.go @@ -22,10 +22,13 @@ 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) { + t.Helper() // this wasm was achieved by building polkadot's adder test parachain runtimeFilePath := "./testdata/test_parachain_adder.wasm" validationCodeBytes, err := os.ReadFile(runtimeFilePath) @@ -38,7 +41,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"), @@ -107,7 +110,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") @@ -261,7 +264,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") @@ -278,6 +281,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) @@ -318,6 +326,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( @@ -355,9 +373,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), @@ -418,6 +453,24 @@ 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, @@ -425,14 +478,7 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) }, 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, diff --git a/dot/parachain/candidate-validation/host_test.go b/dot/parachain/candidate-validation/host_test.go index 588f2526a4..6005f255f2 100644 --- a/dot/parachain/candidate-validation/host_test.go +++ b/dot/parachain/candidate-validation/host_test.go @@ -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") diff --git a/dot/parachain/candidate-validation/mocks_generate_test.go b/dot/parachain/candidate-validation/mocks_generate_test.go index 4cab5225ad..c8f6f93b07 100644 --- a/dot/parachain/candidate-validation/mocks_generate_test.go +++ b/dot/parachain/candidate-validation/mocks_generate_test.go @@ -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 diff --git a/dot/parachain/candidate-validation/mocks_validation_instance_test.go b/dot/parachain/candidate-validation/mocks_validation_instance_test.go new file mode 100644 index 0000000000..519357cfe7 --- /dev/null +++ b/dot/parachain/candidate-validation/mocks_validation_instance_test.go @@ -0,0 +1,55 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ChainSafe/gossamer/dot/parachain/runtime (interfaces: ValidatorInstance) +// +// Generated by this command: +// +// mockgen -destination=mocks_validation_instance_test.go -package candidatevalidation github.com/ChainSafe/gossamer/dot/parachain/runtime ValidatorInstance +// + +// Package candidatevalidation is a generated GoMock package. +package candidatevalidation + +import ( + reflect "reflect" + + parachain "github.com/ChainSafe/gossamer/dot/parachain/runtime" + gomock "go.uber.org/mock/gomock" +) + +// MockValidatorInstance is a mock of ValidatorInstance interface. +type MockValidatorInstance struct { + ctrl *gomock.Controller + recorder *MockValidatorInstanceMockRecorder +} + +// MockValidatorInstanceMockRecorder is the mock recorder for MockValidatorInstance. +type MockValidatorInstanceMockRecorder struct { + mock *MockValidatorInstance +} + +// NewMockValidatorInstance creates a new mock instance. +func NewMockValidatorInstance(ctrl *gomock.Controller) *MockValidatorInstance { + mock := &MockValidatorInstance{ctrl: ctrl} + mock.recorder = &MockValidatorInstanceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockValidatorInstance) EXPECT() *MockValidatorInstanceMockRecorder { + return m.recorder +} + +// ValidateBlock mocks base method. +func (m *MockValidatorInstance) ValidateBlock(arg0 parachain.ValidationParameters) (*parachain.ValidationResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateBlock", arg0) + ret0, _ := ret[0].(*parachain.ValidationResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateBlock indicates an expected call of ValidateBlock. +func (mr *MockValidatorInstanceMockRecorder) ValidateBlock(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateBlock", reflect.TypeOf((*MockValidatorInstance)(nil).ValidateBlock), arg0) +} diff --git a/dot/parachain/candidate-validation/worker.go b/dot/parachain/candidate-validation/worker.go index 6781316594..a58265878c 100644 --- a/dot/parachain/candidate-validation/worker.go +++ b/dot/parachain/candidate-validation/worker.go @@ -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" ) @@ -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) { @@ -31,7 +35,28 @@ 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.Approval: + return 12 * time.Second + default: + return 2 * time.Second + } +} + 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 { @@ -42,10 +67,34 @@ 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} + } else { + validationResultCh <- &resultWithError{ + result: result, + } + } + }() + + 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 } diff --git a/dot/parachain/candidate-validation/worker_pool.go b/dot/parachain/candidate-validation/worker_pool.go index 828f37afac..e883e9d01f 100644 --- a/dot/parachain/candidate-validation/worker_pool.go +++ b/dot/parachain/candidate-validation/worker_pool.go @@ -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) diff --git a/dot/parachain/candidate-validation/worker_test.go b/dot/parachain/candidate-validation/worker_test.go new file mode 100644 index 0000000000..f4c8c0eead --- /dev/null +++ b/dot/parachain/candidate-validation/worker_test.go @@ -0,0 +1,165 @@ +package candidatevalidation + +import ( + "testing" + "time" + + parachain "github.com/ChainSafe/gossamer/dot/parachain/runtime" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/pkg/scale" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func Test_worker_executeRequest(t *testing.T) { + candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCodeWParaId(t, 1000) + + validationRuntime, err := parachain.SetupVM(validationCode) + require.NoError(t, err) + + ctrl := gomock.NewController(t) + t.Cleanup(ctrl.Finish) + + expectedValidationResult := &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, + }, + 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, + 8, 9, 0, 1, 2, 48, 246, 146, 178, 86, 226, 64, 9, + 188, 179, 77, 14, 232, 77, 167, 60, 41, 138, 250, 204, 9, 36, 224, 17, 5, 226, 235, + 15, 1, 168, 127, 226}}, + RelayParentNumber: 1, + RelayParentStorageRoot: common.MustHexToHash( + "0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + MaxPovSize: 2048, + }, + }, + } + + mockValidationInstance := NewMockValidatorInstance(ctrl) + mockValidationInstance.EXPECT().ValidateBlock(gomock.Any()).DoAndReturn(func(parachain. + ValidationParameters) (*parachain.ValidationResult, error) { + time.Sleep(3 * time.Second) // sleep to simulate execution time + return ¶chain.ValidationResult{ + 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, + }, nil + }).Times(2) + + candidateReceiptCommitmentsMismatch := candidateReceipt + candidateReceiptCommitmentsMismatch.CommitmentsHash = common.MustHexToHash( + "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") + + // NOTE: adder parachain internally compares postState with bd.State in it's validate_block, + // so following is necessary. + encodedState, err := scale.Marshal(uint64(1)) + require.NoError(t, err) + postState, err := common.Keccak256(encodedState) + require.NoError(t, err) + + hd, err := scale.Marshal(HeadDataInAdderParachain{ + Number: uint64(1), + ParentHash: common.MustHexToHash("0x0102030405060708090001020304050607080900010203040506070809000102"), + PostState: postState, + }) + require.NoError(t, err) + + blockData, err := scale.Marshal(BlockDataInAdderParachain{ + State: uint64(1), + Add: uint64(1), + }) + require.NoError(t, err) + + timeoutKind := parachaintypes.NewPvfExecTimeoutKind() + err = timeoutKind.SetValue(parachaintypes.Approval{}) + require.NoError(t, err) + + commitmentsHashMismatch := CommitmentsHashMismatch + timeout := Timeout + + tests := map[string]struct { + instance parachain.ValidatorInstance + task *workerTask + want *ValidationResult + }{ + "commitments_hash_mismatch": { + instance: validationRuntime, + task: &workerTask{ + work: parachain.ValidationParameters{ + ParentHeadData: parachaintypes.HeadData{Data: hd}, + BlockData: blockData, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + }, + maxPoVSize: uint32(2048), + candidateReceipt: &candidateReceiptCommitmentsMismatch, + }, + want: &ValidationResult{ + Invalid: &commitmentsHashMismatch, + }, + }, + "execution_timeout": { + instance: mockValidationInstance, + task: &workerTask{ + candidateReceipt: &candidateReceipt, + }, + want: &ValidationResult{ + Invalid: &timeout, + }, + }, + "long_timeout_ok": { + instance: mockValidationInstance, + task: &workerTask{ + work: parachain.ValidationParameters{ + ParentHeadData: parachaintypes.HeadData{Data: hd}, + BlockData: blockData, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + }, + candidateReceipt: &candidateReceipt, + timeoutKind: timeoutKind, + maxPoVSize: 2048, + }, + want: expectedValidationResult, + }, + "happy_path": { + instance: validationRuntime, + task: &workerTask{ + work: parachain.ValidationParameters{ + ParentHeadData: parachaintypes.HeadData{Data: hd}, + BlockData: blockData, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + }, + maxPoVSize: uint32(2048), + candidateReceipt: &candidateReceipt, + timeoutKind: parachaintypes.PvfExecTimeoutKind{}, + }, + want: expectedValidationResult, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + w := &worker{ + instance: tt.instance, + isProcessed: make(map[parachaintypes.CandidateHash]*ValidationResult), + } + got, err := w.executeRequest(tt.task) + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +}