From e50f6deaa6dd9de8a5a282b7fe9591b2395a494e Mon Sep 17 00:00:00 2001 From: Daniel T <30197399+danwt@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:12:17 +0000 Subject: [PATCH] fix(rollapp): fix expensive iteration when setting canonical light client (#1575) --- .gitignore | 3 +- ibctesting/light_client_test.go | 128 +++- ibctesting/utils_test.go | 6 + .../dymension/lightclient/events.proto | 6 + .../dymension/lightclient/tx.proto | 27 + testutil/keeper/lightclient.go | 4 + x/lightclient/ante/ibc_msgs_test.go | 5 + x/lightclient/client/cli/query.go | 4 + x/lightclient/keeper/canonical_client.go | 102 +-- x/lightclient/keeper/hook_listener.go | 4 - x/lightclient/keeper/hook_listener_test.go | 25 +- x/lightclient/keeper/msg_server.go | 29 + x/lightclient/module.go | 3 + x/lightclient/types/codec.go | 27 + x/lightclient/types/events.pb.go | 237 ++++++- x/lightclient/types/expected_keepers.go | 2 +- x/lightclient/types/tx.go | 56 ++ x/lightclient/types/tx.pb.go | 588 ++++++++++++++++++ x/rollapp/keeper/latest_state_info_index.go | 11 + 19 files changed, 1203 insertions(+), 64 deletions(-) create mode 100644 proto/dymensionxyz/dymension/lightclient/tx.proto create mode 100644 x/lightclient/keeper/msg_server.go create mode 100644 x/lightclient/types/tx.go create mode 100644 x/lightclient/types/tx.pb.go diff --git a/.gitignore b/.gitignore index 9cedeba58..e9ad240dc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ build/ proposal.json .go-version **/testdata/rapid/ -**/__debug_bin* \ No newline at end of file +**/__debug_bin* +.aider* \ No newline at end of file diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go index 1b129b3a1..ef4c0164c 100644 --- a/ibctesting/light_client_test.go +++ b/ibctesting/light_client_test.go @@ -1,6 +1,7 @@ package ibctesting_test import ( + "slices" "testing" errorsmod "cosmossdk.io/errors" @@ -8,9 +9,10 @@ import ( clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "github.com/cosmos/ibc-go/v7/testing/simapp" - + lightclientkeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/dymensionxyz/sdk-utils/utils/utest" ibctesting "github.com/cosmos/ibc-go/v7/testing" "github.com/stretchr/testify/suite" @@ -41,13 +43,115 @@ func (s *lightClientSuite) TestSetCanonicalClient_FailsTrustRequirements() { s.coordinator.SetupClients(s.path) // Update rollapp state - this will trigger the check for prospective canonical client - currentRollappBlockHeight := uint64(s.rollappChain().App.LastBlockHeight()) - s.updateRollappState(currentRollappBlockHeight) + _, err := s.lightclientMsgServer().SetCanonicalClient(s.hubCtx(), + &types.MsgSetCanonicalClient{ + Signer: s.hubChain().SenderAccount.GetAddress().String(), ClientId: s.path.EndpointA.ClientID, + }) + s.Require().Error(err) _, found := s.hubApp().LightClientKeeper.GetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID) s.False(found) } +func (s *lightClientSuite) TestSetCanonicalClient_ParamsMismatch() { + s.createRollapp(false, nil) + s.registerSequencer() + // create a custom tm client which matches the trust requirements of a canonical client + endpointA := ibctesting.NewEndpoint(s.hubChain(), &canonicalClientConfig, ibctesting.NewConnectionConfig(), ibctesting.NewChannelConfig()) + endpointB := ibctesting.NewEndpoint(s.rollappChain(), ibctesting.NewTendermintConfig(), ibctesting.NewConnectionConfig(), ibctesting.NewChannelConfig()) + endpointA.Counterparty = endpointB + endpointB.Counterparty = endpointA + s.path = &ibctesting.Path{EndpointA: endpointA, EndpointB: endpointB} + + currentHeader := s.rollappChain().CurrentHeader + startHeight := uint64(currentHeader.Height) + bd := rollapptypes.BlockDescriptor{Height: startHeight, StateRoot: currentHeader.AppHash, Timestamp: currentHeader.Time} + + // Creating the tm client - this will take us to the next block + s.NoError(s.path.EndpointA.CreateClient()) + + currentHeader = s.rollappChain().CurrentHeader + bdNext := rollapptypes.BlockDescriptor{Height: uint64(currentHeader.Height), StateRoot: currentHeader.AppHash, Timestamp: currentHeader.Time} + + // Update the rollapp state - this will trigger the check for prospective canonical client + msgUpdateState := rollapptypes.NewMsgUpdateState( + s.hubChain().SenderAccount.GetAddress().String(), + rollappChainID(), + "mock-da-path", + startHeight, + 2, + &rollapptypes.BlockDescriptors{BD: []rollapptypes.BlockDescriptor{bd, bdNext}}, + ) + _, err := s.rollappMsgServer().UpdateState(s.hubCtx(), msgUpdateState) + s.Require().NoError(err) + + // now break the params + c, _ := s.hubChain().App.GetIBCKeeper().ClientKeeper.GetClientState(s.hubCtx(), s.path.EndpointA.ClientID) + tmClient, _ := c.(*ibctm.ClientState) + tmClient.MaxClockDrift = 0 // wrong + s.hubChain().App.GetIBCKeeper().ClientKeeper.SetClientState(s.hubCtx(), s.path.EndpointA.ClientID, tmClient) + + // Update rollapp state - this will trigger the check for prospective canonical client + setCanonMsg := &types.MsgSetCanonicalClient{ + Signer: s.hubChain().SenderAccount.GetAddress().String(), ClientId: s.path.EndpointA.ClientID, + } + _, err = s.lightclientMsgServer().SetCanonicalClient(s.hubCtx(), setCanonMsg) + utest.IsErr(s.Require(), err, lightclientkeeper.ErrParamsMismatch) + + _, found := s.hubApp().LightClientKeeper.GetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID) + s.False(found) +} + +func (s *lightClientSuite) TestSetCanonicalClient_ConsStateMismatch() { + s.createRollapp(false, nil) + s.registerSequencer() + // create a custom tm client which matches the trust requirements of a canonical client + endpointA := ibctesting.NewEndpoint(s.hubChain(), &canonicalClientConfig, ibctesting.NewConnectionConfig(), ibctesting.NewChannelConfig()) + endpointB := ibctesting.NewEndpoint(s.rollappChain(), ibctesting.NewTendermintConfig(), ibctesting.NewConnectionConfig(), ibctesting.NewChannelConfig()) + endpointA.Counterparty = endpointB + endpointB.Counterparty = endpointA + s.path = &ibctesting.Path{EndpointA: endpointA, EndpointB: endpointB} + + currentHeader := s.rollappChain().CurrentHeader + startHeight := uint64(currentHeader.Height) + bd := rollapptypes.BlockDescriptor{Height: startHeight, StateRoot: currentHeader.AppHash, Timestamp: currentHeader.Time} + + // Creating the tm client - this will take us to the next block + s.NoError(s.path.EndpointA.CreateClient()) + + currentHeader = s.rollappChain().CurrentHeader + bdNext := rollapptypes.BlockDescriptor{Height: uint64(currentHeader.Height), StateRoot: currentHeader.AppHash, Timestamp: currentHeader.Time} + + // It's too early, it should fail + setCanonMsg := &types.MsgSetCanonicalClient{ + Signer: s.hubChain().SenderAccount.GetAddress().String(), ClientId: s.path.EndpointA.ClientID, + } + _, err := s.lightclientMsgServer().SetCanonicalClient(s.hubCtx(), setCanonMsg) + s.Require().Error(err) + + // Update the rollapp state - this will trigger the check for prospective canonical client + msgUpdateState := rollapptypes.NewMsgUpdateState( + s.hubChain().SenderAccount.GetAddress().String(), + rollappChainID(), + "mock-da-path", + startHeight, + 2, + &rollapptypes.BlockDescriptors{BD: []rollapptypes.BlockDescriptor{bd, bdNext}}, + ) + _, err = s.rollappMsgServer().UpdateState(s.hubCtx(), msgUpdateState) + s.Require().NoError(err) + + c, _ := s.hubChain().App.GetIBCKeeper().ClientKeeper.GetClientState(s.hubCtx(), s.path.EndpointA.ClientID) + tmClient, _ := c.(*ibctm.ClientState) + cs, _ := s.hubChain().App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(s.hubCtx(), s.path.EndpointA.ClientID, tmClient.GetLatestHeight()) + tmConsState, _ := cs.(*ibctm.ConsensusState) + slices.Reverse(tmConsState.NextValidatorsHash) // make it wrong + s.hubChain().App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(s.hubCtx(), s.path.EndpointA.ClientID, tmClient.GetLatestHeight(), tmConsState) + + _, err = s.lightclientMsgServer().SetCanonicalClient(s.hubCtx(), setCanonMsg) + utest.IsErr(s.Require(), err, lightclientkeeper.ErrMismatch) +} + func (s *lightClientSuite) TestSetCanonicalClient_FailsIncompatibleState() { s.createRollapp(false, nil) s.registerSequencer() @@ -66,6 +170,12 @@ func (s *lightClientSuite) TestSetCanonicalClient_FailsIncompatibleState() { currentRollappBlockHeight := uint64(s.rollappChain().App.LastBlockHeight()) s.updateRollappState(currentRollappBlockHeight) + _, err := s.lightclientMsgServer().SetCanonicalClient(s.hubCtx(), + &types.MsgSetCanonicalClient{ + Signer: s.hubChain().SenderAccount.GetAddress().String(), ClientId: s.path.EndpointA.ClientID, + }) + s.Require().Error(err) + _, found := s.hubApp().LightClientKeeper.GetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID) s.False(found) } @@ -90,6 +200,13 @@ func (s *lightClientSuite) TestSetCanonicalClient_Succeeds() { currentHeader = s.rollappChain().CurrentHeader bdNext := rollapptypes.BlockDescriptor{Height: uint64(currentHeader.Height), StateRoot: currentHeader.AppHash, Timestamp: currentHeader.Time} + // It's too early, it should fail + setCanonMsg := &types.MsgSetCanonicalClient{ + Signer: s.hubChain().SenderAccount.GetAddress().String(), ClientId: s.path.EndpointA.ClientID, + } + _, err := s.lightclientMsgServer().SetCanonicalClient(s.hubCtx(), setCanonMsg) + s.Require().Error(err) + // Update the rollapp state - this will trigger the check for prospective canonical client msgUpdateState := rollapptypes.NewMsgUpdateState( s.hubChain().SenderAccount.GetAddress().String(), @@ -99,7 +216,10 @@ func (s *lightClientSuite) TestSetCanonicalClient_Succeeds() { 2, &rollapptypes.BlockDescriptors{BD: []rollapptypes.BlockDescriptor{bd, bdNext}}, ) - _, err := s.rollappMsgServer().UpdateState(s.hubCtx(), msgUpdateState) + _, err = s.rollappMsgServer().UpdateState(s.hubCtx(), msgUpdateState) + s.Require().NoError(err) + + _, err = s.lightclientMsgServer().SetCanonicalClient(s.hubCtx(), setCanonMsg) s.Require().NoError(err) canonClientID, found := s.hubApp().LightClientKeeper.GetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID) diff --git a/ibctesting/utils_test.go b/ibctesting/utils_test.go index a5f6f7a5a..afca541de 100644 --- a/ibctesting/utils_test.go +++ b/ibctesting/utils_test.go @@ -23,6 +23,8 @@ import ( ibctesting "github.com/cosmos/ibc-go/v7/testing" "github.com/cosmos/ibc-go/v7/testing/mock" "github.com/cosmos/ibc-go/v7/testing/simapp" + lightclientkeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + lightclienttypes "github.com/dymensionxyz/dymension/v3/x/lightclient/types" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -109,6 +111,10 @@ func (s *utilSuite) rollappMsgServer() rollapptypes.MsgServer { return rollappkeeper.NewMsgServerImpl(s.hubApp().RollappKeeper) } +func (s *utilSuite) lightclientMsgServer() lightclienttypes.MsgServer { + return lightclientkeeper.NewMsgServerImpl(&s.hubApp().LightClientKeeper) +} + // SetupTest creates a coordinator with 2 test chains. func (s *utilSuite) SetupTest() { // this is used as default when creating blocks. diff --git a/proto/dymensionxyz/dymension/lightclient/events.proto b/proto/dymensionxyz/dymension/lightclient/events.proto index 6ed67ef3e..726bde1be 100644 --- a/proto/dymensionxyz/dymension/lightclient/events.proto +++ b/proto/dymensionxyz/dymension/lightclient/events.proto @@ -10,3 +10,9 @@ message EventSetCanonicalChannel { string rollapp_id = 1; string channel_id = 2; } + +// When the canonical client of the rollapp is set +message EventSetCanonicalClient { + string rollapp_id = 1; + string client_id = 2; +} \ No newline at end of file diff --git a/proto/dymensionxyz/dymension/lightclient/tx.proto b/proto/dymensionxyz/dymension/lightclient/tx.proto new file mode 100644 index 000000000..e1cd004e4 --- /dev/null +++ b/proto/dymensionxyz/dymension/lightclient/tx.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package dymensionxyz.dymension.lightclient; + +import "cosmos/msg/v1/msg.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/lightclient/types"; + +service Msg { + rpc SetCanonicalClient(MsgSetCanonicalClient) returns (MsgSetCanonicalClientResponse); +} + +// verify a client state and its consensus states against the rollapp +// if it matches, set the client as the canonical client for the rollapp +// NOTE: this definition is also copied to the relayer code +message MsgSetCanonicalClient { + option (cosmos.msg.v1.signer) = "signer"; + string signer = 1; + // id of ibc client state + string client_id = 2; +} + +message MsgSetCanonicalClientResponse { +} + diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 8a686ca12..7a67c1e3b 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -182,6 +182,10 @@ func NewMockSequencerKeeper(sequencers map[string]*sequencertypes.Sequencer) *Mo type MockRollappKeeper struct{} +func (m *MockRollappKeeper) GetLatestHeight(ctx sdk.Context, rollappId string) (uint64, bool) { + panic("implement me") +} + // GetLatestStateInfo implements types.RollappKeeperExpected. func (m *MockRollappKeeper) GetLatestStateInfo(ctx sdk.Context, rollappId string) (rollapptypes.StateInfo, bool) { panic("unimplemented") diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index 6eeed99d6..460ee0b0f 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -17,6 +17,11 @@ type MockRollappKeeper struct { stateInfos map[string]map[uint64]rollapptypes.StateInfo } +func (m *MockRollappKeeper) GetLatestHeight(ctx sdk.Context, rollappId string) (uint64, bool) { + // TODO implement me + panic("implement me") +} + // GetLatestStateInfo implements types.RollappKeeperExpected. func (m *MockRollappKeeper) GetLatestStateInfo(ctx sdk.Context, rollappId string) (rollapptypes.StateInfo, bool) { return rollapptypes.StateInfo{}, false diff --git a/x/lightclient/client/cli/query.go b/x/lightclient/client/cli/query.go index 3ee78610d..b0ec5d1f8 100644 --- a/x/lightclient/client/cli/query.go +++ b/x/lightclient/client/cli/query.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/version" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" "github.com/spf13/cobra" @@ -65,6 +66,7 @@ The other fields can take any value`, }, } + flags.AddQueryFlagsToCmd(cmd) return cmd } @@ -95,5 +97,7 @@ func CmdGetLightClient() *cobra.Command { }, } + flags.AddQueryFlagsToCmd(cmd) + return cmd } diff --git a/x/lightclient/keeper/canonical_client.go b/x/lightclient/keeper/canonical_client.go index ba192cf50..f3dbd0758 100644 --- a/x/lightclient/keeper/canonical_client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -7,31 +7,58 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/dymensionxyz/gerr-cosmos/gerrc" + "github.com/dymensionxyz/sdk-utils/utils/uevent" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) -// GetProspectiveCanonicalClient returns the client id of the first IBC client which can be set as the canonical client for the given rollapp. -// The canonical client criteria are: -// 1. The client must be a tendermint client. -// 2. The client state must match the expected client params as configured by the module -// 3. All the existing consensus states much match the corresponding height rollapp block descriptors -func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, maxHeight uint64) (clientID string, stateCompatible bool) { - k.ibcClientKeeper.IterateClientStates(ctx, nil, func(client string, cs exported.ClientState) bool { - err := k.validClient(ctx, client, cs, rollappId, maxHeight) - if err != nil && !errorsmod.IsOf(err, errChainIDMismatch) { - ctx.Logger().Debug("tried to validate rollapp against light client for same chain id: rollapp: %s: client: %s", rollappId, client, "err", err) - } - if err == nil { - clientID = client - stateCompatible = true - return true - } - return false - }) - return +// intended to be called by relayer, but can be called by anyone +// verifies that the suggested client is safe to designate canonical and matches state updates from the sequencer +func (k *Keeper) TrySetCanonicalClient(ctx sdk.Context, clientID string) error { + clientStateI, ok := k.ibcClientKeeper.GetClientState(ctx, clientID) + if !ok { + return gerrc.ErrNotFound.Wrap("client") + } + + clientState, ok := clientStateI.(*ibctm.ClientState) + if !ok { + return gerrc.ErrInvalidArgument.Wrap("not tm client") + } + + chainID := clientState.ChainId + _, ok = k.rollappKeeper.GetRollapp(ctx, chainID) + if !ok { + return gerrc.ErrNotFound.Wrap("rollapp") + } + rollappID := chainID + + _, ok = k.GetCanonicalClient(ctx, rollappID) + if ok { + return gerrc.ErrAlreadyExists.Wrap("canonical client for rollapp") + } + + latestHeight, ok := k.rollappKeeper.GetLatestHeight(ctx, rollappID) + if !ok { + return gerrc.ErrNotFound.Wrap("latest rollapp height") + } + + err := k.validClient(ctx, clientID, clientState, rollappID, latestHeight) + if err != nil { + return errorsmod.Wrap(err, "unsafe to mark client canonical: check that sequencer has posted a recent state update") + } + + k.SetCanonicalClient(ctx, rollappID, clientID) + + if err := uevent.EmitTypedEvent(ctx, &types.EventSetCanonicalClient{ + RollappId: rollappID, + ClientId: clientID, + }); err != nil { + return errorsmod.Wrap(err, "emit typed event") + } + + return nil } func (k Keeper) GetCanonicalClient(ctx sdk.Context, rollappId string) (string, bool) { @@ -47,7 +74,6 @@ func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID s store := ctx.KVStore(k.storeKey) store.Set(types.GetRollappClientKey(rollappId), []byte(clientID)) store.Set(types.CanonicalClientKey(clientID), []byte(rollappId)) - // TODO: event and log } func (k Keeper) GetAllCanonicalClients(ctx sdk.Context) (clients []types.CanonicalClient) { @@ -67,21 +93,25 @@ func (k Keeper) expectedClient() ibctm.ClientState { return types.DefaultExpectedCanonicalClientParams() } -var errChainIDMismatch = errors.New("chain id mismatch") +var ( + ErrNoMatch = gerrc.ErrFailedPrecondition.Wrap("not at least one cons state matches the rollapp state") + ErrMismatch = gerrc.ErrInvalidArgument.Wrap("consensus state mismatch") + ErrParamsMismatch = gerrc.ErrInvalidArgument.Wrap("params") +) -func (k Keeper) validClient(ctx sdk.Context, clientID string, cs exported.ClientState, rollappId string, maxHeight uint64) error { - tmClientState, ok := cs.(*ibctm.ClientState) - if !ok { - return errors.New("not tm client") - } - if tmClientState.ChainId != rollappId { - return errChainIDMismatch - } +// The canonical client criteria are: +// 1. The client must be a tendermint client. +// 2. The client state must match the expected client params as configured by the module +// 3. All the existing consensus states much match the corresponding height rollapp block descriptors +func (k Keeper) validClient(ctx sdk.Context, clientID string, cs *ibctm.ClientState, rollappId string, maxHeight uint64) error { + log := k.Logger(ctx).With("component", "valid client func", "rollapp", rollappId, "client", clientID) + + log.Debug("top of func", "max height", maxHeight, "gas", ctx.GasMeter().GasConsumed()) expClient := k.expectedClient() - if err := types.IsCanonicalClientParamsValid(tmClientState, &expClient); err != nil { - return errorsmod.Wrap(err, "params") + if err := types.IsCanonicalClientParamsValid(cs, &expClient); err != nil { + return errors.Join(err, ErrParamsMismatch) } // FIXME: No need to get all consensus states. should iterate over the consensus states @@ -89,13 +119,15 @@ func (k Keeper) validClient(ctx sdk.Context, clientID string, cs exported.Client ClientId: clientID, Pagination: &query.PageRequest{Limit: maxHeight}, }) + log.Debug("after fetch heights", "max height", maxHeight, "gas", ctx.GasMeter().GasConsumed()) if err != nil { return errorsmod.Wrap(err, "cons state heights") } atLeastOneMatch := false for _, consensusHeight := range res.ConsensusStateHeights { + log.Debug("after fetch heights", "cons state height", consensusHeight.RevisionHeight, "gas", ctx.GasMeter().GasConsumed()) h := consensusHeight.GetRevisionHeight() - if maxHeight < h { + if maxHeight <= h { break } consensusState, _ := k.ibcClientKeeper.GetClientConsensusState(ctx, clientID, consensusHeight) @@ -120,7 +152,7 @@ func (k Keeper) validClient(ctx sdk.Context, clientID string, cs exported.Client } err = types.CheckCompatibility(*tmConsensusState, rollappState) if err != nil { - return errorsmod.Wrapf(err, "check compatibility: height: %d", h) + return errorsmod.Wrapf(errors.Join(ErrMismatch, err), "check compatibility: height: %d", h) } atLeastOneMatch = true } @@ -128,7 +160,7 @@ func (k Keeper) validClient(ctx sdk.Context, clientID string, cs exported.Client // (There are also no disagreeing consensus states. There may be some consensus states // for future state updates, which will incur a fraud if they disagree.) if !atLeastOneMatch { - return errors.New("no matching consensus state found") + return ErrNoMatch } return nil } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 8a47e965a..45966964d 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -41,10 +41,6 @@ func (hook rollappHook) AfterUpdateState( client, ok := hook.k.GetCanonicalClient(ctx, rollappId) if !ok { - client, ok = hook.k.GetProspectiveCanonicalClient(ctx, rollappId, stateInfo.GetLatestHeight()-1) - if ok { - hook.k.SetCanonicalClient(ctx, rollappId, client) - } return nil } diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index c2fdd7463..bfdc85589 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -11,15 +11,15 @@ import ( "github.com/stretchr/testify/require" ) -type testInput struct { - rollappId string - stateInfo *rollapptypes.StateInfo -} - func TestAfterUpdateState(t *testing.T) { + type input struct { + rollappId string + stateInfo *rollapptypes.StateInfo + } + testCases := []struct { name string - prepare func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput + prepare func(ctx sdk.Context, k lightClientKeeper.Keeper) input expectErr bool }{ // TODO: tests need expanding @@ -27,8 +27,8 @@ func TestAfterUpdateState(t *testing.T) { // - Client with all cons states after the state update will not be canonical { name: "canonical client does not exist for rollapp", - prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { - return testInput{ + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) input { + return input{ rollappId: "rollapp-no-canon-client", stateInfo: &rollapptypes.StateInfo{}, } @@ -38,11 +38,11 @@ func TestAfterUpdateState(t *testing.T) { { name: "both states are not compatible - slash the sequencer who signed", - prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) input { k.SetCanonicalClient(ctx, keepertest.DefaultRollapp, keepertest.CanonClientID) err := k.SaveSigner(ctx, keepertest.Alice.Address, keepertest.CanonClientID, 2) require.NoError(t, err) - return testInput{ + return input{ rollappId: keepertest.DefaultRollapp, stateInfo: &rollapptypes.StateInfo{ Sequencer: keepertest.Alice.Address, @@ -74,12 +74,12 @@ func TestAfterUpdateState(t *testing.T) { }, { name: "state is compatible", - prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) input { k.SetCanonicalClient(ctx, keepertest.DefaultRollapp, keepertest.CanonClientID) err := k.SaveSigner(ctx, keepertest.Alice.Address, keepertest.CanonClientID, 2) require.NoError(t, err) - return testInput{ + return input{ rollappId: keepertest.DefaultRollapp, stateInfo: &rollapptypes.StateInfo{ Sequencer: keepertest.Alice.Address, @@ -114,6 +114,7 @@ func TestAfterUpdateState(t *testing.T) { t.Run(tc.name, func(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) input := tc.prepare(ctx, *keeper) + err := keeper.RollappHooks().AfterUpdateState(ctx, input.rollappId, input.stateInfo) if tc.expectErr { require.Error(t, err) diff --git a/x/lightclient/keeper/msg_server.go b/x/lightclient/keeper/msg_server.go new file mode 100644 index 000000000..37bb5e65f --- /dev/null +++ b/x/lightclient/keeper/msg_server.go @@ -0,0 +1,29 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" +) + +type msgServer struct { + *Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper *Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +func (m msgServer) SetCanonicalClient(goCtx context.Context, msg *types.MsgSetCanonicalClient) (*types.MsgSetCanonicalClientResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := m.Keeper.TrySetCanonicalClient(ctx, msg.ClientId); err != nil { + return nil, err + } + return &types.MsgSetCanonicalClientResponse{}, nil +} diff --git a/x/lightclient/module.go b/x/lightclient/module.go index a4c110925..8ccf7c138 100644 --- a/x/lightclient/module.go +++ b/x/lightclient/module.go @@ -44,9 +44,11 @@ func (AppModuleBasic) Name() string { } func (AppModuleBasic) RegisterCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) } func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterCodec(cdc) } // RegisterInterfaces registers the module's interface types @@ -120,6 +122,7 @@ func (am AppModule) Name() string { // module-specific GRPC queries. func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterQueryServer(cfg.QueryServer(), am.keeper) + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(&am.keeper)) } // RegisterInvariants registers the module's invariants. diff --git a/x/lightclient/types/codec.go b/x/lightclient/types/codec.go index fb893bff6..a4834525a 100644 --- a/x/lightclient/types/codec.go +++ b/x/lightclient/types/codec.go @@ -1,13 +1,40 @@ package types import ( + "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" "github.com/cosmos/ibc-go/v7/modules/core/exported" ) +func RegisterCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgSetCanonicalClient{}, "lightclient/SetCanonicalClient", nil) +} + func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgSetCanonicalClient{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) registry.RegisterInterface( "ibc.core.client.v1.ClientState", (*exported.ClientState)(nil), ) } + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(Amino) +) + +func init() { + RegisterCodec(Amino) + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + sdk.RegisterLegacyAminoCodec(Amino) + RegisterCodec(authzcodec.Amino) + + Amino.Seal() +} diff --git a/x/lightclient/types/events.pb.go b/x/lightclient/types/events.pb.go index 62f5aa4b7..252de4489 100644 --- a/x/lightclient/types/events.pb.go +++ b/x/lightclient/types/events.pb.go @@ -76,8 +76,62 @@ func (m *EventSetCanonicalChannel) GetChannelId() string { return "" } +// When the canonical client of the rollapp is set +type EventSetCanonicalClient struct { + RollappId string `protobuf:"bytes,1,opt,name=rollapp_id,json=rollappId,proto3" json:"rollapp_id,omitempty"` + ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` +} + +func (m *EventSetCanonicalClient) Reset() { *m = EventSetCanonicalClient{} } +func (m *EventSetCanonicalClient) String() string { return proto.CompactTextString(m) } +func (*EventSetCanonicalClient) ProtoMessage() {} +func (*EventSetCanonicalClient) Descriptor() ([]byte, []int) { + return fileDescriptor_95574d182027cfd8, []int{1} +} +func (m *EventSetCanonicalClient) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventSetCanonicalClient) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventSetCanonicalClient.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventSetCanonicalClient) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventSetCanonicalClient.Merge(m, src) +} +func (m *EventSetCanonicalClient) XXX_Size() int { + return m.Size() +} +func (m *EventSetCanonicalClient) XXX_DiscardUnknown() { + xxx_messageInfo_EventSetCanonicalClient.DiscardUnknown(m) +} + +var xxx_messageInfo_EventSetCanonicalClient proto.InternalMessageInfo + +func (m *EventSetCanonicalClient) GetRollappId() string { + if m != nil { + return m.RollappId + } + return "" +} + +func (m *EventSetCanonicalClient) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + func init() { proto.RegisterType((*EventSetCanonicalChannel)(nil), "dymensionxyz.dymension.lightclient.EventSetCanonicalChannel") + proto.RegisterType((*EventSetCanonicalClient)(nil), "dymensionxyz.dymension.lightclient.EventSetCanonicalClient") } func init() { @@ -85,7 +139,7 @@ func init() { } var fileDescriptor_95574d182027cfd8 = []byte{ - // 212 bytes of a gzipped FileDescriptorProto + // 235 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xa9, 0xcc, 0x4d, 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0xab, 0xa8, 0xac, 0x42, 0x70, 0xf4, 0x73, 0x32, 0xd3, 0x33, 0x4a, 0x92, 0x73, 0x32, 0x53, 0xf3, 0x4a, 0xf4, 0x53, 0xcb, 0x52, 0xf3, 0x4a, 0x8a, 0xf5, 0x0a, 0x8a, @@ -94,12 +148,13 @@ var fileDescriptor_95574d182027cfd8 = []byte{ 0x49, 0xc1, 0xa9, 0x25, 0xce, 0x89, 0x79, 0xf9, 0x79, 0x99, 0xc9, 0x89, 0x39, 0xce, 0x19, 0x89, 0x79, 0x79, 0xa9, 0x39, 0x42, 0xb2, 0x5c, 0x5c, 0x45, 0xf9, 0x39, 0x39, 0x89, 0x05, 0x05, 0xf1, 0x99, 0x29, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x9c, 0x50, 0x11, 0xcf, 0x14, 0x90, 0x74, - 0x32, 0x44, 0x25, 0x48, 0x9a, 0x09, 0x22, 0x0d, 0x15, 0xf1, 0x4c, 0x71, 0x0a, 0x3a, 0xf1, 0x48, - 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, - 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0x8b, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, - 0xe4, 0xfc, 0x5c, 0x5c, 0x3e, 0x2d, 0x33, 0xd6, 0xaf, 0x40, 0xf1, 0x6e, 0x49, 0x65, 0x41, 0x6a, - 0x71, 0x12, 0x1b, 0xd8, 0xd1, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x42, 0xb1, 0x88, 0x2d, - 0x21, 0x01, 0x00, 0x00, + 0x32, 0x44, 0x25, 0x48, 0x9a, 0x09, 0x22, 0x0d, 0x15, 0xf1, 0x4c, 0x51, 0x0a, 0xe5, 0x12, 0xc7, + 0x34, 0x19, 0xec, 0x14, 0x42, 0x06, 0x4b, 0x73, 0x71, 0x42, 0xdc, 0x8c, 0x30, 0x97, 0x03, 0x22, + 0xe0, 0x99, 0xe2, 0x14, 0x74, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, + 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x16, + 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xb8, 0x02, 0xb0, 0xcc, 0x58, 0xbf, + 0x02, 0x25, 0x14, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x61, 0x61, 0x0c, 0x08, 0x00, + 0x00, 0xff, 0xff, 0x42, 0x10, 0x45, 0x95, 0x78, 0x01, 0x00, 0x00, } func (m *EventSetCanonicalChannel) Marshal() (dAtA []byte, err error) { @@ -139,6 +194,43 @@ func (m *EventSetCanonicalChannel) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } +func (m *EventSetCanonicalClient) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventSetCanonicalClient) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventSetCanonicalClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0x12 + } + if len(m.RollappId) > 0 { + i -= len(m.RollappId) + copy(dAtA[i:], m.RollappId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.RollappId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { offset -= sovEvents(v) base := offset @@ -167,6 +259,23 @@ func (m *EventSetCanonicalChannel) Size() (n int) { return n } +func (m *EventSetCanonicalClient) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.RollappId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + func sovEvents(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -287,6 +396,120 @@ func (m *EventSetCanonicalChannel) Unmarshal(dAtA []byte) error { } return nil } +func (m *EventSetCanonicalClient) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventSetCanonicalClient: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventSetCanonicalClient: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RollappId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RollappId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipEvents(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 59999b6d1..3bca788b2 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -19,6 +19,7 @@ type SequencerKeeperExpected interface { type RollappKeeperExpected interface { GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) + GetLatestHeight(ctx sdk.Context, rollappId string) (uint64, bool) FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (*rollapptypes.StateInfo, error) GetLatestStateInfo(ctx sdk.Context, rollappId string) (rollapptypes.StateInfo, bool) SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rollapp) @@ -27,7 +28,6 @@ type RollappKeeperExpected interface { type IBCClientKeeperExpected interface { GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) - IterateClientStates(ctx sdk.Context, prefix []byte, cb func(clientID string, cs exported.ClientState) bool) ConsensusStateHeights(c context.Context, req *ibcclienttypes.QueryConsensusStateHeightsRequest) (*ibcclienttypes.QueryConsensusStateHeightsResponse, error) ClientStore(ctx sdk.Context, clientID string) sdk.KVStore } diff --git a/x/lightclient/types/tx.go b/x/lightclient/types/tx.go new file mode 100644 index 000000000..ef2898ac7 --- /dev/null +++ b/x/lightclient/types/tx.go @@ -0,0 +1,56 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + "github.com/dymensionxyz/gerr-cosmos/gerrc" +) + +const ( + TypeMsgSetCanonicalClient = "set_canonical_client" +) + +var ( + _ sdk.Msg = &MsgSetCanonicalClient{} + _ legacytx.LegacyMsg = &MsgSetCanonicalClient{} +) + +func NewMsgUpdateState(signer, client string) *MsgSetCanonicalClient { + return &MsgSetCanonicalClient{ + Signer: signer, + ClientId: client, + } +} + +func (msg *MsgSetCanonicalClient) Route() string { + return ModuleName +} + +func (msg *MsgSetCanonicalClient) Type() string { + return TypeMsgSetCanonicalClient +} + +func (msg *MsgSetCanonicalClient) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgSetCanonicalClient) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgSetCanonicalClient) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return errorsmod.Wrapf(gerrc.ErrInvalidArgument, "invalid creator address (%s)", err) + } + if msg.ClientId == "" { + return gerrc.ErrInvalidArgument.Wrap("empty client id") + } + return nil +} diff --git a/x/lightclient/types/tx.pb.go b/x/lightclient/types/tx.pb.go new file mode 100644 index 000000000..6fe674bcc --- /dev/null +++ b/x/lightclient/types/tx.pb.go @@ -0,0 +1,588 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/lightclient/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/codec/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// verify a client state and its consensus states against the rollapp +// if it matches, set the client as the canonical client for the rollapp +type MsgSetCanonicalClient struct { + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // id of ibc client state + ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` +} + +func (m *MsgSetCanonicalClient) Reset() { *m = MsgSetCanonicalClient{} } +func (m *MsgSetCanonicalClient) String() string { return proto.CompactTextString(m) } +func (*MsgSetCanonicalClient) ProtoMessage() {} +func (*MsgSetCanonicalClient) Descriptor() ([]byte, []int) { + return fileDescriptor_86d8e0adcad4a570, []int{0} +} +func (m *MsgSetCanonicalClient) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetCanonicalClient) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetCanonicalClient.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSetCanonicalClient) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetCanonicalClient.Merge(m, src) +} +func (m *MsgSetCanonicalClient) XXX_Size() int { + return m.Size() +} +func (m *MsgSetCanonicalClient) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetCanonicalClient.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetCanonicalClient proto.InternalMessageInfo + +func (m *MsgSetCanonicalClient) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgSetCanonicalClient) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +type MsgSetCanonicalClientResponse struct { +} + +func (m *MsgSetCanonicalClientResponse) Reset() { *m = MsgSetCanonicalClientResponse{} } +func (m *MsgSetCanonicalClientResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSetCanonicalClientResponse) ProtoMessage() {} +func (*MsgSetCanonicalClientResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_86d8e0adcad4a570, []int{1} +} +func (m *MsgSetCanonicalClientResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetCanonicalClientResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetCanonicalClientResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSetCanonicalClientResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetCanonicalClientResponse.Merge(m, src) +} +func (m *MsgSetCanonicalClientResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSetCanonicalClientResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetCanonicalClientResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetCanonicalClientResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgSetCanonicalClient)(nil), "dymensionxyz.dymension.lightclient.MsgSetCanonicalClient") + proto.RegisterType((*MsgSetCanonicalClientResponse)(nil), "dymensionxyz.dymension.lightclient.MsgSetCanonicalClientResponse") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/lightclient/tx.proto", fileDescriptor_86d8e0adcad4a570) +} + +var fileDescriptor_86d8e0adcad4a570 = []byte{ + // 312 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0xa9, 0xcc, 0x4d, + 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0xab, 0xa8, 0xac, 0xd2, 0x87, 0x73, 0xf4, 0x73, 0x32, 0xd3, 0x33, + 0x4a, 0x92, 0x73, 0x32, 0x53, 0xf3, 0x4a, 0xf4, 0x4b, 0x2a, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, + 0x85, 0x94, 0x90, 0x15, 0xeb, 0xc1, 0x39, 0x7a, 0x48, 0x8a, 0xa5, 0xc4, 0x93, 0xf3, 0x8b, 0x73, + 0xf3, 0x8b, 0xf5, 0x73, 0x8b, 0xd3, 0xf5, 0xcb, 0x0c, 0x41, 0x14, 0x44, 0xb3, 0x94, 0x48, 0x7a, + 0x7e, 0x7a, 0x3e, 0x98, 0xa9, 0x0f, 0x62, 0x41, 0x45, 0x65, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, 0x52, + 0xf5, 0x13, 0x0b, 0x32, 0xf5, 0x13, 0xf3, 0xf2, 0xf2, 0x4b, 0x12, 0x4b, 0x32, 0xf3, 0xf3, 0x8a, + 0xa1, 0xb2, 0x92, 0x50, 0x59, 0x30, 0x2f, 0xa9, 0x34, 0x4d, 0x3f, 0x31, 0xaf, 0x12, 0x22, 0xa5, + 0x14, 0xc9, 0x25, 0xea, 0x5b, 0x9c, 0x1e, 0x9c, 0x5a, 0xe2, 0x9c, 0x98, 0x97, 0x9f, 0x97, 0x99, + 0x9c, 0x98, 0xe3, 0x0c, 0x76, 0x80, 0x90, 0x18, 0x17, 0x5b, 0x71, 0x66, 0x7a, 0x5e, 0x6a, 0x91, + 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x94, 0x27, 0x24, 0xcd, 0xc5, 0x09, 0x71, 0x62, 0x7c, + 0x66, 0x8a, 0x04, 0x13, 0x58, 0x8a, 0x03, 0x22, 0xe0, 0x99, 0x62, 0xc5, 0xdd, 0xf4, 0x7c, 0x83, + 0x16, 0x54, 0xa5, 0x92, 0x3c, 0x97, 0x2c, 0x56, 0xa3, 0x83, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, + 0x53, 0x8d, 0x66, 0x31, 0x72, 0x31, 0xfb, 0x16, 0xa7, 0x0b, 0x4d, 0x62, 0xe4, 0x12, 0xc2, 0xe2, + 0x02, 0x4b, 0x3d, 0xc2, 0xe1, 0xa4, 0x87, 0xd5, 0x06, 0x29, 0x47, 0xb2, 0xb5, 0xc2, 0x1c, 0xe7, + 0x14, 0x74, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, + 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x16, 0xe9, 0x99, 0x25, + 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x38, 0xa2, 0xbd, 0xcc, 0x58, 0xbf, 0x02, 0x35, + 0xee, 0x2b, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x61, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, + 0x16, 0x12, 0xfb, 0x6f, 0x2e, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + SetCanonicalClient(ctx context.Context, in *MsgSetCanonicalClient, opts ...grpc.CallOption) (*MsgSetCanonicalClientResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SetCanonicalClient(ctx context.Context, in *MsgSetCanonicalClient, opts ...grpc.CallOption) (*MsgSetCanonicalClientResponse, error) { + out := new(MsgSetCanonicalClientResponse) + err := c.cc.Invoke(ctx, "/dymensionxyz.dymension.lightclient.Msg/SetCanonicalClient", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + SetCanonicalClient(context.Context, *MsgSetCanonicalClient) (*MsgSetCanonicalClientResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SetCanonicalClient(ctx context.Context, req *MsgSetCanonicalClient) (*MsgSetCanonicalClientResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetCanonicalClient not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SetCanonicalClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSetCanonicalClient) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SetCanonicalClient(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dymensionxyz.dymension.lightclient.Msg/SetCanonicalClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SetCanonicalClient(ctx, req.(*MsgSetCanonicalClient)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "dymensionxyz.dymension.lightclient.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SetCanonicalClient", + Handler: _Msg_SetCanonicalClient_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "dymensionxyz/dymension/lightclient/tx.proto", +} + +func (m *MsgSetCanonicalClient) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSetCanonicalClient) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetCanonicalClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSetCanonicalClientResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSetCanonicalClientResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetCanonicalClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgSetCanonicalClient) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSetCanonicalClientResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgSetCanonicalClient) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSetCanonicalClient: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetCanonicalClient: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSetCanonicalClientResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSetCanonicalClientResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetCanonicalClientResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/rollapp/keeper/latest_state_info_index.go b/x/rollapp/keeper/latest_state_info_index.go index bf1f0ab3b..94afd8136 100644 --- a/x/rollapp/keeper/latest_state_info_index.go +++ b/x/rollapp/keeper/latest_state_info_index.go @@ -36,6 +36,17 @@ func (k Keeper) GetLatestStateInfoIndex( return val, true } +func (k Keeper) GetLatestHeight( + ctx sdk.Context, + rollappId string, +) (uint64, bool) { + info, ok := k.GetLatestStateInfo(ctx, rollappId) + if !ok { + return 0, false + } + return info.GetLatestHeight(), true +} + // RemoveLatestStateInfoIndex removes a latestStateInfoIndex from the store func (k Keeper) RemoveLatestStateInfoIndex( ctx sdk.Context,