From 5a9a52fe71d352cbad4c3377da7e078244aa7b0e Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Thu, 21 Mar 2024 10:54:48 +0200 Subject: [PATCH] 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: