diff --git a/modules/light-clients/06-solomachine/types/update.go b/modules/light-clients/06-solomachine/types/update.go index eb3941b89d2..d04d93c36af 100644 --- a/modules/light-clients/06-solomachine/types/update.go +++ b/modules/light-clients/06-solomachine/types/update.go @@ -23,6 +23,11 @@ func (cs ClientState) CheckHeaderAndUpdateState( return nil, nil, err } + foundMisbehaviour := cs.CheckForMisbehaviour(ctx, cdc, clientStore, msg) + if foundMisbehaviour { + return cs.UpdateStateOnMisbehaviour(ctx, cdc, clientStore) + } + return cs.UpdateState(ctx, cdc, clientStore, msg) } @@ -109,3 +114,21 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client cs.ConsensusState = consensusState return &cs, consensusState, nil } + +// CheckForMisbehaviour returns true for type Misbehaviour (passed VerifyClientMessage check), otherwise returns false +func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, clientMsg exported.ClientMessage) bool { + if _, ok := clientMsg.(*Misbehaviour); ok { + return true + } + + return false +} + +// UpdateStateOnMisbehaviour updates state upon misbehaviour. This method should only be called on misbehaviour +// as it does not perform any misbehaviour checks. +func (cs ClientState) UpdateStateOnMisbehaviour( + _ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, // prematurely include args for self storage of consensus state +) (*ClientState, exported.ConsensusState, error) { + cs.IsFrozen = true + return &cs, cs.ConsensusState, nil +} diff --git a/modules/light-clients/06-solomachine/types/update_test.go b/modules/light-clients/06-solomachine/types/update_test.go index ddd239ab9a4..56f620b15aa 100644 --- a/modules/light-clients/06-solomachine/types/update_test.go +++ b/modules/light-clients/06-solomachine/types/update_test.go @@ -624,3 +624,85 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { } } } + +func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { + var ( + clientMsg exported.ClientMessage + ) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() { + clientMsg = solomachine.CreateMisbehaviour() + }, + true, + }, + { + "normal header returns false", + func() { + clientMsg = solomachine.CreateHeader() + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState := solomachine.ClientState() + + tc.malleate() + + foundMisbehaviour := clientState.CheckForMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) + + if tc.expPass { + suite.Require().True(foundMisbehaviour) + } else { + suite.Require().False(foundMisbehaviour) + } + + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", + func() {}, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState := solomachine.ClientState() + + tc.malleate() + + cs, _, _ := clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store) + + if tc.expPass { + suite.Require().True(cs.IsFrozen) + } + }) + } + } +}