From 5a9a52fe71d352cbad4c3377da7e078244aa7b0e Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Thu, 21 Mar 2024 10:54:48 +0200 Subject: [PATCH 01/10] tests: refactor tests to use light client module as entrypoint. --- .../06-solomachine/client_state_test.go | 718 ---------------- .../light_client_module_test.go | 779 ++++++++++++++++++ modules/light-clients/06-solomachine/proof.go | 2 +- 3 files changed, 780 insertions(+), 719 deletions(-) diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go index 5b982aaa407..85cda2127d0 100644 --- a/modules/light-clients/06-solomachine/client_state_test.go +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -3,17 +3,11 @@ package solomachine_test import ( "bytes" - sdk "github.com/cosmos/cosmos-sdk/types" - - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" - commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" "github.com/cosmos/ibc-go/v8/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v8/testing" - ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" ) const ( @@ -23,18 +17,6 @@ const ( testPortID = "testportid" ) -func (suite *SoloMachineTestSuite) TestStatus() { - clientState := suite.solomachine.ClientState() - // solo machine discards arguments - status := clientState.Status(suite.chainA.GetContext(), nil, nil) - suite.Require().Equal(exported.Active, status) - - // freeze solo machine - clientState.IsFrozen = true - status = clientState.Status(suite.chainA.GetContext(), nil, nil) - suite.Require().Equal(exported.Frozen, status) -} - func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { @@ -149,464 +131,6 @@ func (suite *SoloMachineTestSuite) TestInitialize() { } } -func (suite *SoloMachineTestSuite) TestVerifyMembership() { - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - var ( - clientState *solomachine.ClientState - path exported.Path - proof []byte - testingPath *ibctesting.Path - signBytes solomachine.SignBytes - err error - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success", - func() {}, - true, - }, - { - "success: client state verification", - func() { - clientState = sm.ClientState() - clientStateBz, err := suite.chainA.Codec.Marshal(clientState) - suite.Require().NoError(err) - - path = sm.GetClientStatePath(counterpartyClientIdentifier) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.GetHeight().GetRevisionHeight(), - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: clientStateBz, - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "success: consensus state verification", - func() { - clientState = sm.ClientState() - consensusState := clientState.ConsensusState - consensusStateBz, err := suite.chainA.Codec.Marshal(consensusState) - suite.Require().NoError(err) - - path = sm.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 1)) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.Sequence, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: consensusStateBz, - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "success: connection state verification", - func() { - testingPath.SetupConnections() - - connectionEnd, found := suite.chainA.GetSimApp().IBCKeeper.ConnectionKeeper.GetConnection(suite.chainA.GetContext(), ibctesting.FirstConnectionID) - suite.Require().True(found) - - connectionEndBz, err := suite.chainA.Codec.Marshal(&connectionEnd) - suite.Require().NoError(err) - - path = sm.GetConnectionStatePath(ibctesting.FirstConnectionID) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.Sequence, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: connectionEndBz, - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "success: channel state verification", - func() { - testingPath.SetupConnections() - suite.coordinator.CreateMockChannels(testingPath) - - channelEnd, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) - suite.Require().True(found) - - channelEndBz, err := suite.chainA.Codec.Marshal(&channelEnd) - suite.Require().NoError(err) - - path = sm.GetChannelStatePath(ibctesting.MockPort, ibctesting.FirstChannelID) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.Sequence, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: channelEndBz, - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "success: next sequence recv verification", - func() { - testingPath.SetupConnections() - suite.coordinator.CreateMockChannels(testingPath) - - nextSeqRecv, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetNextSequenceRecv(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) - suite.Require().True(found) - - path = sm.GetNextSequenceRecvPath(ibctesting.MockPort, ibctesting.FirstChannelID) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.Sequence, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: sdk.Uint64ToBigEndian(nextSeqRecv), - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "success: packet commitment verification", - func() { - packet := channeltypes.NewPacket( - ibctesting.MockPacketData, - 1, - ibctesting.MockPort, - ibctesting.FirstChannelID, - ibctesting.MockPort, - ibctesting.FirstChannelID, - clienttypes.NewHeight(0, 10), - 0, - ) - - commitmentBz := channeltypes.CommitPacket(suite.chainA.Codec, packet) - path = sm.GetPacketCommitmentPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.Sequence, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: commitmentBz, - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "success: packet acknowledgement verification", - func() { - path = sm.GetPacketAcknowledgementPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.Sequence, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: ibctesting.MockAcknowledgement, - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "success: packet receipt verification", - func() { - path = sm.GetPacketReceiptPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.Sequence, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: []byte{byte(1)}, // packet receipt is stored as a single byte - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "invalid path type - empty", - func() { - path = ibcmock.KeyPath{} - }, - false, - }, - { - "malformed proof fails to unmarshal", - func() { - path = sm.GetClientStatePath(counterpartyClientIdentifier) - proof = []byte("invalid proof") - }, - false, - }, - { - "consensus state timestamp is greater than signature", - func() { - consensusState := &solomachine.ConsensusState{ - Timestamp: sm.Time + 1, - PublicKey: sm.ConsensusState().PublicKey, - } - - clientState = solomachine.NewClientState(sm.Sequence, consensusState) - }, - false, - }, - { - "signature data is nil", - func() { - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: nil, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - false, - }, - { - "consensus state public key is nil", - func() { - clientState.ConsensusState.PublicKey = nil - }, - false, - }, - { - "malformed signature data fails to unmarshal", - func() { - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: []byte("invalid signature data"), - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - false, - }, - { - "proof is nil", - func() { - proof = nil - }, - false, - }, - { - "proof verification failed", - func() { - signBytes.Data = []byte("invalid membership data value") - }, - false, - }, - { - "empty path", - func() { - path = commitmenttypes.MerklePath{} - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - testingPath = ibctesting.NewPath(suite.chainA, suite.chainB) - - clientState = sm.ClientState() - - path = commitmenttypes.NewMerklePath("ibc", "solomachine") - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.GetHeight().GetRevisionHeight(), - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: []byte("solomachine"), - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - tc.malleate() - - var expSeq uint64 - if clientState.ConsensusState != nil { - expSeq = clientState.Sequence + 1 - } - - err = clientState.VerifyMembership( - suite.chainA.GetContext(), suite.store, suite.chainA.Codec, - clienttypes.ZeroHeight(), 0, 0, // solomachine does not check delay periods - proof, path, signBytes.Data, - ) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().Equal(expSeq, clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) - } else { - suite.Require().Error(err) - } - }) - } - } -} - func (suite *SoloMachineTestSuite) TestSignBytesMarshalling() { sm := suite.solomachine path := []byte("solomachine") @@ -634,245 +158,3 @@ func (suite *SoloMachineTestSuite) TestSignBytesMarshalling() { suite.Require().True(bytes.Equal(signBzNil, signBzEmptyArray)) } - -func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - var ( - clientState *solomachine.ClientState - path exported.Path - proof []byte - signBytes solomachine.SignBytes - err error - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success", - func() {}, - true, - }, - { - "success: packet receipt absence verification", - func() { - path = sm.GetPacketReceiptPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.GetHeight().GetRevisionHeight(), - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: nil, - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - true, - }, - { - "invalid path type", - func() { - path = ibcmock.KeyPath{} - }, - false, - }, - { - "malformed proof fails to unmarshal", - func() { - path = sm.GetClientStatePath(counterpartyClientIdentifier) - proof = []byte("invalid proof") - }, - false, - }, - { - "consensus state timestamp is greater than signature", - func() { - consensusState := &solomachine.ConsensusState{ - Timestamp: sm.Time + 1, - PublicKey: sm.ConsensusState().PublicKey, - } - - clientState = solomachine.NewClientState(sm.Sequence, consensusState) - }, - false, - }, - { - "signature data is nil", - func() { - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: nil, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - false, - }, - { - "consensus state public key is nil", - func() { - clientState.ConsensusState.PublicKey = nil - }, - false, - }, - { - "malformed signature data fails to unmarshal", - func() { - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: []byte("invalid signature data"), - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - false, - }, - { - "proof is nil", - func() { - proof = nil - }, - false, - }, - { - "proof verification failed", - func() { - signBytes.Data = []byte("invalid non-membership data value") - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - clientState = sm.ClientState() - - path = commitmenttypes.NewMerklePath("ibc", "solomachine") - merklePath, ok := path.(commitmenttypes.MerklePath) - suite.Require().True(ok) - key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store - suite.Require().NoError(err) - signBytes = solomachine.SignBytes{ - Sequence: sm.GetHeight().GetRevisionHeight(), - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: key, - Data: nil, - } - - signBz, err := suite.chainA.Codec.Marshal(&signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - - signatureDoc := &solomachine.TimestampedSignatureData{ - SignatureData: sig, - Timestamp: sm.Time, - } - - proof, err = suite.chainA.Codec.Marshal(signatureDoc) - suite.Require().NoError(err) - - tc.malleate() - - var expSeq uint64 - if clientState.ConsensusState != nil { - expSeq = clientState.Sequence + 1 - } - - err = clientState.VerifyNonMembership( - suite.chainA.GetContext(), suite.store, suite.chainA.Codec, - clienttypes.ZeroHeight(), 0, 0, // solomachine does not check delay periods - proof, path, - ) - - if tc.expPass { - suite.Require().NoError(err) - suite.Require().Equal(expSeq, clientState.Sequence) - suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) - } else { - suite.Require().Error(err) - } - }) - } - } -} - -func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { - tmPath := ibctesting.NewPath(suite.chainA, suite.chainB) - tmPath.SetupClients() - // Single setup for all test cases. - suite.SetupTest() - - testCases := []struct { - name string - clientState *solomachine.ClientState - height exported.Height - expValue uint64 - expPass bool - }{ - { - name: "get timestamp at height exists", - clientState: suite.solomachine.ClientState(), - height: clienttypes.NewHeight(0, suite.solomachine.ClientState().Sequence), - expValue: suite.solomachine.ClientState().ConsensusState.Timestamp, - expPass: true, - }, - } - - for i, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - ctx := suite.chainA.GetContext() - - ts, err := tc.clientState.GetTimestampAtHeight( - ctx, suite.store, suite.chainA.Codec, tc.height, - ) - - suite.Require().Equal(tc.expValue, ts) - - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } - }) - } -} diff --git a/modules/light-clients/06-solomachine/light_client_module_test.go b/modules/light-clients/06-solomachine/light_client_module_test.go index 13331000768..0e8001e2d36 100644 --- a/modules/light-clients/06-solomachine/light_client_module_test.go +++ b/modules/light-clients/06-solomachine/light_client_module_test.go @@ -1,11 +1,19 @@ package solomachine_test import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" "github.com/cosmos/ibc-go/v8/modules/core/exported" solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" ibctesting "github.com/cosmos/ibc-go/v8/testing" + ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" ) const ( @@ -13,6 +21,777 @@ const ( wasmClientID = "08-wasm-0" ) +func (suite *SoloMachineTestSuite) TestStatus() { + clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + clientState := suite.solomachine.ClientState() + + // Set a client state in store. + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + status := lightClientModule.Status(suite.chainA.GetContext(), clientID) + + // solo machine discards arguments + suite.Require().Equal(exported.Active, status) + + // freeze solo machine and update it in store. + clientState.IsFrozen = true + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + + status = clientState.Status(suite.chainA.GetContext(), nil, nil) + suite.Require().Equal(exported.Frozen, status) +} + +func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { + clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + height := clienttypes.NewHeight(0, suite.solomachine.ClientState().Sequence) + // Single setup for all test cases. + suite.SetupTest() + + testCases := []struct { + name string + clientID string + expValue uint64 + expErr error + }{ + { + "get timestamp at height exists", + clientID, + suite.solomachine.ClientState().ConsensusState.Timestamp, + nil, + }, + { + "client not found", + "non existent client", + 0, + clienttypes.ErrClientNotFound, + }, + } + + for i, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + ctx := suite.chainA.GetContext() + + // Set a client state in store and grab light client module for _clientID_, the lookup for the timestamp + // is performed on the _tc.clientID_. + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(ctx, clientID, suite.solomachine.ClientState()) + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + ts, err := lightClientModule.TimestampAtHeight(ctx, tc.clientID, height) + + suite.Require().Equal(tc.expValue, ts) + suite.Require().ErrorIs(err, tc.expErr, "valid test case %d failed: %s", i, tc.name) + }) + } +} + +func (suite *SoloMachineTestSuite) TestVerifyMembership() { + var clientID string + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + var ( + clientState *solomachine.ClientState + path exported.Path + proof []byte + testingPath *ibctesting.Path + signBytes solomachine.SignBytes + err error + ) + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "success", + func() {}, + nil, + }, + { + "success: client state verification", + func() { + clientState = sm.ClientState() + clientStateBz, err := suite.chainA.Codec.Marshal(clientState) + suite.Require().NoError(err) + + path = sm.GetClientStatePath(counterpartyClientIdentifier) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: clientStateBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + { + "success: consensus state verification", + func() { + clientState = sm.ClientState() + consensusState := clientState.ConsensusState + consensusStateBz, err := suite.chainA.Codec.Marshal(consensusState) + suite.Require().NoError(err) + + path = sm.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 1)) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: consensusStateBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + { + "success: connection state verification", + func() { + testingPath.SetupConnections() + + connectionEnd, found := suite.chainA.GetSimApp().IBCKeeper.ConnectionKeeper.GetConnection(suite.chainA.GetContext(), ibctesting.FirstConnectionID) + suite.Require().True(found) + + connectionEndBz, err := suite.chainA.Codec.Marshal(&connectionEnd) + suite.Require().NoError(err) + + path = sm.GetConnectionStatePath(ibctesting.FirstConnectionID) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: connectionEndBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + { + "success: channel state verification", + func() { + testingPath.SetupConnections() + suite.coordinator.CreateMockChannels(testingPath) + + channelEnd, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) + suite.Require().True(found) + + channelEndBz, err := suite.chainA.Codec.Marshal(&channelEnd) + suite.Require().NoError(err) + + path = sm.GetChannelStatePath(ibctesting.MockPort, ibctesting.FirstChannelID) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: channelEndBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + { + "success: next sequence recv verification", + func() { + testingPath.SetupConnections() + suite.coordinator.CreateMockChannels(testingPath) + + nextSeqRecv, found := suite.chainA.GetSimApp().IBCKeeper.ChannelKeeper.GetNextSequenceRecv(suite.chainA.GetContext(), ibctesting.MockPort, ibctesting.FirstChannelID) + suite.Require().True(found) + + path = sm.GetNextSequenceRecvPath(ibctesting.MockPort, ibctesting.FirstChannelID) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: sdk.Uint64ToBigEndian(nextSeqRecv), + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + { + "success: packet commitment verification", + func() { + packet := channeltypes.NewPacket( + ibctesting.MockPacketData, + 1, + ibctesting.MockPort, + ibctesting.FirstChannelID, + ibctesting.MockPort, + ibctesting.FirstChannelID, + clienttypes.NewHeight(0, 10), + 0, + ) + + commitmentBz := channeltypes.CommitPacket(suite.chainA.Codec, packet) + path = sm.GetPacketCommitmentPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: commitmentBz, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + { + "success: packet acknowledgement verification", + func() { + path = sm.GetPacketAcknowledgementPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: ibctesting.MockAcknowledgement, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + { + "success: packet receipt verification", + func() { + path = sm.GetPacketReceiptPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: []byte{byte(1)}, // packet receipt is stored as a single byte + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + // TODO: Cov missing, need a more extensive refactor of tests in order to cov it. + // { + // "client not found", + // func() {}, + // clienttypes.ErrClientNotFound, + // }, + { + "invalid path type - empty", + func() { + path = ibcmock.KeyPath{} + }, + ibcerrors.ErrInvalidType, + }, + { + "malformed proof fails to unmarshal", + func() { + path = sm.GetClientStatePath(counterpartyClientIdentifier) + proof = []byte("invalid proof") + }, + fmt.Errorf("failed to unmarshal proof into type"), + }, + { + "consensus state timestamp is greater than signature", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, + } + + clientState = solomachine.NewClientState(sm.Sequence, consensusState) + }, + fmt.Errorf("the consensus state timestamp is greater than the signature timestamp (11 >= 10): %s", solomachine.ErrInvalidProof), + }, + { + "signature data is nil", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: nil, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + fmt.Errorf("signature data cannot be empty: %s", solomachine.ErrInvalidProof), + }, + { + "consensus state public key is nil", + func() { + clientState.ConsensusState.PublicKey = nil + }, + fmt.Errorf("consensus state PublicKey cannot be nil: %s", clienttypes.ErrInvalidConsensus), + }, + { + "malformed signature data fails to unmarshal", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: []byte("invalid signature data"), + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + fmt.Errorf("failed to unmarshal proof into type"), + }, + { + "proof is nil", + func() { + proof = nil + }, + fmt.Errorf("proof cannot be empty: %s", solomachine.ErrInvalidProof), + }, + { + "proof verification failed", + func() { + signBytes.Data = []byte("invalid membership data value") + }, + solomachine.ErrSignatureVerificationFailed, + }, + { + "empty path", + func() { + path = commitmenttypes.MerklePath{} + }, + fmt.Errorf("path must be of length 2: []: %s", host.ErrInvalidPath), + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + testingPath = ibctesting.NewPath(suite.chainA, suite.chainB) + + clientState = sm.ClientState() + + path = commitmenttypes.NewMerklePath("ibc", "solomachine") + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: []byte("solomachine"), + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + + tc.malleate() + + // Generate clientID + clientID = suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + + // Set the client state in the store for light client call to find. + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + + var expSeq uint64 + if clientState.ConsensusState != nil { + expSeq = clientState.Sequence + 1 + } + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(smClientID) + suite.Require().True(found) + + // Verify the membership proof + err = lightClientModule.VerifyMembership( + suite.chainA.GetContext(), clientID, clienttypes.ZeroHeight(), + 0, 0, proof, path, signBytes.Data, + ) + + // Grab fresh client state after updates. + cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), clientID) + suite.Require().True(found) + clientState = cs.(*solomachine.ClientState) + + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + // clientState.Sequence is the most recent view of state. + suite.Require().Equal(expSeq, clientState.Sequence) + } else { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expErr.Error()) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + var ( + clientState *solomachine.ClientState + path exported.Path + proof []byte + signBytes solomachine.SignBytes + err error + ) + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "success", + func() {}, + nil, + }, + { + "success: packet receipt absence verification", + func() { + path = sm.GetPacketReceiptPath(ibctesting.MockPort, ibctesting.FirstChannelID, 1) + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: nil, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + nil, + }, + // TODO: Cov missing, need a more extensive refactor of tests in order to cov it. + // { + // "client not found", + // func() {}, + // clienttypes.ErrClientNotFound, + // }, + { + "invalid path type", + func() { + path = ibcmock.KeyPath{} + }, + ibcerrors.ErrInvalidType, + }, + { + "malformed proof fails to unmarshal", + func() { + path = sm.GetClientStatePath(counterpartyClientIdentifier) + proof = []byte("invalid proof") + }, + fmt.Errorf("failed to unmarshal proof into type"), + }, + { + "consensus state timestamp is greater than signature", + func() { + consensusState := &solomachine.ConsensusState{ + Timestamp: sm.Time + 1, + PublicKey: sm.ConsensusState().PublicKey, + } + + clientState = solomachine.NewClientState(sm.Sequence, consensusState) + }, + fmt.Errorf("the consensus state timestamp is greater than the signature timestamp (11 >= 10): %s", solomachine.ErrInvalidProof), + }, + { + "signature data is nil", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: nil, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + fmt.Errorf("signature data cannot be empty: %s", solomachine.ErrInvalidProof), + }, + { + "consensus state public key is nil", + func() { + clientState.ConsensusState.PublicKey = nil + }, + fmt.Errorf("consensus state PublicKey cannot be nil: %s", clienttypes.ErrInvalidConsensus), + }, + { + "malformed signature data fails to unmarshal", + func() { + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: []byte("invalid signature data"), + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + fmt.Errorf("failed to unmarshal proof into type"), + }, + { + "proof is nil", + func() { + proof = nil + }, + fmt.Errorf("proof cannot be empty: %s", solomachine.ErrInvalidProof), + }, + { + "proof verification failed", + func() { + signBytes.Data = []byte("invalid non-membership data value") + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + }, + solomachine.ErrSignatureVerificationFailed, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + clientState = sm.ClientState() + + path = commitmenttypes.NewMerklePath("ibc", "solomachine") + merklePath, ok := path.(commitmenttypes.MerklePath) + suite.Require().True(ok) + key, err := merklePath.GetKey(1) // in a multistore context: index 0 is the key for the IBC store in the multistore, index 1 is the key in the IBC store + suite.Require().NoError(err) + signBytes = solomachine.SignBytes{ + Sequence: sm.GetHeight().GetRevisionHeight(), + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: key, + Data: nil, + } + + signBz, err := suite.chainA.Codec.Marshal(&signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + + signatureDoc := &solomachine.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: sm.Time, + } + + proof, err = suite.chainA.Codec.Marshal(signatureDoc) + suite.Require().NoError(err) + + tc.malleate() + + var expSeq uint64 + if clientState.ConsensusState != nil { + expSeq = clientState.Sequence + 1 + } + + // Generate clientID + clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + + // Set the client state in the store for light client call to find. + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(smClientID) + suite.Require().True(found) + + // Verify the membership proof + err = lightClientModule.VerifyNonMembership( + suite.chainA.GetContext(), clientID, clienttypes.ZeroHeight(), + 0, 0, proof, path, + ) + + // Grab fresh client state after updates. + cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), clientID) + suite.Require().True(found) + clientState = cs.(*solomachine.ClientState) + + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + suite.Require().Equal(expSeq, clientState.Sequence) + } else { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expErr.Error()) + } + }) + } + } +} + func (suite *SoloMachineTestSuite) TestRecoverClient() { var ( subjectClientID, substituteClientID string diff --git a/modules/light-clients/06-solomachine/proof.go b/modules/light-clients/06-solomachine/proof.go index f156e353408..b50f7e0ec78 100644 --- a/modules/light-clients/06-solomachine/proof.go +++ b/modules/light-clients/06-solomachine/proof.go @@ -26,7 +26,7 @@ func VerifySignature(pubKey cryptotypes.PubKey, signBytes []byte, sigData signin if err := pubKey.VerifyMultisignature(func(signing.SignMode) ([]byte, error) { return signBytes, nil }, data); err != nil { - return err + return errorsmod.Wrapf(ErrSignatureVerificationFailed, err.Error()) } default: From 0817f12eaed38b3e55a0f4a9a592f3fa731ed344 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Mon, 25 Mar 2024 15:36:35 +0200 Subject: [PATCH 02/10] Rename solomachine clients identifiers. --- .../06-solomachine/light_client_module_test.go | 11 +++++------ .../light-clients/06-solomachine/solomachine_test.go | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/light-clients/06-solomachine/light_client_module_test.go b/modules/light-clients/06-solomachine/light_client_module_test.go index 0e8001e2d36..5d0c90b0a78 100644 --- a/modules/light-clients/06-solomachine/light_client_module_test.go +++ b/modules/light-clients/06-solomachine/light_client_module_test.go @@ -22,7 +22,7 @@ const ( ) func (suite *SoloMachineTestSuite) TestStatus() { - clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + clientID := suite.solomachine.ClientID clientState := suite.solomachine.ClientState() // Set a client state in store. @@ -45,7 +45,7 @@ func (suite *SoloMachineTestSuite) TestStatus() { } func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { - clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + clientID := suite.solomachine.ClientID height := clienttypes.NewHeight(0, suite.solomachine.ClientState().Sequence) // Single setup for all test cases. suite.SetupTest() @@ -92,7 +92,6 @@ func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { } func (suite *SoloMachineTestSuite) TestVerifyMembership() { - var clientID string // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { @@ -534,7 +533,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { tc.malleate() // Generate clientID - clientID = suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + clientID := sm.ClientID // Set the client state in the store for light client call to find. suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) @@ -760,7 +759,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { } // Generate clientID - clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + clientID := sm.ClientID // Set the client state in the store for light client call to find. suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) @@ -891,7 +890,7 @@ func (suite *SoloMachineTestSuite) TestRecoverClient() { } func (suite *SoloMachineTestSuite) TestVerifyUpgradeAndUpdateState() { - clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) + clientID := suite.solomachine.ClientID lightClientModule, found := suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.Route(clientID) suite.Require().True(found) diff --git a/modules/light-clients/06-solomachine/solomachine_test.go b/modules/light-clients/06-solomachine/solomachine_test.go index b5b21c894e2..bd2a134e9ef 100644 --- a/modules/light-clients/06-solomachine/solomachine_test.go +++ b/modules/light-clients/06-solomachine/solomachine_test.go @@ -45,8 +45,8 @@ func (suite *SoloMachineTestSuite) SetupTest() { suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) - suite.solomachineMulti = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinemulti", "testing", 4) + suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 1) + suite.solomachineMulti = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-2", "testing", 4) suite.store = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), exported.Solomachine) } From fda0fdc5c5e47a0851bb45fbe1317daf8a353718 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Tue, 26 Mar 2024 03:18:37 +0200 Subject: [PATCH 03/10] ref: clean up previous tests, add two more for LatestHeight, UpdateStatus. --- .../light_client_module_test.go | 335 +++++++++++++----- .../06-solomachine/misbehaviour_test.go | 2 +- .../06-solomachine/proposal_handle_test.go | 2 +- .../06-solomachine/update_test.go | 2 +- 4 files changed, 252 insertions(+), 89 deletions(-) diff --git a/modules/light-clients/06-solomachine/light_client_module_test.go b/modules/light-clients/06-solomachine/light_client_module_test.go index 0d09abbba0c..ad84aa37cbb 100644 --- a/modules/light-clients/06-solomachine/light_client_module_test.go +++ b/modules/light-clients/06-solomachine/light_client_module_test.go @@ -18,38 +18,69 @@ import ( ) const ( - smClientID = "06-solomachine-100" - wasmClientID = "08-wasm-0" + smClientID = "06-solomachine-100" + unusedSmClientID = "06-solomachine-999" + wasmClientID = "08-wasm-0" ) func (suite *SoloMachineTestSuite) TestStatus() { - clientID := suite.solomachine.ClientID - clientState := suite.solomachine.ClientState() + var ( + clientState *solomachine.ClientState + clientID string + ) - // Set a client state in store. - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + testCases := []struct { + name string + malleate func() + expStatus exported.Status + }{ + { + "client is active", + func() {}, + exported.Active, + }, + { + "client is frozen", + func() { + clientState = solomachine.NewClientState(0, &solomachine.ConsensusState{}) + clientState.IsFrozen = true + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + }, + exported.Frozen, + }, + { + "cannot find subject client state", + func() { + clientID = unusedSmClientID + }, + exported.Unknown, + }, + } - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) - suite.Require().True(found) + for _, tc := range testCases { + tc := tc - status := lightClientModule.Status(suite.chainA.GetContext(), clientID) + suite.Run(tc.name, func() { + clientID = suite.solomachine.ClientID + clientState = suite.solomachine.ClientState() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) - // solo machine discards arguments - suite.Require().Equal(exported.Active, status) + tc.malleate() - // freeze solo machine and update it in store. - clientState.IsFrozen = true - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + status := lightClientModule.Status(suite.chainA.GetContext(), clientID) - status = clientState.Status(suite.chainA.GetContext(), nil, nil) - suite.Require().Equal(exported.Frozen, status) + suite.Require().Equal(tc.expStatus, status) + }) + } } func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { clientID := suite.solomachine.ClientID height := clienttypes.NewHeight(0, suite.solomachine.ClientState().Sequence) - // Single setup for all test cases. - suite.SetupTest() testCases := []struct { name string @@ -65,29 +96,28 @@ func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { }, { "client not found", - "non existent client", + unusedSmClientID, 0, clienttypes.ErrClientNotFound, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc suite.Run(tc.name, func() { - ctx := suite.chainA.GetContext() - - // Set a client state in store and grab light client module for _clientID_, the lookup for the timestamp - // is performed on the _tc.clientID_. - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(ctx, clientID, suite.solomachine.ClientState()) + clientID = suite.solomachine.ClientID + clientState := suite.solomachine.ClientState() lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) suite.Require().True(found) - ts, err := lightClientModule.TimestampAtHeight(ctx, tc.clientID, height) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + + ts, err := lightClientModule.TimestampAtHeight(suite.chainA.GetContext(), tc.clientID, height) suite.Require().Equal(tc.expValue, ts) - suite.Require().ErrorIs(err, tc.expErr, "valid test case %d failed: %s", i, tc.name) + suite.Require().ErrorIs(err, tc.expErr) }) } } @@ -147,12 +177,11 @@ func (suite *SoloMachineTestSuite) TestInitialize() { suite.Run(tc.name, func() { suite.SetupTest() + clientID := sm.ClientID clientStateBz := suite.chainA.Codec.MustMarshal(tc.clientState) consStateBz := suite.chainA.Codec.MustMarshal(tc.consState) - clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), exported.Solomachine) - lcm, found := suite.chainA.GetSimApp().IBCKeeper.ClientKeeper.Route(clientID) suite.Require().True(found) @@ -161,7 +190,7 @@ func (suite *SoloMachineTestSuite) TestInitialize() { expPass := tc.expErr == nil if expPass { - suite.Require().NoError(err, "valid testcase: %s failed", tc.name) + suite.Require().NoError(err) suite.Require().True(store.Has(host.ClientStateKey())) } else { suite.Require().ErrorContains(err, tc.expErr.Error()) @@ -173,18 +202,19 @@ func (suite *SoloMachineTestSuite) TestInitialize() { } func (suite *SoloMachineTestSuite) TestVerifyMembership() { + var ( + clientState *solomachine.ClientState + path exported.Path + proof []byte + testingPath *ibctesting.Path + signBytes solomachine.SignBytes + err error + clientID string + ) + // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - var ( - clientState *solomachine.ClientState - path exported.Path - proof []byte - testingPath *ibctesting.Path - signBytes solomachine.SignBytes - err error - ) - testCases := []struct { name string malleate func() @@ -487,12 +517,13 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { }, nil, }, - // TODO: Cov missing, need a more extensive refactor of tests in order to cov it. - // { - // "client not found", - // func() {}, - // clienttypes.ErrClientNotFound, - // }, + { + "cannot find subject client state", + func() { + clientID = unusedSmClientID + }, + clienttypes.ErrClientNotFound, + }, { "invalid path type - empty", func() { @@ -517,6 +548,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { } clientState = solomachine.NewClientState(sm.Sequence, consensusState) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) }, fmt.Errorf("the consensus state timestamp is greater than the signature timestamp (11 >= 10): %s", solomachine.ErrInvalidProof), }, @@ -537,6 +569,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { "consensus state public key is nil", func() { clientState.ConsensusState.PublicKey = nil + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) }, fmt.Errorf("consensus state PublicKey cannot be nil: %s", clienttypes.ErrInvalidConsensus), }, @@ -583,6 +616,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { suite.SetupTest() testingPath = ibctesting.NewPath(suite.chainA, suite.chainB) + clientID = sm.ClientID clientState = sm.ClientState() path = commitmenttypes.NewMerklePath("ibc", "solomachine") @@ -611,35 +645,32 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { proof, err = suite.chainA.Codec.Marshal(signatureDoc) suite.Require().NoError(err) - tc.malleate() - - // Generate clientID - clientID := sm.ClientID + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) // Set the client state in the store for light client call to find. suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + tc.malleate() + var expSeq uint64 if clientState.ConsensusState != nil { expSeq = clientState.Sequence + 1 } - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(smClientID) - suite.Require().True(found) - // Verify the membership proof err = lightClientModule.VerifyMembership( suite.chainA.GetContext(), clientID, clienttypes.ZeroHeight(), 0, 0, proof, path, signBytes.Data, ) - // Grab fresh client state after updates. - cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), clientID) - suite.Require().True(found) - clientState = cs.(*solomachine.ClientState) - expPass := tc.expErr == nil if expPass { + // Grab fresh client state after updates. + cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), clientID) + suite.Require().True(found) + clientState = cs.(*solomachine.ClientState) + suite.Require().NoError(err) // clientState.Sequence is the most recent view of state. suite.Require().Equal(expSeq, clientState.Sequence) @@ -653,17 +684,17 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { } func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { + var ( + clientState *solomachine.ClientState + path exported.Path + proof []byte + signBytes solomachine.SignBytes + err error + clientID string + ) + // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - var ( - clientState *solomachine.ClientState - path exported.Path - proof []byte - signBytes solomachine.SignBytes - err error - ) - testCases := []struct { name string malleate func() @@ -705,12 +736,13 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { }, nil, }, - // TODO: Cov missing, need a more extensive refactor of tests in order to cov it. - // { - // "client not found", - // func() {}, - // clienttypes.ErrClientNotFound, - // }, + { + "cannot find subject client state", + func() { + clientID = unusedSmClientID + }, + clienttypes.ErrClientNotFound, + }, { "invalid path type", func() { @@ -735,6 +767,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { } clientState = solomachine.NewClientState(sm.Sequence, consensusState) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) }, fmt.Errorf("the consensus state timestamp is greater than the signature timestamp (11 >= 10): %s", solomachine.ErrInvalidProof), }, @@ -755,6 +788,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { "consensus state public key is nil", func() { clientState.ConsensusState.PublicKey = nil + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) }, fmt.Errorf("consensus state PublicKey cannot be nil: %s", clienttypes.ErrInvalidConsensus), }, @@ -805,6 +839,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { suite.Run(tc.name, func() { clientState = sm.ClientState() + clientID = sm.ClientID path = commitmenttypes.NewMerklePath("ibc", "solomachine") merklePath, ok := path.(commitmenttypes.MerklePath) @@ -832,6 +867,12 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { proof, err = suite.chainA.Codec.Marshal(signatureDoc) suite.Require().NoError(err) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + // Set the client state in the store for light client call to find. + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + tc.malleate() var expSeq uint64 @@ -839,28 +880,19 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { expSeq = clientState.Sequence + 1 } - // Generate clientID - clientID := sm.ClientID - - // Set the client state in the store for light client call to find. - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) - - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(smClientID) - suite.Require().True(found) - // Verify the membership proof err = lightClientModule.VerifyNonMembership( suite.chainA.GetContext(), clientID, clienttypes.ZeroHeight(), 0, 0, proof, path, ) - // Grab fresh client state after updates. - cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), clientID) - suite.Require().True(found) - clientState = cs.(*solomachine.ClientState) - expPass := tc.expErr == nil if expPass { + // Grab fresh client state after updates. + cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), clientID) + suite.Require().True(found) + clientState = cs.(*solomachine.ClientState) + suite.Require().NoError(err) suite.Require().Equal(expSeq, clientState.Sequence) } else { @@ -970,6 +1002,92 @@ func (suite *SoloMachineTestSuite) TestRecoverClient() { } } +func (suite *SoloMachineTestSuite) TestUpdateState() { + var ( + clientState *solomachine.ClientState + clientMsg exported.ClientMessage + clientID string + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + malleate func() + expPanic error + }{ + { + "successful update", + func() {}, + nil, + }, + { + "invalid type misbehaviour", + func() { + clientState = sm.ClientState() + clientMsg = sm.CreateMisbehaviour() + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + }, + fmt.Errorf("unsupported ClientMessage: %T", sm.CreateMisbehaviour()), + }, + { + "cannot find subject client state", + func() { + clientID = unusedSmClientID + }, + fmt.Errorf("%s: %s", unusedSmClientID, clienttypes.ErrClientNotFound), + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + clientID = sm.ClientID + clientState = sm.ClientState() + clientMsg = sm.CreateHeader(sm.Diversifier) + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + + tc.malleate() // setup test + + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), clientID) + + var consensusHeights []exported.Height + updateStateFunc := func() { + consensusHeights = lightClientModule.UpdateState(suite.chainA.GetContext(), clientID, clientMsg) + } + + expPass := tc.expPanic == nil + if expPass { + updateStateFunc() + + clientStateBz := store.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + + suite.Require().Len(consensusHeights, 1) + suite.Require().Equal(uint64(0), consensusHeights[0].GetRevisionNumber()) + suite.Require().Equal(newClientState.(*solomachine.ClientState).Sequence, consensusHeights[0].GetRevisionHeight()) + + suite.Require().False(newClientState.(*solomachine.ClientState).IsFrozen) + suite.Require().Equal(clientMsg.(*solomachine.Header).NewPublicKey, newClientState.(*solomachine.ClientState).ConsensusState.PublicKey) + suite.Require().Equal(clientMsg.(*solomachine.Header).NewDiversifier, newClientState.(*solomachine.ClientState).ConsensusState.Diversifier) + suite.Require().Equal(clientMsg.(*solomachine.Header).Timestamp, newClientState.(*solomachine.ClientState).ConsensusState.Timestamp) + } else { + suite.Require().PanicsWithError(tc.expPanic.Error(), updateStateFunc) + } + }) + } + } +} + func (suite *SoloMachineTestSuite) TestVerifyUpgradeAndUpdateState() { clientID := suite.solomachine.ClientID @@ -979,3 +1097,48 @@ func (suite *SoloMachineTestSuite) TestVerifyUpgradeAndUpdateState() { err := lightClientModule.VerifyUpgradeAndUpdateState(suite.chainA.GetContext(), clientID, nil, nil, nil, nil) suite.Require().Error(err) } + +func (suite *SoloMachineTestSuite) TestLatestHeight() { + var clientID string + + testCases := []struct { + name string + malleate func() + expHeight clienttypes.Height + }{ + { + "success", + func() { + }, + // Default as returned by solomachine.ClientState() + clienttypes.NewHeight(0, 1), + }, + { + "cannot find substitute client state", + func() { + clientID = unusedSmClientID + }, + clienttypes.ZeroHeight(), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + clientID = suite.solomachine.ClientID + clientState := suite.solomachine.ClientState() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + + tc.malleate() + + height := lightClientModule.LatestHeight(suite.chainA.GetContext(), clientID) + + suite.Require().Equal(tc.expHeight, height) + }) + } +} diff --git a/modules/light-clients/06-solomachine/misbehaviour_test.go b/modules/light-clients/06-solomachine/misbehaviour_test.go index 5fa0dd8a210..7cc02e6aecc 100644 --- a/modules/light-clients/06-solomachine/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/misbehaviour_test.go @@ -12,7 +12,7 @@ func (suite *SoloMachineTestSuite) TestMisbehaviour() { suite.Require().Equal(exported.Solomachine, misbehaviour.ClientType()) } -func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { +func (suite *SoloMachineTestSuite) Test_MisbehaviourValidateBasic() { // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { diff --git a/modules/light-clients/06-solomachine/proposal_handle_test.go b/modules/light-clients/06-solomachine/proposal_handle_test.go index 35109d66a41..e64e5958fa1 100644 --- a/modules/light-clients/06-solomachine/proposal_handle_test.go +++ b/modules/light-clients/06-solomachine/proposal_handle_test.go @@ -9,7 +9,7 @@ import ( ibctesting "github.com/cosmos/ibc-go/v8/testing" ) -func (suite *SoloMachineTestSuite) TestCheckSubstituteAndUpdateState() { +func (suite *SoloMachineTestSuite) Test_Check_SubstituteAndUpdateState() { var ( subjectClientState *solomachine.ClientState substituteClientState exported.ClientState diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go index 4299f249fb1..46ed4193be8 100644 --- a/modules/light-clients/06-solomachine/update_test.go +++ b/modules/light-clients/06-solomachine/update_test.go @@ -397,7 +397,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { } } -func (suite *SoloMachineTestSuite) TestUpdateState() { +func (suite *SoloMachineTestSuite) Test_UpdateState() { var ( clientState *solomachine.ClientState clientMsg exported.ClientMessage From 19cd092fb046bc2b06eb7b15d018ba741995d024 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Tue, 26 Mar 2024 04:02:45 +0200 Subject: [PATCH 04/10] ref: add verifying funcs. --- .../light_client_module_test.go | 558 ++++++++++++++++++ .../06-solomachine/update_test.go | 8 +- 2 files changed, 562 insertions(+), 4 deletions(-) diff --git a/modules/light-clients/06-solomachine/light_client_module_test.go b/modules/light-clients/06-solomachine/light_client_module_test.go index ad84aa37cbb..2d155d1b18a 100644 --- a/modules/light-clients/06-solomachine/light_client_module_test.go +++ b/modules/light-clients/06-solomachine/light_client_module_test.go @@ -3,6 +3,7 @@ package solomachine_test import ( "fmt" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" @@ -1088,6 +1089,563 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { } } +func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { + var ( + clientMsg exported.ClientMessage + clientID string + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + foundMisbehaviour bool + expPanic error + }{ + { + "success", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + true, + nil, + }, + { + "normal header returns false", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + false, + nil, + }, + { + "cannot find subject client state", + func() { + clientID = unusedSmClientID + }, + false, + fmt.Errorf("%s: %s", unusedSmClientID, clienttypes.ErrClientNotFound), + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + clientID = sm.ClientID + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, sm.ClientState()) + + tc.malleate() + + var foundMisbehaviour bool + foundMisbehaviourFunc := func() { + foundMisbehaviour = lightClientModule.CheckForMisbehaviour(suite.chainA.GetContext(), clientID, clientMsg) + } + + expPass := tc.expPanic == nil + if expPass { + foundMisbehaviourFunc() + + suite.Require().Equal(tc.foundMisbehaviour, foundMisbehaviour) + } else { + suite.Require().PanicsWithError(tc.expPanic.Error(), foundMisbehaviourFunc) + suite.Require().False(foundMisbehaviour) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { + var clientID string + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + testCases := []struct { + name string + malleate func() + expPanic error + }{ + { + "success", + func() {}, + nil, + }, + { + "cannot find subject client state", + func() { + clientID = unusedSmClientID + }, + fmt.Errorf("%s: %s", unusedSmClientID, clienttypes.ErrClientNotFound), + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + clientID = sm.ClientID + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, sm.ClientState()) + + tc.malleate() + + updateOnMisbehaviourFunc := func() { + lightClientModule.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), clientID, nil) + } + + expPass := tc.expPanic == nil + if expPass { + updateOnMisbehaviourFunc() + + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), clientID) + + clientStateBz := store.Get(host.ClientStateKey()) + suite.Require().NotEmpty(clientStateBz) + + newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) + + suite.Require().True(newClientState.(*solomachine.ClientState).IsFrozen) + } else { + suite.Require().PanicsWithError(tc.expPanic.Error(), updateOnMisbehaviourFunc) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { + var ( + clientMsg exported.ClientMessage + clientState *solomachine.ClientState + clientID string + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "successful header", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + nil, + }, + { + "successful header with new diversifier", + func() { + clientMsg = sm.CreateHeader(sm.Diversifier + "0") + }, + nil, + }, + { + "successful misbehaviour", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + nil, + }, + { + "invalid client message type", + func() { + clientMsg = &ibctm.Header{} + }, + clienttypes.ErrInvalidClientType, + }, + { + "invalid header Signature", + func() { + h := sm.CreateHeader(sm.Diversifier) + h.Signature = suite.GetInvalidProof() + clientMsg = h + }, fmt.Errorf("proto: wrong wireType = 0 for field Multi"), + }, + { + "invalid timestamp in header", + func() { + h := sm.CreateHeader(sm.Diversifier) + h.Timestamp-- + clientMsg = h + }, clienttypes.ErrInvalidHeader, + }, + { + "signature uses wrong sequence", + func() { + sm.Sequence++ + clientMsg = sm.CreateHeader(sm.Diversifier) + }, + solomachine.ErrSignatureVerificationFailed, + }, + { + "signature uses new pubkey to sign", + func() { + // store in temp before assigning to interface type + cs := sm.ClientState() + h := sm.CreateHeader(sm.Diversifier) + + publicKey, err := codectypes.NewAnyWithValue(sm.PublicKey) + suite.NoError(err) + + data := &solomachine.HeaderData{ + NewPubKey: publicKey, + NewDiversifier: h.NewDiversifier, + } + + dataBz, err := suite.chainA.Codec.Marshal(data) + suite.Require().NoError(err) + + // generate invalid signature + signBytes := &solomachine.SignBytes{ + Sequence: cs.Sequence, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: dataBz, + } + + signBz, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(signBz) + suite.Require().NoError(err) + h.Signature = sig + + clientState = cs + clientMsg = h + }, + solomachine.ErrSignatureVerificationFailed, + }, + { + "signature signs over old pubkey", + func() { + // store in temp before assigning to interface type + cs := sm.ClientState() + oldPubKey := sm.PublicKey + h := sm.CreateHeader(sm.Diversifier) + + // generate invalid signature + data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) + sig := sm.GenerateSignature(data) + h.Signature = sig + + clientState = cs + clientMsg = h + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, sm.ClientState()) + }, + // TODO(jim): Doesn't fail on VerifySignature + clienttypes.ErrInvalidHeader, + }, + { + "consensus state public key is nil - header", + func() { + clientState.ConsensusState.PublicKey = nil + clientMsg = sm.CreateHeader(sm.Diversifier) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, sm.ClientState()) + }, + // TODO(jim): Doesn't fail on VerifySignature + clienttypes.ErrInvalidHeader, + }, + { + "cannot find subject client state", + func() { + clientID = unusedSmClientID + }, + fmt.Errorf("%s: %s", unusedSmClientID, clienttypes.ErrClientNotFound), + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + clientID = sm.ClientID + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, sm.ClientState()) + + // setup test + tc.malleate() + + err := lightClientModule.VerifyClientMessage(suite.chainA.GetContext(), clientID, clientMsg) + + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + } else { + suite.Require().ErrorContains(err, tc.expErr.Error()) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { + var ( + clientMsg exported.ClientMessage + clientState *solomachine.ClientState + clientID string + ) + + // test singlesig and multisig public keys + for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "successful misbehaviour", + func() { + clientMsg = sm.CreateMisbehaviour() + }, + nil, + }, + { + "old misbehaviour is successful (timestamp is less than current consensus state)", + func() { + clientState = sm.ClientState() + sm.Time -= 5 + clientMsg = sm.CreateMisbehaviour() + }, nil, + }, + { + "invalid client message type", + func() { + clientMsg = &ibctm.Header{} + }, + clienttypes.ErrInvalidClientType, + }, + { + "consensus state pubkey is nil", + func() { + clientState.ConsensusState.PublicKey = nil + clientMsg = sm.CreateMisbehaviour() + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + }, + clienttypes.ErrInvalidConsensus, + }, + { + "invalid SignatureOne SignatureData", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureOne.Signature = suite.GetInvalidProof() + clientMsg = m + }, fmt.Errorf("proto: wrong wireType = 0 for field Multi"), + }, + { + "invalid SignatureTwo SignatureData", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureTwo.Signature = suite.GetInvalidProof() + clientMsg = m + }, fmt.Errorf("proto: wrong wireType = 0 for field Multi"), + }, + { + "invalid SignatureOne timestamp", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureOne.Timestamp = 1000000000000 + clientMsg = m + }, solomachine.ErrSignatureVerificationFailed, + }, + { + "invalid SignatureTwo timestamp", + func() { + m := sm.CreateMisbehaviour() + + m.SignatureTwo.Timestamp = 1000000000000 + clientMsg = m + }, solomachine.ErrSignatureVerificationFailed, + }, + { + "invalid first signature data", + func() { + // store in temp before assigning to interface type + m := sm.CreateMisbehaviour() + + msg := []byte("DATA ONE") + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + clientMsg = m + }, + solomachine.ErrSignatureVerificationFailed, + }, + { + "invalid second signature data", + func() { + // store in temp before assigning to interface type + m := sm.CreateMisbehaviour() + + msg := []byte("DATA TWO") + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + clientMsg = m + }, + solomachine.ErrSignatureVerificationFailed, + }, + { + "wrong pubkey generates first signature", + func() { + badMisbehaviour := sm.CreateMisbehaviour() + + // update public key to a new one + sm.CreateHeader(sm.Diversifier) + m := sm.CreateMisbehaviour() + + // set SignatureOne to use the wrong signature + m.SignatureOne = badMisbehaviour.SignatureOne + clientMsg = m + }, solomachine.ErrSignatureVerificationFailed, + }, + { + "wrong pubkey generates second signature", + func() { + badMisbehaviour := sm.CreateMisbehaviour() + + // update public key to a new one + sm.CreateHeader(sm.Diversifier) + m := sm.CreateMisbehaviour() + + // set SignatureTwo to use the wrong signature + m.SignatureTwo = badMisbehaviour.SignatureTwo + clientMsg = m + }, solomachine.ErrSignatureVerificationFailed, + }, + { + "signatures sign over different sequence", + func() { + // store in temp before assigning to interface type + m := sm.CreateMisbehaviour() + + // Signature One + msg := []byte("DATA ONE") + // sequence used is plus 1 + signBytes := &solomachine.SignBytes{ + Sequence: sm.Sequence + 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + + data, err := suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig := sm.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + + // Signature Two + msg = []byte("DATA TWO") + // sequence used is minus 1 + + signBytes = &solomachine.SignBytes{ + Sequence: sm.Sequence - 1, + Timestamp: sm.Time, + Diversifier: sm.Diversifier, + Path: []byte("invalid signature data"), + Data: msg, + } + data, err = suite.chainA.Codec.Marshal(signBytes) + suite.Require().NoError(err) + + sig = sm.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + + clientMsg = m + }, + solomachine.ErrSignatureVerificationFailed, + }, + { + "cannot find subject client state", + func() { + clientID = unusedSmClientID + }, + fmt.Errorf("%s: %s", unusedSmClientID, clienttypes.ErrClientNotFound), + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + clientID = sm.ClientID + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, sm.ClientState()) + + // setup test + tc.malleate() + + err := lightClientModule.VerifyClientMessage(suite.chainA.GetContext(), clientID, clientMsg) + + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + } else { + suite.Require().ErrorContains(err, tc.expErr.Error()) + } + }) + } + } +} + func (suite *SoloMachineTestSuite) TestVerifyUpgradeAndUpdateState() { clientID := suite.solomachine.ClientID diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go index 46ed4193be8..b9eac2e8005 100644 --- a/modules/light-clients/06-solomachine/update_test.go +++ b/modules/light-clients/06-solomachine/update_test.go @@ -12,7 +12,7 @@ import ( ibctesting "github.com/cosmos/ibc-go/v8/testing" ) -func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { +func (suite *SoloMachineTestSuite) Test_VerifyClientMessageHeader() { var ( clientMsg exported.ClientMessage clientState *solomachine.ClientState @@ -166,7 +166,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { } } -func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { +func (suite *SoloMachineTestSuite) Test_VerifyClientMessageMisbehaviour() { var ( clientMsg exported.ClientMessage clientState *solomachine.ClientState @@ -461,7 +461,7 @@ func (suite *SoloMachineTestSuite) Test_UpdateState() { } } -func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { +func (suite *SoloMachineTestSuite) Test_CheckForMisbehaviour() { var clientMsg exported.ClientMessage // test singlesig and multisig public keys @@ -507,7 +507,7 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { } } -func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { +func (suite *SoloMachineTestSuite) Test_UpdateStateOnMisbehaviour() { // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { testCases := []struct { From 9040312c76798223432edef19ae19cd8d4401063 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Tue, 26 Mar 2024 04:06:27 +0200 Subject: [PATCH 05/10] rm: old tests. --- .../06-solomachine/misbehaviour_test.go | 2 +- .../06-solomachine/proposal_handle_test.go | 83 --- .../06-solomachine/update_test.go | 546 ------------------ 3 files changed, 1 insertion(+), 630 deletions(-) delete mode 100644 modules/light-clients/06-solomachine/proposal_handle_test.go delete mode 100644 modules/light-clients/06-solomachine/update_test.go diff --git a/modules/light-clients/06-solomachine/misbehaviour_test.go b/modules/light-clients/06-solomachine/misbehaviour_test.go index 7cc02e6aecc..5fa0dd8a210 100644 --- a/modules/light-clients/06-solomachine/misbehaviour_test.go +++ b/modules/light-clients/06-solomachine/misbehaviour_test.go @@ -12,7 +12,7 @@ func (suite *SoloMachineTestSuite) TestMisbehaviour() { suite.Require().Equal(exported.Solomachine, misbehaviour.ClientType()) } -func (suite *SoloMachineTestSuite) Test_MisbehaviourValidateBasic() { +func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { diff --git a/modules/light-clients/06-solomachine/proposal_handle_test.go b/modules/light-clients/06-solomachine/proposal_handle_test.go deleted file mode 100644 index e64e5958fa1..00000000000 --- a/modules/light-clients/06-solomachine/proposal_handle_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package solomachine_test - -import ( - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v8/modules/core/24-host" - "github.com/cosmos/ibc-go/v8/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v8/testing" -) - -func (suite *SoloMachineTestSuite) Test_Check_SubstituteAndUpdateState() { - var ( - subjectClientState *solomachine.ClientState - substituteClientState exported.ClientState - ) - - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "substitute is not the solo machine", func() { - substituteClientState = &ibctm.ClientState{} - }, false, - }, - { - "subject public key is nil", func() { - subjectClientState.ConsensusState.PublicKey = nil - }, false, - }, - - { - "substitute public key is nil", func() { - substituteClientState.(*solomachine.ClientState).ConsensusState.PublicKey = nil - }, false, - }, - { - "subject and substitute use the same public key", func() { - substituteClientState.(*solomachine.ClientState).ConsensusState.PublicKey = subjectClientState.ConsensusState.PublicKey - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() - - subjectClientState = sm.ClientState() - substitute := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "substitute", "testing", 5) - substituteClientState = substitute.ClientState() - - tc.malleate() - - subjectClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), sm.ClientID) - substituteClientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), substitute.ClientID) - - err := subjectClientState.CheckSubstituteAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), subjectClientStore, substituteClientStore, substituteClientState) - - if tc.expPass { - suite.Require().NoError(err) - - // ensure updated client state is set in store - bz := subjectClientStore.Get(host.ClientStateKey()) - updatedClient := clienttypes.MustUnmarshalClientState(suite.chainA.App.AppCodec(), bz).(*solomachine.ClientState) - - suite.Require().Equal(substituteClientState.(*solomachine.ClientState).ConsensusState, updatedClient.ConsensusState) - suite.Require().Equal(substituteClientState.(*solomachine.ClientState).Sequence, updatedClient.Sequence) - suite.Require().Equal(false, updatedClient.IsFrozen) - - } else { - suite.Require().Error(err) - } - }) - } - } -} diff --git a/modules/light-clients/06-solomachine/update_test.go b/modules/light-clients/06-solomachine/update_test.go deleted file mode 100644 index b9eac2e8005..00000000000 --- a/modules/light-clients/06-solomachine/update_test.go +++ /dev/null @@ -1,546 +0,0 @@ -package solomachine_test - -import ( - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" - - clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - host "github.com/cosmos/ibc-go/v8/modules/core/24-host" - "github.com/cosmos/ibc-go/v8/modules/core/exported" - solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v8/testing" -) - -func (suite *SoloMachineTestSuite) Test_VerifyClientMessageHeader() { - var ( - clientMsg exported.ClientMessage - clientState *solomachine.ClientState - ) - - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - setup func() - expPass bool - }{ - { - "successful header", - func() { - clientMsg = sm.CreateHeader(sm.Diversifier) - }, - true, - }, - { - "successful header with new diversifier", - func() { - clientMsg = sm.CreateHeader(sm.Diversifier + "0") - }, - true, - }, - { - "successful misbehaviour", - func() { - clientMsg = sm.CreateMisbehaviour() - }, - true, - }, - { - "invalid client message type", - func() { - clientMsg = &ibctm.Header{} - }, - false, - }, - { - "invalid header Signature", - func() { - h := sm.CreateHeader(sm.Diversifier) - h.Signature = suite.GetInvalidProof() - clientMsg = h - }, false, - }, - { - "invalid timestamp in header", - func() { - h := sm.CreateHeader(sm.Diversifier) - h.Timestamp-- - clientMsg = h - }, false, - }, - { - "signature uses wrong sequence", - func() { - sm.Sequence++ - clientMsg = sm.CreateHeader(sm.Diversifier) - }, - false, - }, - { - "signature uses new pubkey to sign", - func() { - // store in temp before assigning to interface type - cs := sm.ClientState() - h := sm.CreateHeader(sm.Diversifier) - - publicKey, err := codectypes.NewAnyWithValue(sm.PublicKey) - suite.NoError(err) - - data := &solomachine.HeaderData{ - NewPubKey: publicKey, - NewDiversifier: h.NewDiversifier, - } - - dataBz, err := suite.chainA.Codec.Marshal(data) - suite.Require().NoError(err) - - // generate invalid signature - signBytes := &solomachine.SignBytes{ - Sequence: cs.Sequence, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: []byte("invalid signature data"), - Data: dataBz, - } - - signBz, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(signBz) - suite.Require().NoError(err) - h.Signature = sig - - clientState = cs - clientMsg = h - }, - false, - }, - { - "signature signs over old pubkey", - func() { - // store in temp before assigning to interface type - cs := sm.ClientState() - oldPubKey := sm.PublicKey - h := sm.CreateHeader(sm.Diversifier) - - // generate invalid signature - data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) - sig := sm.GenerateSignature(data) - h.Signature = sig - - clientState = cs - clientMsg = h - }, - false, - }, - { - "consensus state public key is nil - header", - func() { - clientState.ConsensusState.PublicKey = nil - clientMsg = sm.CreateHeader(sm.Diversifier) - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - clientState = sm.ClientState() - - // setup test - tc.setup() - - err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } - } -} - -func (suite *SoloMachineTestSuite) Test_VerifyClientMessageMisbehaviour() { - var ( - clientMsg exported.ClientMessage - clientState *solomachine.ClientState - ) - - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - setup func() - expPass bool - }{ - { - "successful misbehaviour", - func() { - clientMsg = sm.CreateMisbehaviour() - }, - true, - }, - { - "old misbehaviour is successful (timestamp is less than current consensus state)", - func() { - clientState = sm.ClientState() - sm.Time -= 5 - clientMsg = sm.CreateMisbehaviour() - }, true, - }, - { - "invalid client message type", - func() { - clientMsg = &ibctm.Header{} - }, - false, - }, - { - "consensus state pubkey is nil", - func() { - clientState.ConsensusState.PublicKey = nil - clientMsg = sm.CreateMisbehaviour() - }, - false, - }, - { - "invalid SignatureOne SignatureData", - func() { - m := sm.CreateMisbehaviour() - - m.SignatureOne.Signature = suite.GetInvalidProof() - clientMsg = m - }, false, - }, - { - "invalid SignatureTwo SignatureData", - func() { - m := sm.CreateMisbehaviour() - - m.SignatureTwo.Signature = suite.GetInvalidProof() - clientMsg = m - }, false, - }, - { - "invalid SignatureOne timestamp", - func() { - m := sm.CreateMisbehaviour() - - m.SignatureOne.Timestamp = 1000000000000 - clientMsg = m - }, false, - }, - { - "invalid SignatureTwo timestamp", - func() { - m := sm.CreateMisbehaviour() - - m.SignatureTwo.Timestamp = 1000000000000 - clientMsg = m - }, false, - }, - { - "invalid first signature data", - func() { - // store in temp before assigning to interface type - m := sm.CreateMisbehaviour() - - msg := []byte("DATA ONE") - signBytes := &solomachine.SignBytes{ - Sequence: sm.Sequence + 1, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: []byte("invalid signature data"), - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(data) - - m.SignatureOne.Signature = sig - m.SignatureOne.Data = msg - clientMsg = m - }, - false, - }, - { - "invalid second signature data", - func() { - // store in temp before assigning to interface type - m := sm.CreateMisbehaviour() - - msg := []byte("DATA TWO") - signBytes := &solomachine.SignBytes{ - Sequence: sm.Sequence + 1, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: []byte("invalid signature data"), - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(data) - - m.SignatureTwo.Signature = sig - m.SignatureTwo.Data = msg - clientMsg = m - }, - false, - }, - { - "wrong pubkey generates first signature", - func() { - badMisbehaviour := sm.CreateMisbehaviour() - - // update public key to a new one - sm.CreateHeader(sm.Diversifier) - m := sm.CreateMisbehaviour() - - // set SignatureOne to use the wrong signature - m.SignatureOne = badMisbehaviour.SignatureOne - clientMsg = m - }, false, - }, - { - "wrong pubkey generates second signature", - func() { - badMisbehaviour := sm.CreateMisbehaviour() - - // update public key to a new one - sm.CreateHeader(sm.Diversifier) - m := sm.CreateMisbehaviour() - - // set SignatureTwo to use the wrong signature - m.SignatureTwo = badMisbehaviour.SignatureTwo - clientMsg = m - }, false, - }, - { - "signatures sign over different sequence", - func() { - // store in temp before assigning to interface type - m := sm.CreateMisbehaviour() - - // Signature One - msg := []byte("DATA ONE") - // sequence used is plus 1 - signBytes := &solomachine.SignBytes{ - Sequence: sm.Sequence + 1, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: []byte("invalid signature data"), - Data: msg, - } - - data, err := suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig := sm.GenerateSignature(data) - - m.SignatureOne.Signature = sig - m.SignatureOne.Data = msg - - // Signature Two - msg = []byte("DATA TWO") - // sequence used is minus 1 - - signBytes = &solomachine.SignBytes{ - Sequence: sm.Sequence - 1, - Timestamp: sm.Time, - Diversifier: sm.Diversifier, - Path: []byte("invalid signature data"), - Data: msg, - } - data, err = suite.chainA.Codec.Marshal(signBytes) - suite.Require().NoError(err) - - sig = sm.GenerateSignature(data) - - m.SignatureTwo.Signature = sig - m.SignatureTwo.Data = msg - - clientMsg = m - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - clientState = sm.ClientState() - - // setup test - tc.setup() - - err := clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } - } -} - -func (suite *SoloMachineTestSuite) Test_UpdateState() { - var ( - clientState *solomachine.ClientState - clientMsg exported.ClientMessage - ) - - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - - testCases := []struct { - name string - setup func() - expPass bool - }{ - { - "successful update", - func() { - clientState = sm.ClientState() - clientMsg = sm.CreateHeader(sm.Diversifier) - }, - true, - }, - { - "invalid type misbehaviour", - func() { - clientState = sm.ClientState() - clientMsg = sm.CreateMisbehaviour() - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - tc.setup() // setup test - - if tc.expPass { - consensusHeights := clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) - - clientStateBz := suite.store.Get(host.ClientStateKey()) - suite.Require().NotEmpty(clientStateBz) - - newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) - - suite.Require().Len(consensusHeights, 1) - suite.Require().Equal(uint64(0), consensusHeights[0].GetRevisionNumber()) - suite.Require().Equal(newClientState.(*solomachine.ClientState).Sequence, consensusHeights[0].GetRevisionHeight()) - - suite.Require().False(newClientState.(*solomachine.ClientState).IsFrozen) - suite.Require().Equal(clientMsg.(*solomachine.Header).NewPublicKey, newClientState.(*solomachine.ClientState).ConsensusState.PublicKey) - suite.Require().Equal(clientMsg.(*solomachine.Header).NewDiversifier, newClientState.(*solomachine.ClientState).ConsensusState.Diversifier) - suite.Require().Equal(clientMsg.(*solomachine.Header).Timestamp, newClientState.(*solomachine.ClientState).ConsensusState.Timestamp) - } else { - suite.Require().Panics(func() { - clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, clientMsg) - }) - } - }) - } - } -} - -func (suite *SoloMachineTestSuite) Test_CheckForMisbehaviour() { - var clientMsg exported.ClientMessage - - // test singlesig and multisig public keys - for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "success", - func() { - clientMsg = sm.CreateMisbehaviour() - }, - true, - }, - { - "normal header returns false", - func() { - clientMsg = sm.CreateHeader(sm.Diversifier) - }, - false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - clientState := sm.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) Test_UpdateStateOnMisbehaviour() { - // test singlesig and multisig public keys - for _, sm := 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 := sm.ClientState() - - tc.malleate() - - clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, nil) - - if tc.expPass { - clientStateBz := suite.store.Get(host.ClientStateKey()) - suite.Require().NotEmpty(clientStateBz) - - newClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, clientStateBz) - - suite.Require().True(newClientState.(*solomachine.ClientState).IsFrozen) - } - }) - } - } -} From c58bf3d87b7c426192dda7dd3fbec1f7337715f4 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Tue, 26 Mar 2024 12:13:12 +0200 Subject: [PATCH 06/10] nits: test names --- .../light_client_module_test.go | 154 +++++++++--------- 1 file changed, 78 insertions(+), 76 deletions(-) diff --git a/modules/light-clients/06-solomachine/light_client_module_test.go b/modules/light-clients/06-solomachine/light_client_module_test.go index 2d155d1b18a..ee41ef7f0b9 100644 --- a/modules/light-clients/06-solomachine/light_client_module_test.go +++ b/modules/light-clients/06-solomachine/light_client_module_test.go @@ -63,41 +63,41 @@ func (suite *SoloMachineTestSuite) TestStatus() { suite.Run(tc.name, func() { clientID = suite.solomachine.ClientID - clientState = suite.solomachine.ClientState() lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) suite.Require().True(found) - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, suite.solomachine.ClientState()) tc.malleate() status := lightClientModule.Status(suite.chainA.GetContext(), clientID) - suite.Require().Equal(tc.expStatus, status) }) } } func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { - clientID := suite.solomachine.ClientID + var clientID string height := clienttypes.NewHeight(0, suite.solomachine.ClientState().Sequence) testCases := []struct { name string - clientID string + malleate func() expValue uint64 expErr error }{ { - "get timestamp at height exists", - clientID, + "success: get timestamp at height exists", + func() {}, suite.solomachine.ClientState().ConsensusState.Timestamp, nil, }, { - "client not found", - unusedSmClientID, + "failure: client not found", + func() { + clientID = unusedSmClientID + }, 0, clienttypes.ErrClientNotFound, }, @@ -115,7 +115,9 @@ func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) - ts, err := lightClientModule.TimestampAtHeight(suite.chainA.GetContext(), tc.clientID, height) + tc.malleate() + + ts, err := lightClientModule.TimestampAtHeight(suite.chainA.GetContext(), clientID, height) suite.Require().Equal(tc.expValue, ts) suite.Require().ErrorIs(err, tc.expErr) @@ -136,37 +138,37 @@ func (suite *SoloMachineTestSuite) TestInitialize() { expErr error }{ { - "valid consensus state", + "success: valid consensus state", sm.ConsensusState(), sm.ClientState(), nil, }, { - "nil consensus state", + "failure: nil consensus state", nil, sm.ClientState(), clienttypes.ErrInvalidConsensus, }, { - "invalid consensus state: Tendermint consensus state", + "failure: invalid consensus state: Tendermint consensus state", &ibctm.ConsensusState{}, sm.ClientState(), fmt.Errorf("proto: wrong wireType = 0 for field TypeUrl"), }, { - "invalid consensus state: consensus state does not match consensus state in client", + "failure: invalid consensus state: consensus state does not match consensus state in client", malleatedConsensus, sm.ClientState(), clienttypes.ErrInvalidConsensus, }, { - "invalid client state: sequence is zero", + "failure: invalid client state: sequence is zero", sm.ConsensusState(), solomachine.NewClientState(0, sm.ConsensusState()), clienttypes.ErrInvalidClient, }, { - "invalid client state: Tendermint client state", + "failure: invalid client state: Tendermint client state", sm.ConsensusState(), &ibctm.ClientState{}, fmt.Errorf("proto: wrong wireType = 2 for field IsFrozen"), @@ -519,21 +521,21 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { nil, }, { - "cannot find subject client state", + "failure: cannot find subject client state", func() { clientID = unusedSmClientID }, clienttypes.ErrClientNotFound, }, { - "invalid path type - empty", + "failure: invalid path type - empty", func() { path = ibcmock.KeyPath{} }, ibcerrors.ErrInvalidType, }, { - "malformed proof fails to unmarshal", + "failure: malformed proof fails to unmarshal", func() { path = sm.GetClientStatePath(counterpartyClientIdentifier) proof = []byte("invalid proof") @@ -541,7 +543,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { fmt.Errorf("failed to unmarshal proof into type"), }, { - "consensus state timestamp is greater than signature", + "failure: consensus state timestamp is greater than signature", func() { consensusState := &solomachine.ConsensusState{ Timestamp: sm.Time + 1, @@ -554,7 +556,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { fmt.Errorf("the consensus state timestamp is greater than the signature timestamp (11 >= 10): %s", solomachine.ErrInvalidProof), }, { - "signature data is nil", + "failure: signature data is nil", func() { signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: nil, @@ -567,7 +569,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { fmt.Errorf("signature data cannot be empty: %s", solomachine.ErrInvalidProof), }, { - "consensus state public key is nil", + "failure: consensus state public key is nil", func() { clientState.ConsensusState.PublicKey = nil suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) @@ -575,7 +577,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { fmt.Errorf("consensus state PublicKey cannot be nil: %s", clienttypes.ErrInvalidConsensus), }, { - "malformed signature data fails to unmarshal", + "failure: malformed signature data fails to unmarshal", func() { signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: []byte("invalid signature data"), @@ -588,21 +590,21 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { fmt.Errorf("failed to unmarshal proof into type"), }, { - "proof is nil", + "failure: proof is nil", func() { proof = nil }, fmt.Errorf("proof cannot be empty: %s", solomachine.ErrInvalidProof), }, { - "proof verification failed", + "failure: proof verification failed", func() { signBytes.Data = []byte("invalid membership data value") }, solomachine.ErrSignatureVerificationFailed, }, { - "empty path", + "failure: empty path", func() { path = commitmenttypes.MerklePath{} }, @@ -738,21 +740,21 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { nil, }, { - "cannot find subject client state", + "failure: cannot find subject client state", func() { clientID = unusedSmClientID }, clienttypes.ErrClientNotFound, }, { - "invalid path type", + "failure: invalid path type", func() { path = ibcmock.KeyPath{} }, ibcerrors.ErrInvalidType, }, { - "malformed proof fails to unmarshal", + "failure: malformed proof fails to unmarshal", func() { path = sm.GetClientStatePath(counterpartyClientIdentifier) proof = []byte("invalid proof") @@ -760,7 +762,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { fmt.Errorf("failed to unmarshal proof into type"), }, { - "consensus state timestamp is greater than signature", + "failure: consensus state timestamp is greater than signature", func() { consensusState := &solomachine.ConsensusState{ Timestamp: sm.Time + 1, @@ -773,7 +775,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { fmt.Errorf("the consensus state timestamp is greater than the signature timestamp (11 >= 10): %s", solomachine.ErrInvalidProof), }, { - "signature data is nil", + "failure: signature data is nil", func() { signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: nil, @@ -786,7 +788,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { fmt.Errorf("signature data cannot be empty: %s", solomachine.ErrInvalidProof), }, { - "consensus state public key is nil", + "failure: consensus state public key is nil", func() { clientState.ConsensusState.PublicKey = nil suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) @@ -794,7 +796,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { fmt.Errorf("consensus state PublicKey cannot be nil: %s", clienttypes.ErrInvalidConsensus), }, { - "malformed signature data fails to unmarshal", + "failure: malformed signature data fails to unmarshal", func() { signatureDoc := &solomachine.TimestampedSignatureData{ SignatureData: []byte("invalid signature data"), @@ -807,14 +809,14 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { fmt.Errorf("failed to unmarshal proof into type"), }, { - "proof is nil", + "failure: proof is nil", func() { proof = nil }, fmt.Errorf("proof cannot be empty: %s", solomachine.ErrInvalidProof), }, { - "proof verification failed", + "failure: proof verification failed", func() { signBytes.Data = []byte("invalid non-membership data value") @@ -839,6 +841,8 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { tc := tc suite.Run(tc.name, func() { + suite.SetupTest() + clientState = sm.ClientState() clientID = sm.ClientID @@ -923,28 +927,28 @@ func (suite *SoloMachineTestSuite) TestRecoverClient() { nil, }, { - "cannot parse malformed substitute client ID", + "failure: cannot parse malformed substitute client ID", func() { substituteClientID = ibctesting.InvalidID }, host.ErrInvalidID, }, { - "substitute client ID does not contain 06-solomachine prefix", + "failure: substitute client ID does not contain 06-solomachine prefix", func() { substituteClientID = wasmClientID }, clienttypes.ErrInvalidClientType, }, { - "cannot find subject client state", + "failure: cannot find subject client state", func() { subjectClientID = smClientID }, clienttypes.ErrClientNotFound, }, { - "cannot find substitute client state", + "failure: cannot find substitute client state", func() { substituteClientID = smClientID }, @@ -956,7 +960,7 @@ func (suite *SoloMachineTestSuite) TestRecoverClient() { tc := tc suite.Run(tc.name, func() { suite.SetupTest() // reset - cdc := suite.chainA.Codec + ctx := suite.chainA.GetContext() subjectClientID = suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(ctx, exported.Solomachine) @@ -970,7 +974,7 @@ func (suite *SoloMachineTestSuite) TestRecoverClient() { clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, substituteClientID) clientStore.Get(host.ClientStateKey()) - bz := clienttypes.MustMarshalClientState(cdc, substituteClientState) + bz := clienttypes.MustMarshalClientState(suite.chainA.Codec, substituteClientState) clientStore.Set(host.ClientStateKey(), bz) subjectClientState.IsFrozen = true @@ -990,7 +994,7 @@ func (suite *SoloMachineTestSuite) TestRecoverClient() { // assert that status of subject client is now Active clientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, subjectClientID) bz = clientStore.Get(host.ClientStateKey()) - smClientState := clienttypes.MustUnmarshalClientState(cdc, bz).(*solomachine.ClientState) + smClientState := clienttypes.MustUnmarshalClientState(suite.chainA.Codec, bz).(*solomachine.ClientState) suite.Require().Equal(substituteClientState.ConsensusState, smClientState.ConsensusState) suite.Require().Equal(substituteClientState.Sequence, smClientState.Sequence) @@ -1024,7 +1028,7 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { nil, }, { - "invalid type misbehaviour", + "failure: invalid type misbehaviour", func() { clientState = sm.ClientState() clientMsg = sm.CreateMisbehaviour() @@ -1033,7 +1037,7 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { fmt.Errorf("unsupported ClientMessage: %T", sm.CreateMisbehaviour()), }, { - "cannot find subject client state", + "failure: cannot find subject client state", func() { clientID = unusedSmClientID }, @@ -1046,6 +1050,7 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { suite.Run(tc.name, func() { suite.SetupTest() + clientID = sm.ClientID clientState = sm.ClientState() clientMsg = sm.CreateHeader(sm.Diversifier) @@ -1112,7 +1117,7 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { nil, }, { - "normal header returns false", + "failure: normal header returns false", func() { clientMsg = sm.CreateHeader(sm.Diversifier) }, @@ -1120,7 +1125,7 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { nil, }, { - "cannot find subject client state", + "failure: cannot find subject client state", func() { clientID = unusedSmClientID }, @@ -1179,7 +1184,7 @@ func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { nil, }, { - "cannot find subject client state", + "failure: cannot find subject client state", func() { clientID = unusedSmClientID }, @@ -1241,35 +1246,35 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { expErr error }{ { - "successful header", + "success: successful header", func() { clientMsg = sm.CreateHeader(sm.Diversifier) }, nil, }, { - "successful header with new diversifier", + "success: successful header with new diversifier", func() { clientMsg = sm.CreateHeader(sm.Diversifier + "0") }, nil, }, { - "successful misbehaviour", + "success: successful misbehaviour", func() { clientMsg = sm.CreateMisbehaviour() }, nil, }, { - "invalid client message type", + "failure: invalid client message type", func() { clientMsg = &ibctm.Header{} }, clienttypes.ErrInvalidClientType, }, { - "invalid header Signature", + "failure: invalid header Signature", func() { h := sm.CreateHeader(sm.Diversifier) h.Signature = suite.GetInvalidProof() @@ -1277,7 +1282,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { }, fmt.Errorf("proto: wrong wireType = 0 for field Multi"), }, { - "invalid timestamp in header", + "failure: invalid timestamp in header", func() { h := sm.CreateHeader(sm.Diversifier) h.Timestamp-- @@ -1285,7 +1290,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { }, clienttypes.ErrInvalidHeader, }, { - "signature uses wrong sequence", + "failure: signature uses wrong sequence", func() { sm.Sequence++ clientMsg = sm.CreateHeader(sm.Diversifier) @@ -1332,7 +1337,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { solomachine.ErrSignatureVerificationFailed, }, { - "signature signs over old pubkey", + "failure: signature signs over old pubkey", func() { // store in temp before assigning to interface type cs := sm.ClientState() @@ -1353,7 +1358,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { clienttypes.ErrInvalidHeader, }, { - "consensus state public key is nil - header", + "failure: consensus state public key is nil - header", func() { clientState.ConsensusState.PublicKey = nil clientMsg = sm.CreateHeader(sm.Diversifier) @@ -1363,7 +1368,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { clienttypes.ErrInvalidHeader, }, { - "cannot find subject client state", + "failure: cannot find subject client state", func() { clientID = unusedSmClientID }, @@ -1383,7 +1388,6 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, sm.ClientState()) - // setup test tc.malleate() err := lightClientModule.VerifyClientMessage(suite.chainA.GetContext(), clientID, clientMsg) @@ -1415,14 +1419,14 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { expErr error }{ { - "successful misbehaviour", + "success: successful misbehaviour", func() { clientMsg = sm.CreateMisbehaviour() }, nil, }, { - "old misbehaviour is successful (timestamp is less than current consensus state)", + "success: old misbehaviour is successful (timestamp is less than current consensus state)", func() { clientState = sm.ClientState() sm.Time -= 5 @@ -1430,14 +1434,14 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { }, nil, }, { - "invalid client message type", + "failure: invalid client message type", func() { clientMsg = &ibctm.Header{} }, clienttypes.ErrInvalidClientType, }, { - "consensus state pubkey is nil", + "failure: consensus state pubkey is nil", func() { clientState.ConsensusState.PublicKey = nil clientMsg = sm.CreateMisbehaviour() @@ -1446,7 +1450,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { clienttypes.ErrInvalidConsensus, }, { - "invalid SignatureOne SignatureData", + "failure: invalid SignatureOne SignatureData", func() { m := sm.CreateMisbehaviour() @@ -1455,7 +1459,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { }, fmt.Errorf("proto: wrong wireType = 0 for field Multi"), }, { - "invalid SignatureTwo SignatureData", + "failure: invalid SignatureTwo SignatureData", func() { m := sm.CreateMisbehaviour() @@ -1464,7 +1468,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { }, fmt.Errorf("proto: wrong wireType = 0 for field Multi"), }, { - "invalid SignatureOne timestamp", + "failure: invalid SignatureOne timestamp", func() { m := sm.CreateMisbehaviour() @@ -1473,7 +1477,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { }, solomachine.ErrSignatureVerificationFailed, }, { - "invalid SignatureTwo timestamp", + "failure: invalid SignatureTwo timestamp", func() { m := sm.CreateMisbehaviour() @@ -1482,7 +1486,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { }, solomachine.ErrSignatureVerificationFailed, }, { - "invalid first signature data", + "failure: invalid first signature data", func() { // store in temp before assigning to interface type m := sm.CreateMisbehaviour() @@ -1508,7 +1512,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { solomachine.ErrSignatureVerificationFailed, }, { - "invalid second signature data", + "failure: invalid second signature data", func() { // store in temp before assigning to interface type m := sm.CreateMisbehaviour() @@ -1534,7 +1538,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { solomachine.ErrSignatureVerificationFailed, }, { - "wrong pubkey generates first signature", + "failure: wrong pubkey generates first signature", func() { badMisbehaviour := sm.CreateMisbehaviour() @@ -1548,7 +1552,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { }, solomachine.ErrSignatureVerificationFailed, }, { - "wrong pubkey generates second signature", + "failure: wrong pubkey generates second signature", func() { badMisbehaviour := sm.CreateMisbehaviour() @@ -1562,7 +1566,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { }, solomachine.ErrSignatureVerificationFailed, }, { - "signatures sign over different sequence", + "failure: signatures sign over different sequence", func() { // store in temp before assigning to interface type m := sm.CreateMisbehaviour() @@ -1610,7 +1614,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { solomachine.ErrSignatureVerificationFailed, }, { - "cannot find subject client state", + "failure: cannot find subject client state", func() { clientID = unusedSmClientID }, @@ -1630,7 +1634,6 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, sm.ClientState()) - // setup test tc.malleate() err := lightClientModule.VerifyClientMessage(suite.chainA.GetContext(), clientID, clientMsg) @@ -1666,13 +1669,12 @@ func (suite *SoloMachineTestSuite) TestLatestHeight() { }{ { "success", - func() { - }, + func() {}, // Default as returned by solomachine.ClientState() clienttypes.NewHeight(0, 1), }, { - "cannot find substitute client state", + "failure: cannot find substitute client state", func() { clientID = unusedSmClientID }, From 236f723f42c2e84548d3bf413817428aee94f970 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Tue, 26 Mar 2024 13:14:31 +0200 Subject: [PATCH 07/10] Apply suggestions from code review Co-authored-by: Carlos Rodriguez --- .../light_client_module_test.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/light-clients/06-solomachine/light_client_module_test.go b/modules/light-clients/06-solomachine/light_client_module_test.go index ee41ef7f0b9..51d9772e708 100644 --- a/modules/light-clients/06-solomachine/light_client_module_test.go +++ b/modules/light-clients/06-solomachine/light_client_module_test.go @@ -50,7 +50,7 @@ func (suite *SoloMachineTestSuite) TestStatus() { exported.Frozen, }, { - "cannot find subject client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -94,7 +94,7 @@ func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { nil, }, { - "failure: client not found", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -521,7 +521,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { nil, }, { - "failure: cannot find subject client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -740,7 +740,7 @@ func (suite *SoloMachineTestSuite) TestVerifyNonMembership() { nil, }, { - "failure: cannot find subject client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -1037,7 +1037,7 @@ func (suite *SoloMachineTestSuite) TestUpdateState() { fmt.Errorf("unsupported ClientMessage: %T", sm.CreateMisbehaviour()), }, { - "failure: cannot find subject client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -1125,7 +1125,7 @@ func (suite *SoloMachineTestSuite) TestCheckForMisbehaviour() { nil, }, { - "failure: cannot find subject client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -1184,7 +1184,7 @@ func (suite *SoloMachineTestSuite) TestUpdateStateOnMisbehaviour() { nil, }, { - "failure: cannot find subject client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -1368,7 +1368,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageHeader() { clienttypes.ErrInvalidHeader, }, { - "failure: cannot find subject client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -1614,7 +1614,7 @@ func (suite *SoloMachineTestSuite) TestVerifyClientMessageMisbehaviour() { solomachine.ErrSignatureVerificationFailed, }, { - "failure: cannot find subject client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, @@ -1674,7 +1674,7 @@ func (suite *SoloMachineTestSuite) TestLatestHeight() { clienttypes.NewHeight(0, 1), }, { - "failure: cannot find substitute client state", + "failure: cannot find client state", func() { clientID = unusedSmClientID }, From 6c97bb107cffad264a6357f24573ea3cb9577c42 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Tue, 26 Mar 2024 13:17:45 +0200 Subject: [PATCH 08/10] Update modules/light-clients/06-solomachine/solomachine_test.go Co-authored-by: Carlos Rodriguez --- modules/light-clients/06-solomachine/solomachine_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/light-clients/06-solomachine/solomachine_test.go b/modules/light-clients/06-solomachine/solomachine_test.go index bd2a134e9ef..3b43200b4b7 100644 --- a/modules/light-clients/06-solomachine/solomachine_test.go +++ b/modules/light-clients/06-solomachine/solomachine_test.go @@ -45,8 +45,8 @@ func (suite *SoloMachineTestSuite) SetupTest() { suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) - suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 1) - suite.solomachineMulti = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-2", "testing", 4) + suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 1) + suite.solomachineMulti = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-1", "testing", 4) suite.store = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), exported.Solomachine) } From ece3a6de8cd9a123fe0c91c680ea1d08824104a7 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Tue, 26 Mar 2024 13:20:20 +0200 Subject: [PATCH 09/10] chore: rm one of the consts --- .../light-clients/06-solomachine/light_client_module_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/light-clients/06-solomachine/light_client_module_test.go b/modules/light-clients/06-solomachine/light_client_module_test.go index 51d9772e708..47c6369021c 100644 --- a/modules/light-clients/06-solomachine/light_client_module_test.go +++ b/modules/light-clients/06-solomachine/light_client_module_test.go @@ -19,7 +19,6 @@ import ( ) const ( - smClientID = "06-solomachine-100" unusedSmClientID = "06-solomachine-999" wasmClientID = "08-wasm-0" ) @@ -943,14 +942,14 @@ func (suite *SoloMachineTestSuite) TestRecoverClient() { { "failure: cannot find subject client state", func() { - subjectClientID = smClientID + subjectClientID = unusedSmClientID }, clienttypes.ErrClientNotFound, }, { "failure: cannot find substitute client state", func() { - substituteClientID = smClientID + substituteClientID = unusedSmClientID }, clienttypes.ErrClientNotFound, }, From 048debd195e76fa76e989418bdf7dc8732051910 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Wed, 3 Apr 2024 18:43:06 +0300 Subject: [PATCH 10/10] review: address feedback. --- .../06-solomachine/client_state_test.go | 2 +- .../light_client_module_test.go | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/modules/light-clients/06-solomachine/client_state_test.go b/modules/light-clients/06-solomachine/client_state_test.go index 5c6b6b973b7..66f025e2786 100644 --- a/modules/light-clients/06-solomachine/client_state_test.go +++ b/modules/light-clients/06-solomachine/client_state_test.go @@ -14,7 +14,7 @@ const ( testPortID = "testportid" ) -func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { +func (suite *SoloMachineTestSuite) TestClientStateValidate() { // test singlesig and multisig public keys for _, sm := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { diff --git a/modules/light-clients/06-solomachine/light_client_module_test.go b/modules/light-clients/06-solomachine/light_client_module_test.go index 47c6369021c..2c3777e01e7 100644 --- a/modules/light-clients/06-solomachine/light_client_module_test.go +++ b/modules/light-clients/06-solomachine/light_client_module_test.go @@ -77,8 +77,10 @@ func (suite *SoloMachineTestSuite) TestStatus() { } func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { - var clientID string - height := clienttypes.NewHeight(0, suite.solomachine.ClientState().Sequence) + var ( + clientID string + height exported.Height + ) testCases := []struct { name string @@ -92,6 +94,15 @@ func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { suite.solomachine.ClientState().ConsensusState.Timestamp, nil, }, + { + "success: modified height", + func() { + height = clienttypes.ZeroHeight() + }, + // Timestamp should be the same. + suite.solomachine.ClientState().ConsensusState.Timestamp, + nil, + }, { "failure: cannot find client state", func() { @@ -108,6 +119,7 @@ func (suite *SoloMachineTestSuite) TestGetTimestampAtHeight() { suite.Run(tc.name, func() { clientID = suite.solomachine.ClientID clientState := suite.solomachine.ClientState() + height = clienttypes.NewHeight(0, suite.solomachine.ClientState().Sequence) lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) suite.Require().True(found) @@ -231,7 +243,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { "success: client state verification", func() { clientState = sm.ClientState() - clientStateBz, err := suite.chainA.Codec.Marshal(clientState) + clientStateBz, err := suite.chainA.Codec.MarshalInterface(clientState) suite.Require().NoError(err) path = sm.GetClientStatePath(counterpartyClientIdentifier) @@ -267,7 +279,7 @@ func (suite *SoloMachineTestSuite) TestVerifyMembership() { func() { clientState = sm.ClientState() consensusState := clientState.ConsensusState - consensusStateBz, err := suite.chainA.Codec.Marshal(consensusState) + consensusStateBz, err := suite.chainA.Codec.MarshalInterface(consensusState) suite.Require().NoError(err) path = sm.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 1))