From 31b83f166ec2e6dafda1db53a42d55535a275983 Mon Sep 17 00:00:00 2001 From: edwardmack Date: Tue, 10 Sep 2024 19:05:46 -0400 Subject: [PATCH] handle validation errors InvalidOutputs and BadParent --- .../candidate_validation.go | 37 +++++++++- .../candidate_validation_test.go | 73 +++++++++++++++---- .../candidate-validation/host_test.go | 2 +- .../candidate-validation/worker_test.go | 2 +- 4 files changed, 98 insertions(+), 16 deletions(-) diff --git a/dot/parachain/candidate-validation/candidate_validation.go b/dot/parachain/candidate-validation/candidate_validation.go index ff53d3be02..945b31d51f 100644 --- a/dot/parachain/candidate-validation/candidate_validation.go +++ b/dot/parachain/candidate-validation/candidate_validation.go @@ -165,6 +165,7 @@ func (cv *CandidateValidation) validateFromChainState(msg ValidateFromChainState persistedValidationData, validationCode, err := getValidationData(runtimeInstance, msg.CandidateReceipt.Descriptor.ParaID) + if err != nil { logger.Errorf("getting validation data: %w", err) msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ @@ -173,6 +174,16 @@ func (cv *CandidateValidation) validateFromChainState(msg ValidateFromChainState return } + if persistedValidationData == nil { + badParent := BadParent + reason := ValidationResult{ + InvalidResult: &badParent, + } + msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ + Data: reason, + } + return + } validationTask := &ValidationTask{ PersistedValidationData: *persistedValidationData, ValidationCode: validationCode, @@ -188,9 +199,33 @@ func (cv *CandidateValidation) validateFromChainState(msg ValidateFromChainState msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ Err: err, } - } else { + return + } + if !result.IsValid() { msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ Data: *result, } + return + } + valid, err := runtimeInstance.ParachainHostCheckValidationOutputs(parachaintypes.ParaID(msg.CandidateReceipt. + Descriptor.ParaID), result.ValidResult.CandidateCommitments) + if err != nil { + msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ + Err: fmt.Errorf("check validation outputs: Bad request: %w", err), + } + return + } + if !valid { + invalidOutput := InvalidOutputs + reason := &ValidationResult{ + InvalidResult: &invalidOutput, + } + msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ + Data: *reason, + } + return + } + msg.Ch <- parachaintypes.OverseerFuncRes[ValidationResult]{ + Data: *result, } } diff --git a/dot/parachain/candidate-validation/candidate_validation_test.go b/dot/parachain/candidate-validation/candidate_validation_test.go index 8ddd514a8d..e94b322dec 100644 --- a/dot/parachain/candidate-validation/candidate_validation_test.go +++ b/dot/parachain/candidate-validation/candidate_validation_test.go @@ -22,9 +22,11 @@ var ( paramsTooLarge = ParamsTooLarge codeHashMismatch = CodeHashMismatch badSignature = BadSignature + invalidOutputs = InvalidOutputs + badParent = BadParent ) -func createTestCandidateReceiptAndValidationCode(t *testing.T) ( +func createTestCandidateReceiptAndValidationCodeWParaId(t *testing.T, id uint32) ( parachaintypes.CandidateReceipt, parachaintypes.ValidationCode) { // this wasm was achieved by building polkadot's adder test parachain runtimeFilePath := "./testdata/test_parachain_adder.wasm" @@ -38,7 +40,7 @@ func createTestCandidateReceiptAndValidationCode(t *testing.T) ( collatorKeypair, err := sr25519.GenerateKeypair() require.NoError(t, err) - descriptor := makeValidCandidateDescriptor(t, 1000, + descriptor := makeValidCandidateDescriptor(t, id, common.MustHexToHash("0xded542bacb3ca6c033a57676f94ae7c8f36834511deb44e3164256fd3b1c0de0"), common.MustHexToHash("0x690d8f252ef66ab0f969c3f518f90012b849aa5ac94e1752c5e5ae5a8996de37"), common.MustHexToHash("0xb608991ffc48dd405fd4b10e92eaebe2b5a2eedf44d0c3efb8997fdee8bebed9"), @@ -107,7 +109,7 @@ func TestCandidateValidation_wasm_invalid_magic_number(t *testing.T) { func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) { t.Parallel() - candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) + candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCodeWParaId(t, 1000) candidateReceipt2 := candidateReceipt candidateReceipt2.Descriptor.PovHash = common.MustHexToHash( "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") @@ -261,7 +263,7 @@ func TestCandidateValidation_processMessageValidateFromExhaustive(t *testing.T) func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) { t.Parallel() - candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) + candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCodeWParaId(t, 1000) candidateReceipt2 := candidateReceipt candidateReceipt2.Descriptor.PovHash = common.MustHexToHash( "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") @@ -278,6 +280,11 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) candidateReceipt5 := candidateReceipt candidateReceipt5.Descriptor.ParaID = 5 + candidateReceipt6, _ := createTestCandidateReceiptAndValidationCodeWParaId(t, 6) + + candidateReceipt7 := candidateReceipt + candidateReceipt7.Descriptor.ParaID = 7 + ctrl := gomock.NewController(t) t.Cleanup(ctrl.Finish) @@ -318,6 +325,16 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) mockInstance.EXPECT(). ParachainHostValidationCode(uint32(1000), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})). Return(&validationCode, nil) + validCandidateCommitments := parachaintypes.CandidateCommitments{ + HeadData: parachaintypes.HeadData{Data: []byte{2, 0, 0, 0, 0, 0, 0, 0, 123, 207, 206, 8, 219, 227, + 136, 82, 236, 169, 14, 100, 45, 100, 31, 177, 154, 160, 220, 245, 59, 106, 76, 168, 122, 109, + 164, 169, 22, 46, 144, 39, 103, 92, 31, 78, 66, 72, 252, 64, 24, 194, 129, 162, 128, 1, 77, 147, + 200, 229, 189, 242, 111, 198, 236, 139, 16, 143, 19, 245, 113, 233, 138, 210}}, + ProcessedDownwardMessages: 0, + HrmpWatermark: 1, + } + mockInstance.EXPECT().ParachainHostCheckValidationOutputs(parachaintypes.ParaID(1000), + validCandidateCommitments).Return(true, nil) mockInstance.EXPECT(). ParachainHostPersistedValidationData( @@ -355,9 +372,26 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) ParachainHostValidationCode(uint32(5), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})). Return(&validationCode, nil) + mockInstance.EXPECT(). + ParachainHostPersistedValidationData(uint32(6), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})). + Return(&expectedPersistedValidationData, nil) + mockInstance.EXPECT(). + ParachainHostValidationCode(uint32(6), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})). + Return(&validationCode, nil) + mockInstance.EXPECT().ParachainHostCheckValidationOutputs(parachaintypes.ParaID(6), + validCandidateCommitments).Return(false, nil) + + mockInstance.EXPECT(). + ParachainHostPersistedValidationData(uint32(7), gomock.AssignableToTypeOf(parachaintypes. + OccupiedCoreAssumption{})). + Return(nil, nil) + mockInstance.EXPECT(). + ParachainHostValidationCode(uint32(7), gomock.AssignableToTypeOf(parachaintypes.OccupiedCoreAssumption{})). + Return(&validationCode, nil) + mockBlockState := NewMockBlockState(ctrl) mockBlockState.EXPECT().GetRuntime(common.MustHexToHash( - "0xded542bacb3ca6c033a57676f94ae7c8f36834511deb44e3164256fd3b1c0de0")).Return(mockInstance, nil).Times(5) + "0xded542bacb3ca6c033a57676f94ae7c8f36834511deb44e3164256fd3b1c0de0")).Return(mockInstance, nil).Times(7) bd, err := scale.Marshal(BlockDataInAdderParachain{ State: uint64(1), @@ -423,6 +457,26 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) InvalidResult: &badSignature, }, }, + "invalid_outputs": { + msg: ValidateFromChainState{ + CandidateReceipt: candidateReceipt6, + Pov: pov, + Ch: sender, + }, + want: &ValidationResult{ + InvalidResult: &invalidOutputs, + }, + }, + "bad_parent": { + msg: ValidateFromChainState{ + CandidateReceipt: candidateReceipt7, + Pov: pov, + Ch: sender, + }, + want: &ValidationResult{ + InvalidResult: &badParent, + }, + }, "happy_path": { msg: ValidateFromChainState{ CandidateReceipt: candidateReceipt, @@ -431,14 +485,7 @@ func TestCandidateValidation_processMessageValidateFromChainState(t *testing.T) }, want: &ValidationResult{ ValidResult: &Valid{ - CandidateCommitments: parachaintypes.CandidateCommitments{ - HeadData: parachaintypes.HeadData{Data: []byte{2, 0, 0, 0, 0, 0, 0, 0, 123, 207, 206, 8, 219, 227, - 136, 82, 236, 169, 14, 100, 45, 100, 31, 177, 154, 160, 220, 245, 59, 106, 76, 168, 122, 109, - 164, 169, 22, 46, 144, 39, 103, 92, 31, 78, 66, 72, 252, 64, 24, 194, 129, 162, 128, 1, 77, 147, - 200, 229, 189, 242, 111, 198, 236, 139, 16, 143, 19, 245, 113, 233, 138, 210}}, - ProcessedDownwardMessages: 0, - HrmpWatermark: 1, - }, + CandidateCommitments: validCandidateCommitments, PersistedValidationData: parachaintypes.PersistedValidationData{ ParentHead: parachaintypes.HeadData{Data: []byte{1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, diff --git a/dot/parachain/candidate-validation/host_test.go b/dot/parachain/candidate-validation/host_test.go index 35a00971c1..ac30870c8e 100644 --- a/dot/parachain/candidate-validation/host_test.go +++ b/dot/parachain/candidate-validation/host_test.go @@ -12,7 +12,7 @@ import ( func TestHost_validate(t *testing.T) { t.Parallel() - candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) + candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCodeWParaId(t, 1000) candidateReceipt2 := candidateReceipt candidateReceipt2.Descriptor.PovHash = common.MustHexToHash( "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") diff --git a/dot/parachain/candidate-validation/worker_test.go b/dot/parachain/candidate-validation/worker_test.go index 636cea0bf7..ad1c8e6ddc 100644 --- a/dot/parachain/candidate-validation/worker_test.go +++ b/dot/parachain/candidate-validation/worker_test.go @@ -13,7 +13,7 @@ import ( ) func Test_worker_executeRequest(t *testing.T) { - candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCode(t) + candidateReceipt, validationCode := createTestCandidateReceiptAndValidationCodeWParaId(t, 1000) validationRuntime, err := parachain.SetupVM(validationCode) require.NoError(t, err)