diff --git a/dot/parachain/backing/candidate_backing_test.go b/dot/parachain/backing/candidate_backing_test.go index f2bbcb7ee7..eb2c29da12 100644 --- a/dot/parachain/backing/candidate_backing_test.go +++ b/dot/parachain/backing/candidate_backing_test.go @@ -731,7 +731,7 @@ func TestValidateAndMakeAvailable(t *testing.T) { ci := candidatevalidation.ExecutionError data.Ch <- parachaintypes.OverseerFuncRes[candidatevalidation.ValidationResult]{ Data: candidatevalidation.ValidationResult{ - InvalidResult: &ci, + Invalid: &ci, }, } default: @@ -768,7 +768,7 @@ func TestValidateAndMakeAvailable(t *testing.T) { case candidatevalidation.ValidateFromExhaustive: data.Ch <- parachaintypes.OverseerFuncRes[candidatevalidation.ValidationResult]{ Data: candidatevalidation.ValidationResult{ - ValidResult: &candidatevalidation.ValidValidationResult{}, + Valid: &candidatevalidation.Valid{}, }, } case availabilitystore.StoreAvailableData: diff --git a/dot/parachain/backing/integration_test.go b/dot/parachain/backing/integration_test.go index 2066acef9c..60162248ef 100644 --- a/dot/parachain/backing/integration_test.go +++ b/dot/parachain/backing/integration_test.go @@ -227,7 +227,7 @@ func validResponseForValidateFromExhaustive( msgValidate.Ch <- parachaintypes.OverseerFuncRes[candidatevalidation.ValidationResult]{ Data: candidatevalidation.ValidationResult{ - ValidResult: &candidatevalidation.ValidValidationResult{ + Valid: &candidatevalidation.Valid{ CandidateCommitments: parachaintypes.CandidateCommitments{ HeadData: headData, UpwardMessages: []parachaintypes.UpwardMessage{}, @@ -337,7 +337,7 @@ func TestSecondsValidCandidate(t *testing.T) { badReturn := candidatevalidation.BadReturn validateFromExhaustive.Ch <- parachaintypes.OverseerFuncRes[candidatevalidation.ValidationResult]{ Data: candidatevalidation.ValidationResult{ - InvalidResult: &badReturn, + Invalid: &badReturn, }, } return true diff --git a/dot/parachain/backing/per_relay_parent_state.go b/dot/parachain/backing/per_relay_parent_state.go index d9e90fb5a2..4c4b2861af 100644 --- a/dot/parachain/backing/per_relay_parent_state.go +++ b/dot/parachain/backing/per_relay_parent_state.go @@ -340,8 +340,8 @@ func (rpState *perRelayParentState) validateAndMakeAvailable( bgValidationResult = backgroundValidationResult{ outputs: &backgroundValidationOutputs{ candidateReceipt: candidateReceipt, - candidateCommitments: validationResultRes.Data.ValidResult.CandidateCommitments, - persistedValidationData: validationResultRes.Data.ValidResult.PersistedValidationData, + candidateCommitments: validationResultRes.Data.Valid.CandidateCommitments, + persistedValidationData: validationResultRes.Data.Valid.PersistedValidationData, }, candidate: nil, err: nil, @@ -358,11 +358,11 @@ func (rpState *perRelayParentState) validateAndMakeAvailable( } } else { // Invalid - logger.Error(validationResultRes.Data.InvalidResult.Error()) + logger.Error(validationResultRes.Data.Invalid.Error()) bgValidationResult = backgroundValidationResult{ outputs: nil, candidate: &candidateReceipt, - err: fmt.Errorf(validationResultRes.Data.InvalidResult.Error()), + err: fmt.Errorf(validationResultRes.Data.Invalid.Error()), } } diff --git a/dot/parachain/candidate-validation/candidate_validation.go b/dot/parachain/candidate-validation/candidate_validation.go index e0ce583b49..ef6674d979 100644 --- a/dot/parachain/candidate-validation/candidate_validation.go +++ b/dot/parachain/candidate-validation/candidate_validation.go @@ -10,24 +10,15 @@ import ( parachainruntime "github.com/ChainSafe/gossamer/dot/parachain/runtime" parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" - "github.com/ChainSafe/gossamer/internal/log" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/runtime" - "github.com/ChainSafe/gossamer/pkg/scale" -) - -var logger = log.NewFromGlobal(log.AddContext("pkg", "parachain-candidate-validation")) - -var ( - ErrValidationCodeMismatch = errors.New("validation code hash does not match") - ErrValidationInputOverLimit = errors.New("validation input is over the limit") ) // CandidateValidation is a parachain subsystem that validates candidate parachain blocks type CandidateValidation struct { SubsystemToOverseer chan<- any - ValidationHost parachainruntime.ValidationHost BlockState BlockState + pvfHost *host // pvfHost is the host for the parachain validation function } type BlockState interface { @@ -38,6 +29,7 @@ type BlockState interface { func NewCandidateValidation(overseerChan chan<- any, blockState BlockState) *CandidateValidation { candidateValidation := CandidateValidation{ SubsystemToOverseer: overseerChan, + pvfHost: newValidationHost(), BlockState: blockState, } return &candidateValidation @@ -48,7 +40,6 @@ func (cv *CandidateValidation) Run(ctx context.Context, overseerToSubsystem <-ch for { select { case msg := <-overseerToSubsystem: - logger.Debugf("received message %v", msg) cv.processMessage(msg) case <-ctx.Done(): if err := ctx.Err(); err != nil { @@ -77,36 +68,25 @@ func (*CandidateValidation) ProcessBlockFinalizedSignal(parachaintypes.BlockFina } // Stop stops the CandidateValidation subsystem -func (cv *CandidateValidation) Stop() { +func (*CandidateValidation) Stop() { } // processMessage processes messages sent to the CandidateValidation subsystem func (cv *CandidateValidation) processMessage(msg any) { switch msg := msg.(type) { case ValidateFromChainState: - runtimeInstance, err := cv.BlockState.GetRuntime(msg.CandidateReceipt.Descriptor.RelayParent) - if err != nil { - logger.Errorf("failed to get runtime: %w", err) - msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ - Err: err, - } - return - } - result, err := validateFromChainState(runtimeInstance, msg.Pov, msg.CandidateReceipt) - if err != nil { - logger.Errorf("failed to validate from chain state: %w", err) - msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ - Err: err, - } - } else { - msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ - Data: *result, - } + cv.validateFromChainState(msg) + case ValidateFromExhaustive: + validationTask := &ValidationTask{ + PersistedValidationData: msg.PersistedValidationData, + ValidationCode: &msg.ValidationCode, + CandidateReceipt: &msg.CandidateReceipt, + PoV: msg.PoV, + ExecutorParams: msg.ExecutorParams, + PvfExecTimeoutKind: msg.PvfExecTimeoutKind, } - case ValidateFromExhaustive: - result, err := validateFromExhaustive(cv.ValidationHost, msg.PersistedValidationData, - msg.ValidationCode, msg.CandidateReceipt, msg.PoV) + result, err := cv.pvfHost.validate(validationTask) if err != nil { logger.Errorf("failed to validate from exhaustive: %w", err) msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ @@ -171,134 +151,45 @@ func getValidationData(runtimeInstance parachainruntime.RuntimeInstance, paraID return nil, nil, fmt.Errorf("getting persisted validation data: %w", mergedError) } -// validateFromChainState validates a candidate parachain block with provided parameters using relay-chain -// state and using the parachain runtime. -func validateFromChainState(runtimeInstance parachainruntime.RuntimeInstance, pov parachaintypes.PoV, - candidateReceipt parachaintypes.CandidateReceipt) ( - *ValidationResult, error) { - - persistedValidationData, validationCode, err := getValidationData(runtimeInstance, - candidateReceipt.Descriptor.ParaID) +// validateFromChainState validates a parachain block from chain state message +func (cv *CandidateValidation) validateFromChainState(msg ValidateFromChainState) { + runtimeInstance, err := cv.BlockState.GetRuntime(msg.CandidateReceipt.Descriptor.RelayParent) if err != nil { - return nil, fmt.Errorf("getting validation data: %w", err) - } - - parachainRuntimeInstance, err := parachainruntime.SetupVM(*validationCode) - if err != nil { - return nil, fmt.Errorf("setting up VM: %w", err) - } - - validationResults, err := validateFromExhaustive(parachainRuntimeInstance, *persistedValidationData, - *validationCode, candidateReceipt, pov) - if err != nil { - return nil, fmt.Errorf("validating from exhaustive: %w", err) - } - return validationResults, nil -} - -// validateFromExhaustive validates a candidate parachain block with provided parameters -func validateFromExhaustive(validationHost parachainruntime.ValidationHost, - persistedValidationData parachaintypes.PersistedValidationData, - validationCode parachaintypes.ValidationCode, - candidateReceipt parachaintypes.CandidateReceipt, pov parachaintypes.PoV) ( - *ValidationResult, error) { - - validationCodeHash := validationCode.Hash() - // basic checks - validationErr, internalErr := performBasicChecks(&candidateReceipt.Descriptor, persistedValidationData.MaxPovSize, - pov, - validationCodeHash) - if internalErr != nil { - return nil, fmt.Errorf("performing basic checks: %w", internalErr) - } - - if validationErr != nil { - validationResult := &ValidationResult{ - InvalidResult: validationErr, + logger.Errorf("getting runtime instance: %w", err) + msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ + Err: fmt.Errorf("getting runtime instance: %w", err), } - return validationResult, nil //nolint: nilerr - } - - validationParams := parachainruntime.ValidationParameters{ - ParentHeadData: persistedValidationData.ParentHead, - BlockData: pov.BlockData, - RelayParentNumber: persistedValidationData.RelayParentNumber, - RelayParentStorageRoot: persistedValidationData.RelayParentStorageRoot, - } - - validationResult, err := validationHost.ValidateBlock(validationParams) - // TODO: implement functionality to parse errors generated by the runtime when PVF host is implemented, issue #3934 - if err != nil { - return nil, fmt.Errorf("executing validate_block: %w", err) + return } - headDataHash, err := validationResult.HeadData.Hash() - if err != nil { - return nil, fmt.Errorf("hashing head data: %w", err) - } - - if headDataHash != candidateReceipt.Descriptor.ParaHead { - ci := ParaHeadHashMismatch - return &ValidationResult{InvalidResult: &ci}, nil - } - candidateCommitments := parachaintypes.CandidateCommitments{ - UpwardMessages: validationResult.UpwardMessages, - HorizontalMessages: validationResult.HorizontalMessages, - NewValidationCode: validationResult.NewValidationCode, - HeadData: validationResult.HeadData, - ProcessedDownwardMessages: validationResult.ProcessedDownwardMessages, - HrmpWatermark: validationResult.HrmpWatermark, - } - - // if validation produced a new set of commitments, we treat the candidate as invalid - if candidateReceipt.CommitmentsHash != candidateCommitments.Hash() { - ci := CommitmentsHashMismatch - return &ValidationResult{InvalidResult: &ci}, nil - } - return &ValidationResult{ - ValidResult: &ValidValidationResult{ - CandidateCommitments: candidateCommitments, - PersistedValidationData: persistedValidationData, - }, - }, nil - -} - -// performBasicChecks Does basic checks of a candidate. Provide the encoded PoV-block. -// Returns ReasonForInvalidity and internal error if any. -func performBasicChecks(candidate *parachaintypes.CandidateDescriptor, maxPoVSize uint32, - pov parachaintypes.PoV, validationCodeHash parachaintypes.ValidationCodeHash) (validationError *ReasonForInvalidity, - internalError error) { - povHash, err := pov.Hash() - if err != nil { - return nil, fmt.Errorf("hashing PoV: %w", err) - } - - encodedPoV, err := scale.Marshal(pov) + persistedValidationData, validationCode, err := getValidationData(runtimeInstance, + msg.CandidateReceipt.Descriptor.ParaID) if err != nil { - return nil, fmt.Errorf("encoding PoV: %w", err) - } - encodedPoVSize := uint32(len(encodedPoV)) - - if encodedPoVSize > maxPoVSize { - ci := ParamsTooLarge - return &ci, nil - } - - if povHash != candidate.PovHash { - ci := PoVHashMismatch - return &ci, nil + logger.Errorf("getting validation data: %w", err) + msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ + Err: fmt.Errorf("getting validation data: %w", err), + } + return } - if validationCodeHash != candidate.ValidationCodeHash { - ci := CodeHashMismatch - return &ci, nil + 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{}, } - err = candidate.CheckCollatorSignature() + result, err := cv.pvfHost.validate(validationTask) if err != nil { - ci := BadSignature - return &ci, nil + msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ + Err: err, + } + } else { + msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ + Data: *result, + } } - return nil, nil } diff --git a/dot/parachain/candidate-validation/candidate_validation_test.go b/dot/parachain/candidate-validation/candidate_validation_test.go index 38c3d64a37..9e2050ddcc 100644 --- a/dot/parachain/candidate-validation/candidate_validation_test.go +++ b/dot/parachain/candidate-validation/candidate_validation_test.go @@ -5,11 +5,8 @@ package candidatevalidation import ( "context" - "errors" - "fmt" "os" "testing" - "time" parachainruntime "github.com/ChainSafe/gossamer/dot/parachain/runtime" parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" @@ -20,6 +17,13 @@ import ( "go.uber.org/mock/gomock" ) +var ( + povHashMismatch = PoVHashMismatch + paramsTooLarge = ParamsTooLarge + codeHashMismatch = CodeHashMismatch + badSignature = BadSignature +) + func createTestCandidateReceiptAndValidationCode(t *testing.T) ( parachaintypes.CandidateReceipt, parachaintypes.ValidationCode) { // this wasm was achieved by building polkadot's adder test parachain @@ -82,60 +86,6 @@ func makeValidCandidateDescriptor(t *testing.T, paraID uint32, relayParent commo return descriptor } -func TestValidateFromChainState(t *testing.T) { - - candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) - - bd, err := scale.Marshal(BlockDataInAdderParachain{ - State: uint64(1), - Add: uint64(1), - }) - require.NoError(t, err) - - pov := parachaintypes.PoV{ - BlockData: bd, - } - - // 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) - - expectedPersistedValidationData := parachaintypes.PersistedValidationData{ - ParentHead: parachaintypes.HeadData{Data: hd}, - RelayParentNumber: uint32(1), - RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), - MaxPovSize: uint32(2048), - } - - ctrl := gomock.NewController(t) - - mockInstance := NewMockRuntimeInstance(ctrl) - mockInstance.EXPECT(). - ParachainHostPersistedValidationData( - uint32(1000), - gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})). - Return(&expectedPersistedValidationData, nil) - mockInstance.EXPECT(). - ParachainHostValidationCode(uint32(1000), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})). - Return(&validationCode, nil) - - validationResult, err := validateFromChainState( - mockInstance, pov, candidateReceipt) - require.NoError(t, err) - require.True(t, validationResult.IsValid()) - require.Equal(t, expectedPersistedValidationData, validationResult.ValidResult.PersistedValidationData) -} - type HeadDataInAdderParachain struct { Number uint64 ParentHash [32]byte @@ -147,251 +97,6 @@ type BlockDataInAdderParachain struct { Add uint64 } -func TestCandidateValidation_validateFromExhaustive(t *testing.T) { - t.Parallel() - candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) - candidateReceipt2 := candidateReceipt - candidateReceipt2.Descriptor.PovHash = common.MustHexToHash( - "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") - candidateReceiptParaHeadMismatch := candidateReceipt - candidateReceiptParaHeadMismatch.Descriptor.ParaHead = common.MustHexToHash( - "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") - candidateReceiptCommitmentsMismatch := candidateReceipt - candidateReceiptCommitmentsMismatch.CommitmentsHash = common.MustHexToHash( - "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") - testValidationHost, err := parachainruntime.SetupVM(validationCode) - require.NoError(t, err) - - povHashMismatch := PoVHashMismatch - paramsTooLarge := ParamsTooLarge - codeHashMismatch := CodeHashMismatch - paraHedHashMismatch := ParaHeadHashMismatch - commitmentsHashMismatch := CommitmentsHashMismatch - - ctrl := gomock.NewController(t) - t.Cleanup(ctrl.Finish) - - mockValidationHost := NewMockValidationHost(ctrl) - mockValidationHost.EXPECT().ValidateBlock(gomock.Any()).Return(nil, parachainruntime.ErrHardTimeout).AnyTimes() - - bd, err := scale.Marshal(BlockDataInAdderParachain{ - State: uint64(1), - Add: uint64(1), - }) - require.NoError(t, err) - pov := parachaintypes.PoV{ - BlockData: bd, - } - - // 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) - - type args struct { - validationHost parachainruntime.ValidationHost - persistedValidationData parachaintypes.PersistedValidationData - validationCode parachaintypes.ValidationCode - candidateReceipt parachaintypes.CandidateReceipt - pov parachaintypes.PoV - } - tests := map[string]struct { - args args - want *ValidationResult - expectedError error - isValid bool - }{ - "invalid_pov_hash": { - args: args{ - persistedValidationData: parachaintypes.PersistedValidationData{ - ParentHead: parachaintypes.HeadData{Data: hd}, - RelayParentNumber: uint32(1), - RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), - MaxPovSize: uint32(2048), - }, - validationCode: validationCode, - candidateReceipt: candidateReceipt2, - pov: pov, - }, - want: &ValidationResult{ - InvalidResult: &povHashMismatch, - }, - isValid: false, - }, - "invalid_pov_size": { - args: args{ - persistedValidationData: parachaintypes.PersistedValidationData{ - ParentHead: parachaintypes.HeadData{Data: hd}, - RelayParentNumber: uint32(1), - RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), - MaxPovSize: uint32(10), - }, - validationCode: validationCode, - candidateReceipt: candidateReceipt, - pov: pov, - }, - want: &ValidationResult{ - InvalidResult: ¶msTooLarge, - }, - }, - "code_mismatch": { - args: args{ - persistedValidationData: parachaintypes.PersistedValidationData{ - ParentHead: parachaintypes.HeadData{Data: hd}, - RelayParentNumber: uint32(1), - RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), - MaxPovSize: uint32(2048), - }, - validationCode: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - candidateReceipt: candidateReceipt, - pov: pov, - }, - want: &ValidationResult{ - InvalidResult: &codeHashMismatch, - }, - isValid: false, - }, - "mock_test": { - args: args{ - persistedValidationData: parachaintypes.PersistedValidationData{ - ParentHead: parachaintypes.HeadData{Data: hd}, - RelayParentNumber: uint32(1), - RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), - MaxPovSize: uint32(2048), - }, - validationHost: mockValidationHost, - validationCode: validationCode, - candidateReceipt: candidateReceipt, - pov: pov, - }, - want: nil, - expectedError: fmt.Errorf("executing validate_block: %w", parachainruntime.ErrHardTimeout), - }, - "wasm_error_unreachable": { - args: args{ - persistedValidationData: parachaintypes.PersistedValidationData{ - MaxPovSize: uint32(2048), - }, - validationHost: testValidationHost, - validationCode: validationCode, - candidateReceipt: candidateReceipt, - pov: pov, - }, - want: nil, - expectedError: errors.New("executing validate_block: running runtime function: wasm error: unreachable" + - "\nwasm stack trace:\n\t.rust_begin_unwind(i32)\n\t._ZN4core9panicking9panic_fmt17h55a9886e2bf4227aE(" + - "i32,i32)\n\t\t0xcbc: /rustc/1c42cb4ef0544fbfaa500216e53382d6b079c001/library/core/src/panicking." + - "rs:67:14\n\t._ZN4core6result13unwrap_failed17h18cc772327ac51f6E(i32,i32,i32,i32," + - "i32)\n\t\t0xfe9: /rustc/1c42cb4ef0544fbfaa500216e53382d6b079c001/library/core/src/result." + - "rs:1651:5\n\t.validate_block(i32,i32) i64"), - }, - "para_head_hash_mismatch": { - args: args{ - persistedValidationData: parachaintypes.PersistedValidationData{ - ParentHead: parachaintypes.HeadData{Data: hd}, - RelayParentNumber: uint32(1), - RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), - MaxPovSize: uint32(2048), - }, - validationHost: testValidationHost, - validationCode: validationCode, - candidateReceipt: candidateReceiptParaHeadMismatch, - pov: pov, - }, - want: &ValidationResult{ - InvalidResult: ¶HedHashMismatch, - }, - isValid: false, - }, - "commitments_hash_mismatch": { - args: args{ - persistedValidationData: parachaintypes.PersistedValidationData{ - ParentHead: parachaintypes.HeadData{Data: hd}, - RelayParentNumber: uint32(1), - RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), - MaxPovSize: uint32(2048), - }, - validationHost: testValidationHost, - validationCode: validationCode, - candidateReceipt: candidateReceiptCommitmentsMismatch, - pov: pov, - }, - want: &ValidationResult{ - InvalidResult: &commitmentsHashMismatch, - }, - isValid: false, - }, - "happy_path": { - args: args{ - persistedValidationData: parachaintypes.PersistedValidationData{ - ParentHead: parachaintypes.HeadData{Data: hd}, - RelayParentNumber: uint32(1), - RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), - MaxPovSize: uint32(2048), - }, - validationHost: testValidationHost, - validationCode: validationCode, - candidateReceipt: candidateReceipt, - pov: pov, - }, - want: &ValidationResult{ - ValidResult: &ValidValidationResult{ - CandidateCommitments: parachaintypes.CandidateCommitments{ - UpwardMessages: nil, - HorizontalMessages: nil, - NewValidationCode: nil, - 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, - }, - }, - }, - isValid: true, - }, - } - for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { - t.Parallel() - got, err := validateFromExhaustive(tt.args.validationHost, tt.args.persistedValidationData, - tt.args.validationCode, - tt.args.candidateReceipt, tt.args.pov) - if tt.expectedError != nil { - require.EqualError(t, err, tt.expectedError.Error()) - } else { - require.NoError(t, err) - } - require.Equal(t, tt.want, got) - if got != nil { - require.Equal(t, tt.isValid, got.IsValid()) - } - }) - } -} - func TestCandidateValidation_wasm_invalid_magic_number(t *testing.T) { validationCode := parachaintypes.ValidationCode{1, 2, 3, 4, 5, 6, 7, 8} parachainRuntimeInstance, err := parachainruntime.SetupVM(validationCode) @@ -400,16 +105,12 @@ func TestCandidateValidation_wasm_invalid_magic_number(t *testing.T) { } func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) { - povHashMismatch := PoVHashMismatch - paramsTooLarge := ParamsTooLarge - codeHashMismatch := CodeHashMismatch + t.Parallel() candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) candidateReceipt2 := candidateReceipt candidateReceipt2.Descriptor.PovHash = common.MustHexToHash( "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") - testValidationHost, err := parachainruntime.SetupVM(validationCode) - require.NoError(t, err) bd, err := scale.Marshal(BlockDataInAdderParachain{ State: uint64(1), @@ -436,9 +137,10 @@ func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) overseerToSubsystem := make(chan any) sender := make(chan parachaintypes.OverseerFuncRes[ValidationResult]) candidateValidationSubsystem := CandidateValidation{ - ValidationHost: testValidationHost, + pvfHost: newValidationHost(), } - defer candidateValidationSubsystem.Stop() + + t.Cleanup(candidateValidationSubsystem.Stop) ctx := context.Background() go candidateValidationSubsystem.Run(ctx, overseerToSubsystem) @@ -462,7 +164,7 @@ func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) }, want: parachaintypes.OverseerFuncRes[ValidationResult]{ Data: ValidationResult{ - InvalidResult: &povHashMismatch, + Invalid: &povHashMismatch, }, Err: nil, }, @@ -482,7 +184,7 @@ func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) }, want: parachaintypes.OverseerFuncRes[ValidationResult]{ Data: ValidationResult{ - InvalidResult: ¶msTooLarge, + Invalid: ¶msTooLarge, }, }, }, @@ -501,7 +203,7 @@ func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) }, want: parachaintypes.OverseerFuncRes[ValidationResult]{ Data: ValidationResult{ - InvalidResult: &codeHashMismatch, + Invalid: &codeHashMismatch, }, }, }, @@ -520,7 +222,7 @@ func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) }, want: parachaintypes.OverseerFuncRes[ValidationResult]{ Data: ValidationResult{ - ValidResult: &ValidValidationResult{ + 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, @@ -546,130 +248,19 @@ func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) }, } for name, tt := range tests { + tt := tt t.Run(name, func(t *testing.T) { - time.Sleep(100 * time.Millisecond) + t.Parallel() overseerToSubsystem <- tt.msg - time.Sleep(100 * time.Millisecond) result := <-sender require.Equal(t, tt.want, result) }) } } -func Test_performBasicChecks(t *testing.T) { - paramsTooLarge := ParamsTooLarge - povHashMismatch := PoVHashMismatch - codeHashMismatch := CodeHashMismatch - badSignature := BadSignature - - pov := parachaintypes.PoV{ - BlockData: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - } - povHash, err := pov.Hash() - pov2 := parachaintypes.PoV{ - BlockData: []byte{1, 1, 1, 1, 1}, - } - validationCode := parachaintypes.ValidationCode{1, 2, 3} - validationCodeHash := validationCode.Hash() - - require.NoError(t, err) - collatorKeypair, err := sr25519.GenerateKeypair() - require.NoError(t, err) - collatorID, err := sr25519.NewPublicKey(collatorKeypair.Public().Encode()) - require.NoError(t, err) - - candidate := parachaintypes.CandidateDescriptor{ - Collator: collatorID.AsBytes(), - PovHash: povHash, - ValidationCodeHash: validationCodeHash, - } - candidate2 := candidate - - payload, err := candidate.CreateSignaturePayload() - require.NoError(t, err) - - signatureBytes, err := collatorKeypair.Sign(payload) - require.NoError(t, err) - - signature := [sr25519.SignatureLength]byte{} - copy(signature[:], signatureBytes) - - signature2Bytes, err := collatorKeypair.Sign([]byte{1, 2, 3, 4, 5, 6, 7, 8}) - require.NoError(t, err) - signature2 := [sr25519.SignatureLength]byte{} - copy(signature2[:], signature2Bytes) - - candidate.Signature = parachaintypes.CollatorSignature(signature) - candidate2.Signature = parachaintypes.CollatorSignature(signature2) - - type args struct { - candidate *parachaintypes.CandidateDescriptor - maxPoVSize uint32 - pov parachaintypes.PoV - validationCodeHash parachaintypes.ValidationCodeHash - } - tests := map[string]struct { - args args - expectedError *ReasonForInvalidity - }{ - "params_too_large": { - args: args{ - candidate: &candidate, - maxPoVSize: 2, - pov: pov, - }, - expectedError: ¶msTooLarge, - }, - "invalid_pov_hash": { - args: args{ - candidate: &candidate, - maxPoVSize: 1024, - pov: pov2, - }, - expectedError: &povHashMismatch, - }, - "invalid_code_hash": { - args: args{ - candidate: &candidate, - maxPoVSize: 1024, - pov: pov, - validationCodeHash: parachaintypes.ValidationCodeHash{1, 2, 3}, - }, - expectedError: &codeHashMismatch, - }, - "invalid_signature": { - args: args{ - candidate: &candidate2, - maxPoVSize: 1024, - pov: pov, - validationCodeHash: validationCodeHash, - }, - expectedError: &badSignature, - }, - "happy_path": { - args: args{ - candidate: &candidate, - maxPoVSize: 1024, - pov: pov, - validationCodeHash: validationCodeHash, - }, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - validationError, _ := performBasicChecks(tt.args.candidate, tt.args.maxPoVSize, tt.args.pov, - tt.args.validationCodeHash) - if tt.expectedError != nil { - require.EqualError(t, validationError, tt.expectedError.Error()) - } else { - require.Nil(t, validationError) - } - }) - } -} - -func TestCandidateValidation_validateFromChainState(t *testing.T) { +func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) { t.Parallel() + candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) candidateReceipt2 := candidateReceipt candidateReceipt2.Descriptor.PovHash = common.MustHexToHash( @@ -687,11 +278,6 @@ func TestCandidateValidation_validateFromChainState(t *testing.T) { candidateReceipt5 := candidateReceipt candidateReceipt5.Descriptor.ParaID = 5 - povHashMismatch := PoVHashMismatch - paramsTooLarge := ParamsTooLarge - codeHashMismatch := CodeHashMismatch - badSignature := BadSignature - ctrl := gomock.NewController(t) t.Cleanup(ctrl.Finish) @@ -723,7 +309,7 @@ func TestCandidateValidation_validateFromChainState(t *testing.T) { MaxPovSize: uint32(10), } - mockInstance := NewMockRuntimeInstance(ctrl) + mockInstance := NewMockInstance(ctrl) mockInstance.EXPECT(). ParachainHostPersistedValidationData( uint32(1000), @@ -769,6 +355,10 @@ func TestCandidateValidation_validateFromChainState(t *testing.T) { ParachainHostValidationCode(uint32(5), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})). Return(&validationCode, nil) + mockBlockState := NewMockBlockState(ctrl) + mockBlockState.EXPECT().GetRuntime(common.MustHexToHash( + "0xded542bacb3ca6c033a57676f94ae7c8f36834511deb44e3164256fd3b1c0de0")).Return(mockInstance, nil).Times(5) + bd, err := scale.Marshal(BlockDataInAdderParachain{ State: uint64(1), Add: uint64(1), @@ -778,39 +368,63 @@ func TestCandidateValidation_validateFromChainState(t *testing.T) { BlockData: bd, } + toSubsystem := make(chan any) + candidateValidationSubsystem := CandidateValidation{ + pvfHost: newValidationHost(), + BlockState: mockBlockState, + } + defer candidateValidationSubsystem.Stop() + + go candidateValidationSubsystem.Run(context.Background(), toSubsystem) + tests := map[string]struct { - candidateReceipt parachaintypes.CandidateReceipt - want *ValidationResult - expectedError error + msg ValidateFromChainState + want *ValidationResult + expectedError error }{ "invalid_pov_hash": { - candidateReceipt: candidateReceipt2, + msg: ValidateFromChainState{ + CandidateReceipt: candidateReceipt2, + Pov: pov, + }, want: &ValidationResult{ - InvalidResult: &povHashMismatch, + Invalid: &povHashMismatch, }, }, "invalid_pov_size": { - candidateReceipt: candidateReceipt3, + msg: ValidateFromChainState{ + CandidateReceipt: candidateReceipt3, + Pov: pov, + }, want: &ValidationResult{ - InvalidResult: ¶msTooLarge, + Invalid: ¶msTooLarge, }, }, "code_mismatch": { - candidateReceipt: candidateReceipt4, + msg: ValidateFromChainState{ + CandidateReceipt: candidateReceipt4, + Pov: pov, + }, want: &ValidationResult{ - InvalidResult: &codeHashMismatch, + Invalid: &codeHashMismatch, }, }, "bad_signature": { - candidateReceipt: candidateReceipt5, + msg: ValidateFromChainState{ + CandidateReceipt: candidateReceipt5, + Pov: pov, + }, want: &ValidationResult{ - InvalidResult: &badSignature, + Invalid: &badSignature, }, }, "happy_path": { - candidateReceipt: candidateReceipt, + msg: ValidateFromChainState{ + CandidateReceipt: candidateReceipt, + Pov: pov, + }, want: &ValidationResult{ - ValidResult: &ValidValidationResult{ + 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, @@ -838,13 +452,13 @@ func TestCandidateValidation_validateFromChainState(t *testing.T) { tt := tt t.Run(name, func(t *testing.T) { t.Parallel() - got, err := validateFromChainState(mockInstance, pov, tt.candidateReceipt) - if tt.expectedError != nil { - require.EqualError(t, err, tt.expectedError.Error()) - } else { - require.NoError(t, err) - } - require.Equal(t, *tt.want, *got) + + sender := make(chan parachaintypes.OverseerFuncRes[ValidationResult]) + tt.msg.Ch = sender + + toSubsystem <- tt.msg + result := <-sender + require.Equal(t, tt.want, &result.Data) }) } } diff --git a/dot/parachain/candidate-validation/host.go b/dot/parachain/candidate-validation/host.go new file mode 100644 index 0000000000..8b192d82e4 --- /dev/null +++ b/dot/parachain/candidate-validation/host.go @@ -0,0 +1,80 @@ +package candidatevalidation + +import ( + "fmt" + + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + "github.com/ChainSafe/gossamer/internal/log" + "github.com/ChainSafe/gossamer/pkg/scale" +) + +var logger = log.NewFromGlobal(log.AddContext("pkg", "candidatevalidation"), log.SetLevel(log.Debug)) + +// host is the struct that holds the workerPool which is responsible for executing the validation tasks +type host struct { + workerPool *workerPool +} + +func newValidationHost() *host { + return &host{ + workerPool: newWorkerPool(), + } +} + +func (v *host) validate(msg *ValidationTask) (*ValidationResult, error) { + validationCodeHash := msg.ValidationCode.Hash() + validationErr, internalErr := performBasicChecks(&msg.CandidateReceipt.Descriptor, + msg.PersistedValidationData.MaxPovSize, + msg.PoV, + validationCodeHash) + + if internalErr != nil { + return nil, internalErr + } + if validationErr != nil { + return &ValidationResult{Invalid: validationErr}, nil //nolint + } + + // submit request + return v.workerPool.executeRequest(msg) +} + +// performBasicChecks does basic checks of a candidate. Provided the encoded PoV-block it returns ReasonForInvalidity +// and internal error if any. +func performBasicChecks(candidate *parachaintypes.CandidateDescriptor, maxPoVSize uint32, + pov parachaintypes.PoV, validationCodeHash parachaintypes.ValidationCodeHash) ( + validationError *ReasonForInvalidity, internalError error) { + povHash, err := pov.Hash() + if err != nil { + return nil, fmt.Errorf("hashing PoV: %w", err) + } + + encodedPoV, err := scale.Marshal(pov) + if err != nil { + return nil, fmt.Errorf("encoding PoV: %w", err) + } + encodedPoVSize := uint32(len(encodedPoV)) + + if encodedPoVSize > maxPoVSize { + ci := ParamsTooLarge + return &ci, nil + } + + if povHash != candidate.PovHash { + ci := PoVHashMismatch + return &ci, nil + } + + if validationCodeHash != candidate.ValidationCodeHash { + ci := CodeHashMismatch + return &ci, nil + } + + err = candidate.CheckCollatorSignature() + if err != nil { + ci := BadSignature + return &ci, nil + } + + return nil, nil +} diff --git a/dot/parachain/candidate-validation/host_test.go b/dot/parachain/candidate-validation/host_test.go new file mode 100644 index 0000000000..588f2526a4 --- /dev/null +++ b/dot/parachain/candidate-validation/host_test.go @@ -0,0 +1,330 @@ +package candidatevalidation + +import ( + "testing" + + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/lib/crypto/sr25519" + "github.com/ChainSafe/gossamer/pkg/scale" + "github.com/stretchr/testify/require" +) + +func TestHost_validate(t *testing.T) { + t.Parallel() + candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) + candidateReceipt2 := candidateReceipt + candidateReceipt2.Descriptor.PovHash = common.MustHexToHash( + "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") + candidateReceiptParaHeadMismatch := candidateReceipt + candidateReceiptParaHeadMismatch.Descriptor.ParaHead = common.MustHexToHash( + "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") + candidateReceiptCommitmentsMismatch := candidateReceipt + candidateReceiptCommitmentsMismatch.CommitmentsHash = common.MustHexToHash( + "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") + + povHashMismatch := PoVHashMismatch + paramsTooLarge := ParamsTooLarge + codeHashMismatch := CodeHashMismatch + paraHedHashMismatch := ParaHeadHashMismatch + commitmentsHashMismatch := CommitmentsHashMismatch + executionError := ExecutionError + + pvfHost := newValidationHost() + + bd, err := scale.Marshal(BlockDataInAdderParachain{ + State: uint64(1), + Add: uint64(1), + }) + require.NoError(t, err) + pov := parachaintypes.PoV{ + BlockData: bd, + } + + // 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) + + tests := map[string]struct { + validationTask *ValidationTask + want *ValidationResult + isValid bool + }{ + "invalid_pov_hash": { + validationTask: &ValidationTask{ + PersistedValidationData: parachaintypes.PersistedValidationData{ + ParentHead: parachaintypes.HeadData{Data: hd}, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + MaxPovSize: uint32(2048), + }, + CandidateReceipt: &candidateReceipt2, + PoV: pov, + PvfExecTimeoutKind: parachaintypes.PvfExecTimeoutKind{}, + ValidationCode: &validationCode, + }, + want: &ValidationResult{ + Invalid: &povHashMismatch, + }, + isValid: false, + }, + "invalid_pov_size": { + validationTask: &ValidationTask{ + PersistedValidationData: parachaintypes.PersistedValidationData{ + ParentHead: parachaintypes.HeadData{Data: hd}, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + MaxPovSize: uint32(10), + }, + ValidationCode: &validationCode, + CandidateReceipt: &candidateReceipt, + PoV: pov, + }, + want: &ValidationResult{ + Invalid: ¶msTooLarge, + }, + }, + "code_mismatch": { + validationTask: &ValidationTask{ + PersistedValidationData: parachaintypes.PersistedValidationData{ + ParentHead: parachaintypes.HeadData{Data: hd}, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + MaxPovSize: uint32(2048), + }, + ValidationCode: ¶chaintypes.ValidationCode{1, 2, 3, 4, 5, 6, 7, 8}, + CandidateReceipt: &candidateReceipt, + PoV: pov, + }, + want: &ValidationResult{ + Invalid: &codeHashMismatch, + }, + isValid: false, + }, + "wasm_error_unreachable": { + validationTask: &ValidationTask{ + PersistedValidationData: parachaintypes.PersistedValidationData{ + MaxPovSize: uint32(2048), + }, + ValidationCode: &validationCode, + CandidateReceipt: &candidateReceipt, + PoV: pov, + }, + want: &ValidationResult{ + Invalid: &executionError, + }, + }, + "para_head_hash_mismatch": { + validationTask: &ValidationTask{ + PersistedValidationData: parachaintypes.PersistedValidationData{ + ParentHead: parachaintypes.HeadData{Data: hd}, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + MaxPovSize: uint32(2048), + }, + ValidationCode: &validationCode, + CandidateReceipt: &candidateReceiptParaHeadMismatch, + PoV: pov, + }, + want: &ValidationResult{ + Invalid: ¶HedHashMismatch, + }, + isValid: false, + }, + "commitments_hash_mismatch": { + validationTask: &ValidationTask{ + PersistedValidationData: parachaintypes.PersistedValidationData{ + ParentHead: parachaintypes.HeadData{Data: hd}, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + MaxPovSize: uint32(2048), + }, + ValidationCode: &validationCode, + CandidateReceipt: &candidateReceiptCommitmentsMismatch, + PoV: pov, + }, + want: &ValidationResult{ + Invalid: &commitmentsHashMismatch, + }, + isValid: false, + }, + "happy_path": { + validationTask: &ValidationTask{ + PersistedValidationData: parachaintypes.PersistedValidationData{ + ParentHead: parachaintypes.HeadData{Data: hd}, + RelayParentNumber: uint32(1), + RelayParentStorageRoot: common.MustHexToHash("0x50c969706800c0e9c3c4565dc2babb25e4a73d1db0dee1bcf7745535a32e7ca1"), + MaxPovSize: uint32(2048), + }, + ValidationCode: &validationCode, + CandidateReceipt: &candidateReceipt, + PoV: pov, + }, + want: &ValidationResult{ + Valid: &Valid{ + CandidateCommitments: parachaintypes.CandidateCommitments{ + UpwardMessages: nil, + HorizontalMessages: nil, + NewValidationCode: nil, + 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, + }, + }, + }, + isValid: true, + }, + } + for name, tt := range tests { + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + + taskResult, err := pvfHost.validate(tt.validationTask) + + require.NoError(t, err) + require.Equal(t, tt.want, taskResult) + require.Equal(t, tt.isValid, taskResult.IsValid()) + }) + } +} + +func TestHost_performBasicChecks(t *testing.T) { + t.Parallel() + paramsTooLarge := ParamsTooLarge + povHashMismatch := PoVHashMismatch + codeHashMismatch := CodeHashMismatch + badSignature := BadSignature + + pov := parachaintypes.PoV{ + BlockData: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + } + povHash, err := pov.Hash() + pov2 := parachaintypes.PoV{ + BlockData: []byte{1, 1, 1, 1, 1}, + } + validationCode := parachaintypes.ValidationCode{1, 2, 3} + validationCodeHash := validationCode.Hash() + + require.NoError(t, err) + collatorKeypair, err := sr25519.GenerateKeypair() + require.NoError(t, err) + collatorID, err := sr25519.NewPublicKey(collatorKeypair.Public().Encode()) + require.NoError(t, err) + + candidate := parachaintypes.CandidateDescriptor{ + Collator: collatorID.AsBytes(), + PovHash: povHash, + ValidationCodeHash: validationCodeHash, + } + candidate2 := candidate + + payload, err := candidate.CreateSignaturePayload() + require.NoError(t, err) + + signatureBytes, err := collatorKeypair.Sign(payload) + require.NoError(t, err) + + signature := [sr25519.SignatureLength]byte{} + copy(signature[:], signatureBytes) + + signature2Bytes, err := collatorKeypair.Sign([]byte{1, 2, 3, 4, 5, 6, 7, 8}) + require.NoError(t, err) + signature2 := [sr25519.SignatureLength]byte{} + copy(signature2[:], signature2Bytes) + + candidate.Signature = parachaintypes.CollatorSignature(signature) + candidate2.Signature = parachaintypes.CollatorSignature(signature2) + + type args struct { + candidate *parachaintypes.CandidateDescriptor + maxPoVSize uint32 + pov parachaintypes.PoV + validationCodeHash parachaintypes.ValidationCodeHash + } + tests := map[string]struct { + args args + expectedError *ReasonForInvalidity + }{ + "params_too_large": { + args: args{ + candidate: &candidate, + maxPoVSize: 2, + pov: pov, + }, + expectedError: ¶msTooLarge, + }, + "invalid_pov_hash": { + args: args{ + candidate: &candidate, + maxPoVSize: 1024, + pov: pov2, + }, + expectedError: &povHashMismatch, + }, + "invalid_code_hash": { + args: args{ + candidate: &candidate, + maxPoVSize: 1024, + pov: pov, + validationCodeHash: parachaintypes.ValidationCodeHash{1, 2, 3}, + }, + expectedError: &codeHashMismatch, + }, + "invalid_signature": { + args: args{ + candidate: &candidate2, + maxPoVSize: 1024, + pov: pov, + validationCodeHash: validationCodeHash, + }, + expectedError: &badSignature, + }, + "happy_path": { + args: args{ + candidate: &candidate, + maxPoVSize: 1024, + pov: pov, + validationCodeHash: validationCodeHash, + }, + }, + } + for name, tt := range tests { + tt := tt + t.Run(name, func(t *testing.T) { + t.Parallel() + validationError, internalError := performBasicChecks(tt.args.candidate, tt.args.maxPoVSize, tt.args.pov, + tt.args.validationCodeHash) + require.NoError(t, internalError) + if tt.expectedError != nil { + require.EqualError(t, validationError, tt.expectedError.Error()) + } else { + require.Nil(t, validationError) + } + }) + } +} diff --git a/dot/parachain/candidate-validation/messages.go b/dot/parachain/candidate-validation/messages.go index ca1b99e6ca..8ac68e2832 100644 --- a/dot/parachain/candidate-validation/messages.go +++ b/dot/parachain/candidate-validation/messages.go @@ -31,92 +31,6 @@ type ValidateFromExhaustive struct { Ch chan parachaintypes.OverseerFuncRes[ValidationResult] } -// ValidationResult represents the result coming from the candidate validation subsystem. -// Validation results can be either a ValidValidationResult or InvalidValidationResult. -// -// If the result is invalid, -// store the reason for invalidity in the InvalidResult field of ValidationResult. -// -// If the result is valid, -// set the values of the ValidResult field of ValidValidationResult. -type ValidationResult struct { - ValidResult *ValidValidationResult - InvalidResult *ReasonForInvalidity -} - -func (vr ValidationResult) IsValid() bool { - return vr.ValidResult != nil -} - -type ValidValidationResult struct { - CandidateCommitments parachaintypes.CandidateCommitments - PersistedValidationData parachaintypes.PersistedValidationData -} - -type ReasonForInvalidity byte - -const ( - // ExecutionError Failed to execute `validate_block`. This includes function panicking. - ExecutionError ReasonForInvalidity = iota - // InvalidOutputs Validation outputs check doesn't pass. - InvalidOutputs - // Timeout Execution timeout. - Timeout - // ParamsTooLarge Validation input is over the limit. - ParamsTooLarge - // CodeTooLarge Code size is over the limit. - CodeTooLarge - // PoVDecompressionFailure PoV does not decompress correctly. - PoVDecompressionFailure - // BadReturn Validation function returned invalid data. - BadReturn - // BadParent Invalid relay chain parent. - BadParent - // PoVHashMismatch POV hash does not match. - PoVHashMismatch - // BadSignature Bad collator signature. - BadSignature - // ParaHeadHashMismatch Para head hash does not match. - ParaHeadHashMismatch - // CodeHashMismatch Validation code hash does not match. - CodeHashMismatch - // CommitmentsHashMismatch Validation has generated different candidate commitments. - CommitmentsHashMismatch -) - -func (ci ReasonForInvalidity) Error() string { - switch ci { - case ExecutionError: - return "failed to execute `validate_block`" - case InvalidOutputs: - return "validation outputs check doesn't pass" - case Timeout: - return "execution timeout" - case ParamsTooLarge: - return "validation input is over the limit" - case CodeTooLarge: - return "code size is over the limit" - case PoVDecompressionFailure: - return "PoV does not decompress correctly" - case BadReturn: - return "validation function returned invalid data" - case BadParent: - return "invalid relay chain parent" - case PoVHashMismatch: - return "PoV hash does not match" - case BadSignature: - return "bad collator signature" - case ParaHeadHashMismatch: - return "para head hash does not match" - case CodeHashMismatch: - return "validation code hash does not match" - case CommitmentsHashMismatch: - return "validation has generated different candidate commitments" - default: - return "unknown invalidity reason" - } -} - // PreCheck try to compile the given validation code and return the result // The validation code is specified by the hash and will be queried from the runtime API at // the given relay-parent. diff --git a/dot/parachain/candidate-validation/mocks_blockstate_test.go b/dot/parachain/candidate-validation/mocks_blockstate_test.go new file mode 100644 index 0000000000..fe0d7c587f --- /dev/null +++ b/dot/parachain/candidate-validation/mocks_blockstate_test.go @@ -0,0 +1,56 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ChainSafe/gossamer/dot/parachain/candidate-validation (interfaces: BlockState) +// +// Generated by this command: +// +// mockgen -destination=mocks_blockstate_test.go -package=candidatevalidation . BlockState +// + +// Package candidatevalidation is a generated GoMock package. +package candidatevalidation + +import ( + reflect "reflect" + + common "github.com/ChainSafe/gossamer/lib/common" + runtime "github.com/ChainSafe/gossamer/lib/runtime" + gomock "go.uber.org/mock/gomock" +) + +// MockBlockState is a mock of BlockState interface. +type MockBlockState struct { + ctrl *gomock.Controller + recorder *MockBlockStateMockRecorder +} + +// MockBlockStateMockRecorder is the mock recorder for MockBlockState. +type MockBlockStateMockRecorder struct { + mock *MockBlockState +} + +// NewMockBlockState creates a new mock instance. +func NewMockBlockState(ctrl *gomock.Controller) *MockBlockState { + mock := &MockBlockState{ctrl: ctrl} + mock.recorder = &MockBlockStateMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBlockState) EXPECT() *MockBlockStateMockRecorder { + return m.recorder +} + +// GetRuntime mocks base method. +func (m *MockBlockState) GetRuntime(arg0 common.Hash) (runtime.Instance, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRuntime", arg0) + ret0, _ := ret[0].(runtime.Instance) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRuntime indicates an expected call of GetRuntime. +func (mr *MockBlockStateMockRecorder) GetRuntime(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRuntime", reflect.TypeOf((*MockBlockState)(nil).GetRuntime), arg0) +} diff --git a/dot/parachain/candidate-validation/mocks_generate_test.go b/dot/parachain/candidate-validation/mocks_generate_test.go index 7bdd10bed1..4cab5225ad 100644 --- a/dot/parachain/candidate-validation/mocks_generate_test.go +++ b/dot/parachain/candidate-validation/mocks_generate_test.go @@ -4,4 +4,5 @@ package candidatevalidation //go:generate mockgen -destination=mocks_test.go -package=$GOPACKAGE . PoVRequestor -//go:generate mockgen -destination=mocks_runtime_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/dot/parachain/runtime RuntimeInstance,ValidationHost +//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 diff --git a/dot/parachain/candidate-validation/mocks_instance_test.go b/dot/parachain/candidate-validation/mocks_instance_test.go new file mode 100644 index 0000000000..22efc588d2 --- /dev/null +++ b/dot/parachain/candidate-validation/mocks_instance_test.go @@ -0,0 +1,650 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ChainSafe/gossamer/lib/runtime (interfaces: Instance) +// +// Generated by this command: +// +// mockgen -destination=mocks_instance_test.go -package candidatevalidation github.com/ChainSafe/gossamer/lib/runtime Instance +// + +// Package candidatevalidation is a generated GoMock package. +package candidatevalidation + +import ( + reflect "reflect" + + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + types "github.com/ChainSafe/gossamer/dot/types" + common "github.com/ChainSafe/gossamer/lib/common" + ed25519 "github.com/ChainSafe/gossamer/lib/crypto/ed25519" + keystore "github.com/ChainSafe/gossamer/lib/keystore" + runtime "github.com/ChainSafe/gossamer/lib/runtime" + transaction "github.com/ChainSafe/gossamer/lib/transaction" + gomock "go.uber.org/mock/gomock" +) + +// MockInstance is a mock of Instance interface. +type MockInstance struct { + ctrl *gomock.Controller + recorder *MockInstanceMockRecorder +} + +// MockInstanceMockRecorder is the mock recorder for MockInstance. +type MockInstanceMockRecorder struct { + mock *MockInstance +} + +// NewMockInstance creates a new mock instance. +func NewMockInstance(ctrl *gomock.Controller) *MockInstance { + mock := &MockInstance{ctrl: ctrl} + mock.recorder = &MockInstanceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockInstance) EXPECT() *MockInstanceMockRecorder { + return m.recorder +} + +// ApplyExtrinsic mocks base method. +func (m *MockInstance) ApplyExtrinsic(arg0 types.Extrinsic) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyExtrinsic", arg0) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyExtrinsic indicates an expected call of ApplyExtrinsic. +func (mr *MockInstanceMockRecorder) ApplyExtrinsic(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyExtrinsic", reflect.TypeOf((*MockInstance)(nil).ApplyExtrinsic), arg0) +} + +// BabeConfiguration mocks base method. +func (m *MockInstance) BabeConfiguration() (*types.BabeConfiguration, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BabeConfiguration") + ret0, _ := ret[0].(*types.BabeConfiguration) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BabeConfiguration indicates an expected call of BabeConfiguration. +func (mr *MockInstanceMockRecorder) BabeConfiguration() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BabeConfiguration", reflect.TypeOf((*MockInstance)(nil).BabeConfiguration)) +} + +// BabeGenerateKeyOwnershipProof mocks base method. +func (m *MockInstance) BabeGenerateKeyOwnershipProof(arg0 uint64, arg1 [32]byte) (types.OpaqueKeyOwnershipProof, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BabeGenerateKeyOwnershipProof", arg0, arg1) + ret0, _ := ret[0].(types.OpaqueKeyOwnershipProof) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BabeGenerateKeyOwnershipProof indicates an expected call of BabeGenerateKeyOwnershipProof. +func (mr *MockInstanceMockRecorder) BabeGenerateKeyOwnershipProof(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BabeGenerateKeyOwnershipProof", reflect.TypeOf((*MockInstance)(nil).BabeGenerateKeyOwnershipProof), arg0, arg1) +} + +// BabeSubmitReportEquivocationUnsignedExtrinsic mocks base method. +func (m *MockInstance) BabeSubmitReportEquivocationUnsignedExtrinsic(arg0 types.BabeEquivocationProof, arg1 types.OpaqueKeyOwnershipProof) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BabeSubmitReportEquivocationUnsignedExtrinsic", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// BabeSubmitReportEquivocationUnsignedExtrinsic indicates an expected call of BabeSubmitReportEquivocationUnsignedExtrinsic. +func (mr *MockInstanceMockRecorder) BabeSubmitReportEquivocationUnsignedExtrinsic(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BabeSubmitReportEquivocationUnsignedExtrinsic", reflect.TypeOf((*MockInstance)(nil).BabeSubmitReportEquivocationUnsignedExtrinsic), arg0, arg1) +} + +// CheckInherents mocks base method. +func (m *MockInstance) CheckInherents() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "CheckInherents") +} + +// CheckInherents indicates an expected call of CheckInherents. +func (mr *MockInstanceMockRecorder) CheckInherents() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckInherents", reflect.TypeOf((*MockInstance)(nil).CheckInherents)) +} + +// DecodeSessionKeys mocks base method. +func (m *MockInstance) DecodeSessionKeys(arg0 []byte) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecodeSessionKeys", arg0) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DecodeSessionKeys indicates an expected call of DecodeSessionKeys. +func (mr *MockInstanceMockRecorder) DecodeSessionKeys(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeSessionKeys", reflect.TypeOf((*MockInstance)(nil).DecodeSessionKeys), arg0) +} + +// Exec mocks base method. +func (m *MockInstance) Exec(arg0 string, arg1 []byte) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Exec", arg0, arg1) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Exec indicates an expected call of Exec. +func (mr *MockInstanceMockRecorder) Exec(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockInstance)(nil).Exec), arg0, arg1) +} + +// ExecuteBlock mocks base method. +func (m *MockInstance) ExecuteBlock(arg0 *types.Block) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteBlock", arg0) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteBlock indicates an expected call of ExecuteBlock. +func (mr *MockInstanceMockRecorder) ExecuteBlock(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBlock", reflect.TypeOf((*MockInstance)(nil).ExecuteBlock), arg0) +} + +// FinalizeBlock mocks base method. +func (m *MockInstance) FinalizeBlock() (*types.Header, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FinalizeBlock") + ret0, _ := ret[0].(*types.Header) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FinalizeBlock indicates an expected call of FinalizeBlock. +func (mr *MockInstanceMockRecorder) FinalizeBlock() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalizeBlock", reflect.TypeOf((*MockInstance)(nil).FinalizeBlock)) +} + +// GenerateSessionKeys mocks base method. +func (m *MockInstance) GenerateSessionKeys() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "GenerateSessionKeys") +} + +// GenerateSessionKeys indicates an expected call of GenerateSessionKeys. +func (mr *MockInstanceMockRecorder) GenerateSessionKeys() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateSessionKeys", reflect.TypeOf((*MockInstance)(nil).GenerateSessionKeys)) +} + +// GetCodeHash mocks base method. +func (m *MockInstance) GetCodeHash() common.Hash { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCodeHash") + ret0, _ := ret[0].(common.Hash) + return ret0 +} + +// GetCodeHash indicates an expected call of GetCodeHash. +func (mr *MockInstanceMockRecorder) GetCodeHash() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCodeHash", reflect.TypeOf((*MockInstance)(nil).GetCodeHash)) +} + +// GrandpaAuthorities mocks base method. +func (m *MockInstance) GrandpaAuthorities() ([]types.Authority, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrandpaAuthorities") + ret0, _ := ret[0].([]types.Authority) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GrandpaAuthorities indicates an expected call of GrandpaAuthorities. +func (mr *MockInstanceMockRecorder) GrandpaAuthorities() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrandpaAuthorities", reflect.TypeOf((*MockInstance)(nil).GrandpaAuthorities)) +} + +// GrandpaGenerateKeyOwnershipProof mocks base method. +func (m *MockInstance) GrandpaGenerateKeyOwnershipProof(arg0 uint64, arg1 ed25519.PublicKeyBytes) (types.GrandpaOpaqueKeyOwnershipProof, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrandpaGenerateKeyOwnershipProof", arg0, arg1) + ret0, _ := ret[0].(types.GrandpaOpaqueKeyOwnershipProof) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GrandpaGenerateKeyOwnershipProof indicates an expected call of GrandpaGenerateKeyOwnershipProof. +func (mr *MockInstanceMockRecorder) GrandpaGenerateKeyOwnershipProof(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrandpaGenerateKeyOwnershipProof", reflect.TypeOf((*MockInstance)(nil).GrandpaGenerateKeyOwnershipProof), arg0, arg1) +} + +// GrandpaSubmitReportEquivocationUnsignedExtrinsic mocks base method. +func (m *MockInstance) GrandpaSubmitReportEquivocationUnsignedExtrinsic(arg0 types.GrandpaEquivocationProof, arg1 types.GrandpaOpaqueKeyOwnershipProof) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrandpaSubmitReportEquivocationUnsignedExtrinsic", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// GrandpaSubmitReportEquivocationUnsignedExtrinsic indicates an expected call of GrandpaSubmitReportEquivocationUnsignedExtrinsic. +func (mr *MockInstanceMockRecorder) GrandpaSubmitReportEquivocationUnsignedExtrinsic(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrandpaSubmitReportEquivocationUnsignedExtrinsic", reflect.TypeOf((*MockInstance)(nil).GrandpaSubmitReportEquivocationUnsignedExtrinsic), arg0, arg1) +} + +// InherentExtrinsics mocks base method. +func (m *MockInstance) InherentExtrinsics(arg0 []byte) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InherentExtrinsics", arg0) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InherentExtrinsics indicates an expected call of InherentExtrinsics. +func (mr *MockInstanceMockRecorder) InherentExtrinsics(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InherentExtrinsics", reflect.TypeOf((*MockInstance)(nil).InherentExtrinsics), arg0) +} + +// InitializeBlock mocks base method. +func (m *MockInstance) InitializeBlock(arg0 *types.Header) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InitializeBlock", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// InitializeBlock indicates an expected call of InitializeBlock. +func (mr *MockInstanceMockRecorder) InitializeBlock(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitializeBlock", reflect.TypeOf((*MockInstance)(nil).InitializeBlock), arg0) +} + +// Keystore mocks base method. +func (m *MockInstance) Keystore() *keystore.GlobalKeystore { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Keystore") + ret0, _ := ret[0].(*keystore.GlobalKeystore) + return ret0 +} + +// Keystore indicates an expected call of Keystore. +func (mr *MockInstanceMockRecorder) Keystore() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Keystore", reflect.TypeOf((*MockInstance)(nil).Keystore)) +} + +// Metadata mocks base method. +func (m *MockInstance) Metadata() ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Metadata") + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Metadata indicates an expected call of Metadata. +func (mr *MockInstanceMockRecorder) Metadata() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Metadata", reflect.TypeOf((*MockInstance)(nil).Metadata)) +} + +// NetworkService mocks base method. +func (m *MockInstance) NetworkService() runtime.BasicNetwork { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NetworkService") + ret0, _ := ret[0].(runtime.BasicNetwork) + return ret0 +} + +// NetworkService indicates an expected call of NetworkService. +func (mr *MockInstanceMockRecorder) NetworkService() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkService", reflect.TypeOf((*MockInstance)(nil).NetworkService)) +} + +// NodeStorage mocks base method. +func (m *MockInstance) NodeStorage() runtime.NodeStorage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NodeStorage") + ret0, _ := ret[0].(runtime.NodeStorage) + return ret0 +} + +// NodeStorage indicates an expected call of NodeStorage. +func (mr *MockInstanceMockRecorder) NodeStorage() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeStorage", reflect.TypeOf((*MockInstance)(nil).NodeStorage)) +} + +// OffchainWorker mocks base method. +func (m *MockInstance) OffchainWorker() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "OffchainWorker") +} + +// OffchainWorker indicates an expected call of OffchainWorker. +func (mr *MockInstanceMockRecorder) OffchainWorker() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OffchainWorker", reflect.TypeOf((*MockInstance)(nil).OffchainWorker)) +} + +// ParachainHostAsyncBackingParams mocks base method. +func (m *MockInstance) ParachainHostAsyncBackingParams() (*parachaintypes.AsyncBackingParams, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostAsyncBackingParams") + ret0, _ := ret[0].(*parachaintypes.AsyncBackingParams) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostAsyncBackingParams indicates an expected call of ParachainHostAsyncBackingParams. +func (mr *MockInstanceMockRecorder) ParachainHostAsyncBackingParams() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostAsyncBackingParams", reflect.TypeOf((*MockInstance)(nil).ParachainHostAsyncBackingParams)) +} + +// ParachainHostAvailabilityCores mocks base method. +func (m *MockInstance) ParachainHostAvailabilityCores() ([]parachaintypes.CoreState, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostAvailabilityCores") + ret0, _ := ret[0].([]parachaintypes.CoreState) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostAvailabilityCores indicates an expected call of ParachainHostAvailabilityCores. +func (mr *MockInstanceMockRecorder) ParachainHostAvailabilityCores() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostAvailabilityCores", reflect.TypeOf((*MockInstance)(nil).ParachainHostAvailabilityCores)) +} + +// ParachainHostCandidateEvents mocks base method. +func (m *MockInstance) ParachainHostCandidateEvents() ([]parachaintypes.CandidateEvent, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostCandidateEvents") + ret0, _ := ret[0].([]parachaintypes.CandidateEvent) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostCandidateEvents indicates an expected call of ParachainHostCandidateEvents. +func (mr *MockInstanceMockRecorder) ParachainHostCandidateEvents() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostCandidateEvents", reflect.TypeOf((*MockInstance)(nil).ParachainHostCandidateEvents)) +} + +// ParachainHostCandidatePendingAvailability mocks base method. +func (m *MockInstance) ParachainHostCandidatePendingAvailability(arg0 parachaintypes.ParaID) (*parachaintypes.CommittedCandidateReceipt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostCandidatePendingAvailability", arg0) + ret0, _ := ret[0].(*parachaintypes.CommittedCandidateReceipt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostCandidatePendingAvailability indicates an expected call of ParachainHostCandidatePendingAvailability. +func (mr *MockInstanceMockRecorder) ParachainHostCandidatePendingAvailability(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostCandidatePendingAvailability", reflect.TypeOf((*MockInstance)(nil).ParachainHostCandidatePendingAvailability), arg0) +} + +// ParachainHostCheckValidationOutputs mocks base method. +func (m *MockInstance) ParachainHostCheckValidationOutputs(arg0 parachaintypes.ParaID, arg1 parachaintypes.CandidateCommitments) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostCheckValidationOutputs", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostCheckValidationOutputs indicates an expected call of ParachainHostCheckValidationOutputs. +func (mr *MockInstanceMockRecorder) ParachainHostCheckValidationOutputs(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostCheckValidationOutputs", reflect.TypeOf((*MockInstance)(nil).ParachainHostCheckValidationOutputs), arg0, arg1) +} + +// ParachainHostMinimumBackingVotes mocks base method. +func (m *MockInstance) ParachainHostMinimumBackingVotes() (uint32, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostMinimumBackingVotes") + ret0, _ := ret[0].(uint32) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostMinimumBackingVotes indicates an expected call of ParachainHostMinimumBackingVotes. +func (mr *MockInstanceMockRecorder) ParachainHostMinimumBackingVotes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostMinimumBackingVotes", reflect.TypeOf((*MockInstance)(nil).ParachainHostMinimumBackingVotes)) +} + +// ParachainHostPersistedValidationData mocks base method. +func (m *MockInstance) ParachainHostPersistedValidationData(arg0 uint32, arg1 parachaintypes.OccupiedCoreAssumption) (*parachaintypes.PersistedValidationData, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostPersistedValidationData", arg0, arg1) + ret0, _ := ret[0].(*parachaintypes.PersistedValidationData) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostPersistedValidationData indicates an expected call of ParachainHostPersistedValidationData. +func (mr *MockInstanceMockRecorder) ParachainHostPersistedValidationData(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostPersistedValidationData", reflect.TypeOf((*MockInstance)(nil).ParachainHostPersistedValidationData), arg0, arg1) +} + +// ParachainHostSessionExecutorParams mocks base method. +func (m *MockInstance) ParachainHostSessionExecutorParams(arg0 parachaintypes.SessionIndex) (*parachaintypes.ExecutorParams, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostSessionExecutorParams", arg0) + ret0, _ := ret[0].(*parachaintypes.ExecutorParams) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostSessionExecutorParams indicates an expected call of ParachainHostSessionExecutorParams. +func (mr *MockInstanceMockRecorder) ParachainHostSessionExecutorParams(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostSessionExecutorParams", reflect.TypeOf((*MockInstance)(nil).ParachainHostSessionExecutorParams), arg0) +} + +// ParachainHostSessionIndexForChild mocks base method. +func (m *MockInstance) ParachainHostSessionIndexForChild() (parachaintypes.SessionIndex, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostSessionIndexForChild") + ret0, _ := ret[0].(parachaintypes.SessionIndex) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostSessionIndexForChild indicates an expected call of ParachainHostSessionIndexForChild. +func (mr *MockInstanceMockRecorder) ParachainHostSessionIndexForChild() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostSessionIndexForChild", reflect.TypeOf((*MockInstance)(nil).ParachainHostSessionIndexForChild)) +} + +// ParachainHostSessionInfo mocks base method. +func (m *MockInstance) ParachainHostSessionInfo(arg0 parachaintypes.SessionIndex) (*parachaintypes.SessionInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostSessionInfo", arg0) + ret0, _ := ret[0].(*parachaintypes.SessionInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostSessionInfo indicates an expected call of ParachainHostSessionInfo. +func (mr *MockInstanceMockRecorder) ParachainHostSessionInfo(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostSessionInfo", reflect.TypeOf((*MockInstance)(nil).ParachainHostSessionInfo), arg0) +} + +// ParachainHostValidationCode mocks base method. +func (m *MockInstance) ParachainHostValidationCode(arg0 uint32, arg1 parachaintypes.OccupiedCoreAssumption) (*parachaintypes.ValidationCode, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostValidationCode", arg0, arg1) + ret0, _ := ret[0].(*parachaintypes.ValidationCode) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostValidationCode indicates an expected call of ParachainHostValidationCode. +func (mr *MockInstanceMockRecorder) ParachainHostValidationCode(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostValidationCode", reflect.TypeOf((*MockInstance)(nil).ParachainHostValidationCode), arg0, arg1) +} + +// ParachainHostValidationCodeByHash mocks base method. +func (m *MockInstance) ParachainHostValidationCodeByHash(arg0 common.Hash) (*parachaintypes.ValidationCode, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostValidationCodeByHash", arg0) + ret0, _ := ret[0].(*parachaintypes.ValidationCode) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostValidationCodeByHash indicates an expected call of ParachainHostValidationCodeByHash. +func (mr *MockInstanceMockRecorder) ParachainHostValidationCodeByHash(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostValidationCodeByHash", reflect.TypeOf((*MockInstance)(nil).ParachainHostValidationCodeByHash), arg0) +} + +// ParachainHostValidatorGroups mocks base method. +func (m *MockInstance) ParachainHostValidatorGroups() (*parachaintypes.ValidatorGroups, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostValidatorGroups") + ret0, _ := ret[0].(*parachaintypes.ValidatorGroups) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostValidatorGroups indicates an expected call of ParachainHostValidatorGroups. +func (mr *MockInstanceMockRecorder) ParachainHostValidatorGroups() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostValidatorGroups", reflect.TypeOf((*MockInstance)(nil).ParachainHostValidatorGroups)) +} + +// ParachainHostValidators mocks base method. +func (m *MockInstance) ParachainHostValidators() ([]parachaintypes.ValidatorID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ParachainHostValidators") + ret0, _ := ret[0].([]parachaintypes.ValidatorID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ParachainHostValidators indicates an expected call of ParachainHostValidators. +func (mr *MockInstanceMockRecorder) ParachainHostValidators() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostValidators", reflect.TypeOf((*MockInstance)(nil).ParachainHostValidators)) +} + +// PaymentQueryInfo mocks base method. +func (m *MockInstance) PaymentQueryInfo(arg0 []byte) (*types.RuntimeDispatchInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PaymentQueryInfo", arg0) + ret0, _ := ret[0].(*types.RuntimeDispatchInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PaymentQueryInfo indicates an expected call of PaymentQueryInfo. +func (mr *MockInstanceMockRecorder) PaymentQueryInfo(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PaymentQueryInfo", reflect.TypeOf((*MockInstance)(nil).PaymentQueryInfo), arg0) +} + +// RandomSeed mocks base method. +func (m *MockInstance) RandomSeed() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RandomSeed") +} + +// RandomSeed indicates an expected call of RandomSeed. +func (mr *MockInstanceMockRecorder) RandomSeed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RandomSeed", reflect.TypeOf((*MockInstance)(nil).RandomSeed)) +} + +// SetContextStorage mocks base method. +func (m *MockInstance) SetContextStorage(arg0 runtime.Storage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetContextStorage", arg0) +} + +// SetContextStorage indicates an expected call of SetContextStorage. +func (mr *MockInstanceMockRecorder) SetContextStorage(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetContextStorage", reflect.TypeOf((*MockInstance)(nil).SetContextStorage), arg0) +} + +// Stop mocks base method. +func (m *MockInstance) Stop() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Stop") +} + +// Stop indicates an expected call of Stop. +func (mr *MockInstanceMockRecorder) Stop() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockInstance)(nil).Stop)) +} + +// ValidateTransaction mocks base method. +func (m *MockInstance) ValidateTransaction(arg0 types.Extrinsic) (*transaction.Validity, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateTransaction", arg0) + ret0, _ := ret[0].(*transaction.Validity) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateTransaction indicates an expected call of ValidateTransaction. +func (mr *MockInstanceMockRecorder) ValidateTransaction(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateTransaction", reflect.TypeOf((*MockInstance)(nil).ValidateTransaction), arg0) +} + +// Validator mocks base method. +func (m *MockInstance) Validator() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Validator") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Validator indicates an expected call of Validator. +func (mr *MockInstanceMockRecorder) Validator() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validator", reflect.TypeOf((*MockInstance)(nil).Validator)) +} + +// Version mocks base method. +func (m *MockInstance) Version() (runtime.Version, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Version") + ret0, _ := ret[0].(runtime.Version) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Version indicates an expected call of Version. +func (mr *MockInstanceMockRecorder) Version() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Version", reflect.TypeOf((*MockInstance)(nil).Version)) +} diff --git a/dot/parachain/candidate-validation/mocks_runtime_test.go b/dot/parachain/candidate-validation/mocks_runtime_test.go deleted file mode 100644 index 6f29676556..0000000000 --- a/dot/parachain/candidate-validation/mocks_runtime_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ChainSafe/gossamer/dot/parachain/runtime (interfaces: RuntimeInstance,ValidationHost) -// -// Generated by this command: -// -// mockgen -destination=mocks_runtime_test.go -package candidatevalidation github.com/ChainSafe/gossamer/dot/parachain/runtime RuntimeInstance,ValidationHost -// - -// Package candidatevalidation is a generated GoMock package. -package candidatevalidation - -import ( - reflect "reflect" - - parachain "github.com/ChainSafe/gossamer/dot/parachain/runtime" - parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" - gomock "go.uber.org/mock/gomock" -) - -// MockRuntimeInstance is a mock of RuntimeInstance interface. -type MockRuntimeInstance struct { - ctrl *gomock.Controller - recorder *MockRuntimeInstanceMockRecorder -} - -// MockRuntimeInstanceMockRecorder is the mock recorder for MockRuntimeInstance. -type MockRuntimeInstanceMockRecorder struct { - mock *MockRuntimeInstance -} - -// NewMockRuntimeInstance creates a new mock instance. -func NewMockRuntimeInstance(ctrl *gomock.Controller) *MockRuntimeInstance { - mock := &MockRuntimeInstance{ctrl: ctrl} - mock.recorder = &MockRuntimeInstanceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockRuntimeInstance) EXPECT() *MockRuntimeInstanceMockRecorder { - return m.recorder -} - -// ParachainHostCandidateEvents mocks base method. -func (m *MockRuntimeInstance) ParachainHostCandidateEvents() ([]parachaintypes.CandidateEvent, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ParachainHostCandidateEvents") - ret0, _ := ret[0].([]parachaintypes.CandidateEvent) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ParachainHostCandidateEvents indicates an expected call of ParachainHostCandidateEvents. -func (mr *MockRuntimeInstanceMockRecorder) ParachainHostCandidateEvents() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostCandidateEvents", reflect.TypeOf((*MockRuntimeInstance)(nil).ParachainHostCandidateEvents)) -} - -// ParachainHostCheckValidationOutputs mocks base method. -func (m *MockRuntimeInstance) ParachainHostCheckValidationOutputs(arg0 parachaintypes.ParaID, arg1 parachaintypes.CandidateCommitments) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ParachainHostCheckValidationOutputs", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ParachainHostCheckValidationOutputs indicates an expected call of ParachainHostCheckValidationOutputs. -func (mr *MockRuntimeInstanceMockRecorder) ParachainHostCheckValidationOutputs(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostCheckValidationOutputs", reflect.TypeOf((*MockRuntimeInstance)(nil).ParachainHostCheckValidationOutputs), arg0, arg1) -} - -// ParachainHostPersistedValidationData mocks base method. -func (m *MockRuntimeInstance) ParachainHostPersistedValidationData(arg0 uint32, arg1 parachaintypes.OccupiedCoreAssumption) (*parachaintypes.PersistedValidationData, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ParachainHostPersistedValidationData", arg0, arg1) - ret0, _ := ret[0].(*parachaintypes.PersistedValidationData) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ParachainHostPersistedValidationData indicates an expected call of ParachainHostPersistedValidationData. -func (mr *MockRuntimeInstanceMockRecorder) ParachainHostPersistedValidationData(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostPersistedValidationData", reflect.TypeOf((*MockRuntimeInstance)(nil).ParachainHostPersistedValidationData), arg0, arg1) -} - -// ParachainHostValidationCode mocks base method. -func (m *MockRuntimeInstance) ParachainHostValidationCode(arg0 uint32, arg1 parachaintypes.OccupiedCoreAssumption) (*parachaintypes.ValidationCode, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ParachainHostValidationCode", arg0, arg1) - ret0, _ := ret[0].(*parachaintypes.ValidationCode) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ParachainHostValidationCode indicates an expected call of ParachainHostValidationCode. -func (mr *MockRuntimeInstanceMockRecorder) ParachainHostValidationCode(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParachainHostValidationCode", reflect.TypeOf((*MockRuntimeInstance)(nil).ParachainHostValidationCode), arg0, arg1) -} - -// MockValidationHost is a mock of ValidationHost interface. -type MockValidationHost struct { - ctrl *gomock.Controller - recorder *MockValidationHostMockRecorder -} - -// MockValidationHostMockRecorder is the mock recorder for MockValidationHost. -type MockValidationHostMockRecorder struct { - mock *MockValidationHost -} - -// NewMockValidationHost creates a new mock instance. -func NewMockValidationHost(ctrl *gomock.Controller) *MockValidationHost { - mock := &MockValidationHost{ctrl: ctrl} - mock.recorder = &MockValidationHostMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockValidationHost) EXPECT() *MockValidationHostMockRecorder { - return m.recorder -} - -// ValidateBlock mocks base method. -func (m *MockValidationHost) 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 *MockValidationHostMockRecorder) ValidateBlock(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateBlock", reflect.TypeOf((*MockValidationHost)(nil).ValidateBlock), arg0) -} diff --git a/dot/parachain/candidate-validation/worker.go b/dot/parachain/candidate-validation/worker.go new file mode 100644 index 0000000000..6781316594 --- /dev/null +++ b/dot/parachain/candidate-validation/worker.go @@ -0,0 +1,91 @@ +package candidatevalidation + +import ( + parachainruntime "github.com/ChainSafe/gossamer/dot/parachain/runtime" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" +) + +// worker is the thing that can execute a validation request +type worker struct { + workerID parachaintypes.ValidationCodeHash + instance parachainruntime.ValidatorInstance + isProcessed map[parachaintypes.CandidateHash]*ValidationResult +} + +type workerTask struct { + work parachainruntime.ValidationParameters + maxPoVSize uint32 + candidateReceipt *parachaintypes.CandidateReceipt +} + +func newWorker(validationCode parachaintypes.ValidationCode) (*worker, error) { + parachainRuntime, err := parachainruntime.SetupVM(validationCode) + if err != nil { + return nil, err + } + + return &worker{ + workerID: validationCode.Hash(), + instance: parachainRuntime, + isProcessed: make(map[parachaintypes.CandidateHash]*ValidationResult), + }, nil +} + +func (w *worker) executeRequest(task *workerTask) (*ValidationResult, error) { + logger.Debugf("[EXECUTING] worker %x task %v", w.workerID, task.work) + candidateHash, err := parachaintypes.GetCandidateHash(task.candidateReceipt) + if err != nil { + return nil, err + } + + if processed, ok := w.isProcessed[candidateHash]; ok { + 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 + return &ValidationResult{Invalid: &reasonForInvalidity}, nil + } + + headDataHash, err := validationResult.HeadData.Hash() + if err != nil { + logger.Errorf("hashing head data: %w", err) + reasonForInvalidity := ExecutionError + return &ValidationResult{Invalid: &reasonForInvalidity}, nil + } + + if headDataHash != task.candidateReceipt.Descriptor.ParaHead { + reasonForInvalidity := ParaHeadHashMismatch + return &ValidationResult{Invalid: &reasonForInvalidity}, nil + } + candidateCommitments := parachaintypes.CandidateCommitments{ + UpwardMessages: validationResult.UpwardMessages, + HorizontalMessages: validationResult.HorizontalMessages, + NewValidationCode: validationResult.NewValidationCode, + HeadData: validationResult.HeadData, + ProcessedDownwardMessages: validationResult.ProcessedDownwardMessages, + HrmpWatermark: validationResult.HrmpWatermark, + } + + // if validation produced a new set of commitments, we treat the candidate as invalid + if task.candidateReceipt.CommitmentsHash != candidateCommitments.Hash() { + reasonForInvalidity := CommitmentsHashMismatch + return &ValidationResult{Invalid: &reasonForInvalidity}, nil + } + pvd := parachaintypes.PersistedValidationData{ + ParentHead: task.work.ParentHeadData, + RelayParentNumber: task.work.RelayParentNumber, + RelayParentStorageRoot: task.work.RelayParentStorageRoot, + MaxPovSize: task.maxPoVSize, + } + result := &ValidationResult{ + Valid: &Valid{ + CandidateCommitments: candidateCommitments, + PersistedValidationData: pvd, + }, + } + w.isProcessed[candidateHash] = result + return result, nil +} diff --git a/dot/parachain/candidate-validation/worker_pool.go b/dot/parachain/candidate-validation/worker_pool.go new file mode 100644 index 0000000000..828f37afac --- /dev/null +++ b/dot/parachain/candidate-validation/worker_pool.go @@ -0,0 +1,164 @@ +package candidatevalidation + +import ( + "fmt" + + parachainruntime "github.com/ChainSafe/gossamer/dot/parachain/runtime" + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" +) + +type workerPool struct { + workers map[parachaintypes.ValidationCodeHash]*worker +} + +type ValidationTask struct { + PersistedValidationData parachaintypes.PersistedValidationData + WorkerID *parachaintypes.ValidationCodeHash + CandidateReceipt *parachaintypes.CandidateReceipt + PoV parachaintypes.PoV + ExecutorParams parachaintypes.ExecutorParams + PvfExecTimeoutKind parachaintypes.PvfExecTimeoutKind + ValidationCode *parachaintypes.ValidationCode +} + +// ValidationResult represents the result coming from the candidate validation subsystem. +// Validation results can be either valid or invalid. +// +// If the result is invalid, store the reason for invalidity. +// +// If the result is valid, store persisted validation data and candidate commitments. +type ValidationResult struct { + Valid *Valid + Invalid *ReasonForInvalidity +} + +func (vr ValidationResult) IsValid() bool { + return vr.Valid != nil +} + +type Valid struct { + CandidateCommitments parachaintypes.CandidateCommitments + PersistedValidationData parachaintypes.PersistedValidationData +} + +type ReasonForInvalidity byte + +const ( + // ExecutionError Failed to execute `validate_block`. This includes function panicking. + ExecutionError ReasonForInvalidity = iota + // InvalidOutputs Validation outputs check doesn't pass. + InvalidOutputs + // Timeout Execution timeout. + Timeout + // ParamsTooLarge Validation input is over the limit. + ParamsTooLarge + // CodeTooLarge Code size is over the limit. + CodeTooLarge + // PoVDecompressionFailure PoV does not decompress correctly. + PoVDecompressionFailure + // BadReturn Validation function returned invalid data. + BadReturn + // BadParent Invalid relay chain parent. + BadParent + // PoVHashMismatch POV hash does not match. + PoVHashMismatch + // BadSignature Bad collator signature. + BadSignature + // ParaHeadHashMismatch Para head hash does not match. + ParaHeadHashMismatch + // CodeHashMismatch Validation code hash does not match. + CodeHashMismatch + // CommitmentsHashMismatch Validation has generated different candidate commitments. + CommitmentsHashMismatch +) + +func (ci ReasonForInvalidity) Error() string { + switch ci { + case ExecutionError: + return "failed to execute `validate_block`" + case InvalidOutputs: + return "validation outputs check doesn't pass" + case Timeout: + return "execution timeout" + case ParamsTooLarge: + return "validation input is over the limit" + case CodeTooLarge: + return "code size is over the limit" + case PoVDecompressionFailure: + return "PoV does not decompress correctly" + case BadReturn: + return "validation function returned invalid data" + case BadParent: + return "invalid relay chain parent" + case PoVHashMismatch: + return "PoV hash does not match" + case BadSignature: + return "bad collator signature" + case ParaHeadHashMismatch: + return "para head hash does not match" + case CodeHashMismatch: + return "validation code hash does not match" + case CommitmentsHashMismatch: + return "validation has generated different candidate commitments" + default: + return "unknown invalidity reason" + } +} + +func newWorkerPool() *workerPool { + return &workerPool{ + workers: make(map[parachaintypes.ValidationCodeHash]*worker), + } +} + +func (v *workerPool) addNewWorker(validationCode parachaintypes.ValidationCode) error { + workerID := validationCode.Hash() + if !v.containsWorker(workerID) { + worker, err := newWorker(validationCode) + if err != nil { + return fmt.Errorf("failed to create a new worker: %w", err) + } + + v.workers[workerID] = worker + + } + + return nil +} + +// executeRequest given a request, the worker pool will get the worker for a given task and submit the request +// to the worker. The worker will execute the request and return the result. If the worker does not exist, a new worker +// will be created and the request will be submitted to the worker. +func (v *workerPool) executeRequest(msg *ValidationTask) (*ValidationResult, error) { + validationCodeHash := msg.ValidationCode.Hash() + + // create worker if not in pool + if !v.containsWorker(validationCodeHash) { + err := v.addNewWorker(*msg.ValidationCode) + if err != nil { + return nil, err + } + } + worker := v.workers[validationCodeHash] + + logger.Debugf("sending request", validationCodeHash) + + validationParams := parachainruntime.ValidationParameters{ + ParentHeadData: msg.PersistedValidationData.ParentHead, + BlockData: msg.PoV.BlockData, + RelayParentNumber: msg.PersistedValidationData.RelayParentNumber, + RelayParentStorageRoot: msg.PersistedValidationData.RelayParentStorageRoot, + } + workTask := &workerTask{ + work: validationParams, + maxPoVSize: msg.PersistedValidationData.MaxPovSize, + candidateReceipt: msg.CandidateReceipt, + } + return worker.executeRequest(workTask) + +} + +func (v *workerPool) containsWorker(workerID parachaintypes.ValidationCodeHash) bool { + _, inMap := v.workers[workerID] + return inMap +} diff --git a/dot/parachain/candidate-validation/worker_pool_test.go b/dot/parachain/candidate-validation/worker_pool_test.go new file mode 100644 index 0000000000..edf30412c3 --- /dev/null +++ b/dot/parachain/candidate-validation/worker_pool_test.go @@ -0,0 +1,64 @@ +package candidatevalidation + +import ( + "os" + "testing" + + parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" +) + +func createTestValidationCode(t *testing.T) parachaintypes.ValidationCode { + // this wasm was achieved by building polkadot's adder test parachain + runtimeFilePath := "./testdata/test_parachain_adder.wasm" + validationCodeBytes, err := os.ReadFile(runtimeFilePath) + require.NoError(t, err) + + return parachaintypes.ValidationCode(validationCodeBytes) + +} + +func TestValidationWorkerPool_newValidationWorker(t *testing.T) { + t.Parallel() + testValidationCode := createTestValidationCode(t) + + cases := map[string]struct { + setupWorkerPool func(t *testing.T) *workerPool + expectedWorkers []parachaintypes.ValidationCodeHash + }{ + "add_one_invalid_worker": { + setupWorkerPool: func(t *testing.T) *workerPool { + pool := newWorkerPool() + err := pool.addNewWorker(parachaintypes.ValidationCode{1, 2, 3, 4}) + require.Error(t, err) + return pool + }, + expectedWorkers: []parachaintypes.ValidationCodeHash{}, + }, + "add_one_valid_worker": { + setupWorkerPool: func(t *testing.T) *workerPool { + pool := newWorkerPool() + err := pool.addNewWorker(testValidationCode) + require.NoError(t, err) + return pool + }, + expectedWorkers: []parachaintypes.ValidationCodeHash{ + testValidationCode.Hash(), + }, + }, + } + + for tname, tt := range cases { + tt := tt + t.Run(tname, func(t *testing.T) { + t.Parallel() + + workerPool := tt.setupWorkerPool(t) + + require.ElementsMatch(t, + maps.Keys(workerPool.workers), + tt.expectedWorkers) + }) + } +} diff --git a/dot/parachain/overseer/mock_runtime_instance_test.go b/dot/parachain/overseer/mock_runtime_instance_test.go index 7b892fabcf..ea8aa49824 100644 --- a/dot/parachain/overseer/mock_runtime_instance_test.go +++ b/dot/parachain/overseer/mock_runtime_instance_test.go @@ -55,7 +55,7 @@ func (mr *MockRuntimeInstanceMockRecorder) ParachainHostCandidateEvents() *gomoc } // ParachainHostCheckValidationOutputs mocks base method. -func (m *MockRuntimeInstance) ParachainHostCheckValidationOutputs(arg0 uint32, arg1 parachaintypes.CandidateCommitments) (bool, error) { +func (m *MockRuntimeInstance) ParachainHostCheckValidationOutputs(arg0 parachaintypes.ParaID, arg1 parachaintypes.CandidateCommitments) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ParachainHostCheckValidationOutputs", arg0, arg1) ret0, _ := ret[0].(bool) diff --git a/dot/parachain/runtime/instance.go b/dot/parachain/runtime/instance.go index f67bb3adae..35bedf5b62 100644 --- a/dot/parachain/runtime/instance.go +++ b/dot/parachain/runtime/instance.go @@ -89,7 +89,8 @@ func (in *Instance) ValidateBlock(params ValidationParameters) ( return &validationResult, nil } -type ValidationHost interface { +// ValidatorInstance for candidate validation methods +type ValidatorInstance interface { // ValidateBlock validates a block by calling parachain runtime's validate_block call and returns the result. ValidateBlock(params ValidationParameters) (*ValidationResult, error) } @@ -100,8 +101,8 @@ type RuntimeInstance interface { ) (*parachaintypes.PersistedValidationData, error) ParachainHostValidationCode(parachaidID uint32, assumption parachaintypes.OccupiedCoreAssumption, ) (*parachaintypes.ValidationCode, error) - ParachainHostCheckValidationOutputs(parachainID parachaintypes.ParaID, - outputs parachaintypes.CandidateCommitments) (bool, error) + ParachainHostCheckValidationOutputs(parachainID parachaintypes.ParaID, outputs parachaintypes.CandidateCommitments) ( + bool, error) ParachainHostCandidateEvents() ([]parachaintypes.CandidateEvent, error) } diff --git a/go.mod b/go.mod index d9d9ebbf39..f585b2c749 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/fatih/color v1.17.0 github.com/gammazero/deque v0.2.1 github.com/go-playground/validator/v10 v10.21.0 + github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 diff --git a/go.sum b/go.sum index 03ec8f0ac3..990d74d772 100644 --- a/go.sum +++ b/go.sum @@ -206,6 +206,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -797,6 +799,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=