From c4116c72c78138943393b652373d62f85fef219b Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:59:28 +0100 Subject: [PATCH 01/87] adding light client module --- x/lightclient/module.go | 138 ++++++++++++++++++++++++++++++++++++ x/lightclient/types/keys.go | 17 +++++ 2 files changed, 155 insertions(+) create mode 100644 x/lightclient/module.go create mode 100644 x/lightclient/types/keys.go diff --git a/x/lightclient/module.go b/x/lightclient/module.go new file mode 100644 index 000000000..189b853bc --- /dev/null +++ b/x/lightclient/module.go @@ -0,0 +1,138 @@ +package sequencer + +import ( + "encoding/json" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + "github.com/dymensionxyz/dymension/v3/x/eibc/client/cli" + "github.com/dymensionxyz/dymension/v3/x/eibc/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface for the module. +type AppModuleBasic struct { + cdc codec.BinaryCodec +} + +func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +func (AppModuleBasic) RegisterCodec(cdc *codec.LegacyAmino) { +} + +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { +} + +// RegisterInterfaces registers the module's interface types +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { +} + +// DefaultGenesis returns the module's default genesis state. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return nil +} + +// ValidateGenesis performs genesis state validation for the module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + return nil +} + +// RegisterRESTRoutes registers the capability module's REST service handlers. +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { +} + +// GetTxCmd returns the capability module's root tx command. +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns the capability module's root query command. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd(types.StoreKey) +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface for the module. +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper +} + +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + } +} + +// Name returns the module's name. +func (am AppModule) Name() string { + return am.AppModuleBasic.Name() +} + +// RegisterServices registers a GRPC query service to respond to the +// module-specific GRPC queries. +func (am AppModule) RegisterServices(cfg module.Configurator) { +} + +// RegisterInvariants registers the module's invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { +} + +// InitGenesis performs the module's genesis initialization It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + return nil +} + +// ConsensusVersion implements ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// BeginBlock executes all ABCI BeginBlock logic respective to the module. +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock executes all ABCI EndBlock logic respective to the module. It +// returns no validator updates. +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go new file mode 100644 index 000000000..84b2b410d --- /dev/null +++ b/x/lightclient/types/keys.go @@ -0,0 +1,17 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "lightclient" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // QuerierRoute defines the module's query routing key + QuerierRoute = ModuleName +) + +var ( + SequencersKeyPrefix = []byte{0x00} // prefix/seqAddr + +) From 9340d9ebceb87b5ab12dae1d396b5c8655d44f28 Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 12 Aug 2024 12:13:07 +0100 Subject: [PATCH 02/87] implement msgcreateclient antehandler --- x/lightclient/ante/ibc_msg_create_client.go | 68 +++++++++++++++++++++ x/lightclient/ante/ibc_msgs.go | 46 ++++++++++++++ x/lightclient/keeper/keeper.go | 59 ++++++++++++++++++ x/lightclient/types/keys.go | 18 +++++- 4 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 x/lightclient/ante/ibc_msg_create_client.go create mode 100644 x/lightclient/ante/ibc_msgs.go create mode 100644 x/lightclient/keeper/keeper.go diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go new file mode 100644 index 000000000..97e8da0be --- /dev/null +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -0,0 +1,68 @@ +package ante + +import ( + "reflect" + + sdk "github.com/cosmos/cosmos-sdk/types" + 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" +) + +func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibcclienttypes.MsgCreateClient) { + // Unpack client state from message + clientState, err := ibcclienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return + } + // Only tendermint client types can be set as canonical light client + if clientState.ClientType() != exported.Tendermint { + // Cast client state to tendermint client state - we need this to check the chain id and latest height + tendmermintClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return + } + // Check if rollapp exists for given chain id + rollappID := tendmermintClientState.ChainId + _, found := i.rollappKeeper.GetRollapp(ctx, rollappID) + if !found { + return + } + // Check if canonical client already exists for rollapp. Only one canonical client is allowed per rollapp + _, found = i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) + if found { + return + } + // Check if there are existing block descriptors for the given height of client state + height := tendmermintClientState.GetLatestHeight() + stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) + if err != nil { + return + } + blockDescriptor := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] + // Unpack consensus state from message + consensusState, err := ibcclienttypes.UnpackConsensusState(msg.ConsensusState) + if err != nil { + return + } + // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp + tendermintConsensusState, ok := consensusState.(*ibctm.ConsensusState) + if !ok { + return + } + // Check if block descriptor state root matches tendermint consensus state root + if reflect.DeepEqual(blockDescriptor.StateRoot, tendermintConsensusState.GetRoot().GetHash()) { + return + } + // Check if block descriptor timestamp matches tendermint consensus state timestamp + // TODO: Add this field to BD struct + // if blockDescriptor.Timestamp != tendermintConsensusState.GetTimestamp() { + // return + // } + + // Generate client id and begin canonical light client registration by storing it in transient store. + // Will be confirmed after the client is created in post handler. + nextClientID := i.ibcKeeper.ClientKeeper.GenerateClientIdentifier(ctx, exported.Tendermint) + i.lightClientKeeper.BeginCanonicalLightClientRegistration(ctx, rollappID, nextClientID) + } +} diff --git a/x/lightclient/ante/ibc_msgs.go b/x/lightclient/ante/ibc_msgs.go new file mode 100644 index 000000000..dd1c6596d --- /dev/null +++ b/x/lightclient/ante/ibc_msgs.go @@ -0,0 +1,46 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +var _ sdk.AnteDecorator = IBCMessagesDecorator{} + +type RollappKeeperExpected interface { + GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) + FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (rollapptypes.StateInfo, error) +} + +type IBCMessagesDecorator struct { + ibcKeeper ibckeeper.Keeper + rollappKeeper RollappKeeperExpected + lightClientKeeper keeper.Keeper +} + +func NewIBCMessagesDecorator() IBCMessagesDecorator { + return IBCMessagesDecorator{} +} + +// AnteHandle implements types.AnteDecorator. +func (i IBCMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + msgs := tx.GetMsgs() + for _, m := range msgs { + switch msg := m.(type) { + case *ibcclienttypes.MsgCreateClient: + i.HandleMsgCreateClient(ctx, msg) + case *ibcclienttypes.MsgUpdateClient: + { + } + case *ibcclienttypes.MsgSubmitMisbehaviour: + { + } + default: + continue + } + } + return next(ctx, tx, simulate) +} diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go new file mode 100644 index 000000000..91456534c --- /dev/null +++ b/x/lightclient/keeper/keeper.go @@ -0,0 +1,59 @@ +package keeper + +import ( + "fmt" + + "github.com/cometbft/cometbft/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" +) + +type Keeper struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey +} + +func NewKeeper( + cdc codec.BinaryCodec, + storeKey storetypes.StoreKey, +) *Keeper { + + k := &Keeper{ + cdc: cdc, + storeKey: storeKey, + } + return k +} + +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +func (k Keeper) GetCanonicalClient(ctx sdk.Context, rollappId string) (string, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.CanonicalClientKey(rollappId)) + if bz == nil { + return "", false + } + return string(bz), true +} + +func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.CanonicalClientKey(rollappId), []byte(clientID)) +} + +func (k Keeper) BeginCanonicalLightClientRegistration(ctx sdk.Context, rollappId string, clientID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.CanonicalLightClientRegistrationKey(rollappId), []byte(clientID)) +} + +func (k Keeper) ConfirmCanonicalLightClientRegistration(ctx sdk.Context, rollappId string, clientID string) { + k.SetCanonicalClient(ctx, rollappId, clientID) + store := ctx.KVStore(k.storeKey) + store.Delete(types.CanonicalLightClientRegistrationKey(rollappId)) +} diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go index 84b2b410d..4ffcabb26 100644 --- a/x/lightclient/types/keys.go +++ b/x/lightclient/types/keys.go @@ -9,9 +9,25 @@ const ( // QuerierRoute defines the module's query routing key QuerierRoute = ModuleName + + // TransientKey defines the module's transient store key + TransientKey = "t_lightclient" ) +// KV Store var ( - SequencersKeyPrefix = []byte{0x00} // prefix/seqAddr + RollappClientKey = []byte{0x01} +) +// Transient Store +var ( + LightClientRegistrationKey = []byte{0x02} ) + +func CanonicalClientKey(rollappId string) []byte { + return append(RollappClientKey, []byte(rollappId)...) +} + +func CanonicalLightClientRegistrationKey(rollappId string) []byte { + return append(LightClientRegistrationKey, []byte(rollappId)...) +} From dfc1a245f5355494c5b95c845bb8bc00c980192c Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 12 Aug 2024 12:23:50 +0100 Subject: [PATCH 03/87] implement ante handler for submit misbehaviour --- .../ante/ibc_msg_submit_misbehaviour.go | 27 +++++++++++++++++++ x/lightclient/ante/ibc_msgs.go | 6 ++--- 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 x/lightclient/ante/ibc_msg_submit_misbehaviour.go diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go new file mode 100644 index 000000000..9ebb8d923 --- /dev/null +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go @@ -0,0 +1,27 @@ +package ante + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" +) + +func (i IBCMessagesDecorator) HandleMsgSubmitMisbehaviour(ctx sdk.Context, msg *ibcclienttypes.MsgSubmitMisbehaviour) error { + clientState, found := i.ibcKeeper.ClientKeeper.GetClientState(ctx, msg.ClientId) + if !found { + return nil + } + // Cast client state to tendermint client state - we need this to check the chain id + tendmermintClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return nil + } + // Check if the the client is the canonical client for the rollapp + rollappID := tendmermintClientState.ChainId + canonicalClient, found := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) + if !found || canonicalClient != msg.ClientId { + return nil + } + return errorsmod.Wrap(ibcclienttypes.ErrInvalidClient, "cannot submit misbehavour for a canonical client") +} diff --git a/x/lightclient/ante/ibc_msgs.go b/x/lightclient/ante/ibc_msgs.go index dd1c6596d..3bebc4730 100644 --- a/x/lightclient/ante/ibc_msgs.go +++ b/x/lightclient/ante/ibc_msgs.go @@ -32,11 +32,11 @@ func (i IBCMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo switch msg := m.(type) { case *ibcclienttypes.MsgCreateClient: i.HandleMsgCreateClient(ctx, msg) - case *ibcclienttypes.MsgUpdateClient: - { - } case *ibcclienttypes.MsgSubmitMisbehaviour: + i.HandleMsgSubmitMisbehaviour(ctx, msg) + case *ibcclienttypes.MsgUpdateClient: { + } default: continue From c1a3f7e941287907f4f17358ed306478906070cd Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:26:11 +0100 Subject: [PATCH 04/87] implement msg update client ante --- x/lightclient/ante/ibc_msg_create_client.go | 4 +- x/lightclient/ante/ibc_msg_update_client.go | 53 +++++++++++++++++++++ x/lightclient/ante/ibc_msgs.go | 8 ++-- 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 x/lightclient/ante/ibc_msg_update_client.go diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index 97e8da0be..5e714af96 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -1,7 +1,7 @@ package ante import ( - "reflect" + "bytes" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -51,7 +51,7 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli return } // Check if block descriptor state root matches tendermint consensus state root - if reflect.DeepEqual(blockDescriptor.StateRoot, tendermintConsensusState.GetRoot().GetHash()) { + if bytes.Equal(blockDescriptor.StateRoot, tendermintConsensusState.GetRoot().GetHash()) { return } // Check if block descriptor timestamp matches tendermint consensus state timestamp diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go new file mode 100644 index 000000000..d8fce6e8d --- /dev/null +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -0,0 +1,53 @@ +package ante + +import ( + "bytes" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" +) + +func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibcclienttypes.MsgUpdateClient) error { + clientState, found := i.ibcKeeper.ClientKeeper.GetClientState(ctx, msg.ClientId) + if !found { + return nil + } + // Cast client state to tendermint client state - we need this to check the chain id + tendmermintClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return nil + } + // Check if the the client is the canonical client for the rollapp + rollappID := tendmermintClientState.ChainId + canonicalClient, found := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) + if !found || canonicalClient != msg.ClientId { + return nil + } + // Check if there are existing block descriptors for the given height of client state - if not return and optimistically accept the update + height := tendmermintClientState.GetLatestHeight() + stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) + if err != nil { + return nil + } + blockDescriptor := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] + clientMessage, err := ibcclienttypes.UnpackClientMessage(msg.ClientMessage) + if err != nil { + return nil + } + header, ok := clientMessage.(*ibctm.Header) + if !ok { + return nil + } + // Check if block descriptor state root matches tendermint header app hash + if !bytes.Equal(blockDescriptor.StateRoot, header.Header.AppHash) { + return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") + } + // TODO: Check if block descriptor timestamp matches tendermint header timestamp + // Check if the validator set hash matches the sequencer + // if len(header.ValidatorSet.Validators) == 1 && header.ValidatorSet.Validators[0].Address { + + // } + return nil +} diff --git a/x/lightclient/ante/ibc_msgs.go b/x/lightclient/ante/ibc_msgs.go index 3bebc4730..a9ca5e323 100644 --- a/x/lightclient/ante/ibc_msgs.go +++ b/x/lightclient/ante/ibc_msgs.go @@ -33,10 +33,12 @@ func (i IBCMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo case *ibcclienttypes.MsgCreateClient: i.HandleMsgCreateClient(ctx, msg) case *ibcclienttypes.MsgSubmitMisbehaviour: - i.HandleMsgSubmitMisbehaviour(ctx, msg) + if err := i.HandleMsgSubmitMisbehaviour(ctx, msg); err != nil { + return ctx, err + } case *ibcclienttypes.MsgUpdateClient: - { - + if err := i.HandleMsgUpdateClient(ctx, msg); err != nil { + return ctx, err } default: continue From d56cf5069f2928e6c93089995dfb38a940336851 Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:10:57 +0100 Subject: [PATCH 05/87] adding posthandler --- x/lightclient/ante/ibc_msg_create_client.go | 9 +++++- x/lightclient/ante/ibc_msg_update_client.go | 1 + x/lightclient/keeper/keeper.go | 12 ++++++-- x/lightclient/post/ibc_msg_create_client.go | 29 ++++++++++++++++++ x/lightclient/post/ibc_msgs.go | 33 +++++++++++++++++++++ 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 x/lightclient/post/ibc_msg_create_client.go create mode 100644 x/lightclient/post/ibc_msgs.go diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index 5e714af96..09434047c 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -16,7 +16,7 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli return } // Only tendermint client types can be set as canonical light client - if clientState.ClientType() != exported.Tendermint { + if clientState.ClientType() == exported.Tendermint { // Cast client state to tendermint client state - we need this to check the chain id and latest height tendmermintClientState, ok := clientState.(*ibctm.ClientState) if !ok { @@ -60,6 +60,13 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli // return // } + // Check if the validator set hash matches the sequencer + // if len(tendermintConsensusState.GetNextValidators().Validators) == 1 && tendermintConsensusState.GetNextValidators().Validators[0].Address { + // return + // } + + // Check if the nextValidatorHash matches the sequencer for h+1 + // Generate client id and begin canonical light client registration by storing it in transient store. // Will be confirmed after the client is created in post handler. nextClientID := i.ibcKeeper.ClientKeeper.GenerateClientIdentifier(ctx, exported.Tendermint) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index d8fce6e8d..70a7438c4 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -49,5 +49,6 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // if len(header.ValidatorSet.Validators) == 1 && header.ValidatorSet.Validators[0].Address { // } + return nil } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 91456534c..1bbe43407 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -52,8 +52,16 @@ func (k Keeper) BeginCanonicalLightClientRegistration(ctx sdk.Context, rollappId store.Set(types.CanonicalLightClientRegistrationKey(rollappId), []byte(clientID)) } -func (k Keeper) ConfirmCanonicalLightClientRegistration(ctx sdk.Context, rollappId string, clientID string) { - k.SetCanonicalClient(ctx, rollappId, clientID) +func (k Keeper) GetCanonicalLightClientRegistration(ctx sdk.Context, rollappId string) (string, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.CanonicalLightClientRegistrationKey(rollappId)) + if bz == nil { + return "", false + } + return string(bz), true +} + +func (k Keeper) ClearCanonicalLightClientRegistration(ctx sdk.Context, rollappId string) { store := ctx.KVStore(k.storeKey) store.Delete(types.CanonicalLightClientRegistrationKey(rollappId)) } diff --git a/x/lightclient/post/ibc_msg_create_client.go b/x/lightclient/post/ibc_msg_create_client.go new file mode 100644 index 000000000..cbe9af844 --- /dev/null +++ b/x/lightclient/post/ibc_msg_create_client.go @@ -0,0 +1,29 @@ +package post + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" +) + +func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibcclienttypes.MsgCreateClient, success bool) { + if success { + clientState, err := ibcclienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return + } + tendmermintClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return + } + rollappID := tendmermintClientState.ChainId + nextClientID, registrationFound := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) + if registrationFound { + _, clientFound := i.ibcKeeper.ClientKeeper.GetClientState(ctx, nextClientID) + if clientFound { + i.lightClientKeeper.SetCanonicalClient(ctx, rollappID, nextClientID) + } + i.lightClientKeeper.ClearCanonicalLightClientRegistration(ctx, rollappID) + } + } +} diff --git a/x/lightclient/post/ibc_msgs.go b/x/lightclient/post/ibc_msgs.go new file mode 100644 index 000000000..903738b7f --- /dev/null +++ b/x/lightclient/post/ibc_msgs.go @@ -0,0 +1,33 @@ +package post + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" +) + +var _ sdk.PostDecorator = IBCMessagesDecorator{} + +type IBCMessagesDecorator struct { + ibcKeeper ibckeeper.Keeper + lightClientKeeper keeper.Keeper +} + +func NewIBCMessagesDecorator() IBCMessagesDecorator { + return IBCMessagesDecorator{} +} + +// PostHandle implements types.PostDecorator. +func (i IBCMessagesDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, success bool, next sdk.PostHandler) (newCtx sdk.Context, err error) { + msgs := tx.GetMsgs() + for _, m := range msgs { + switch msg := m.(type) { + case *ibcclienttypes.MsgCreateClient: + i.HandleMsgCreateClient(ctx, msg, success) + default: + continue + } + } + return next(ctx, tx, simulate, success) +} From 11a0ad488d8076a744f3cbaf2079616c889d0c67 Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:16:00 +0100 Subject: [PATCH 06/87] adding block timestamp to block descriptor --- go.mod | 2 +- .../dymension/rollapp/block_descriptor.proto | 4 +- x/rollapp/types/block_descriptor.pb.go | 93 +++++++++++++++---- 3 files changed, 80 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index a1bb9ee06..04f35588a 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/dymensionxyz/sdk-utils v0.2.7 github.com/ethereum/go-ethereum v1.10.26 github.com/evmos/ethermint v0.22.0 + github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.4 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -114,7 +115,6 @@ require ( github.com/go-stack/stack v1.8.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect - github.com/gogo/protobuf v1.3.3 // indirect github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect diff --git a/proto/dymensionxyz/dymension/rollapp/block_descriptor.proto b/proto/dymensionxyz/dymension/rollapp/block_descriptor.proto index f3bede6cf..1a558ea31 100644 --- a/proto/dymensionxyz/dymension/rollapp/block_descriptor.proto +++ b/proto/dymensionxyz/dymension/rollapp/block_descriptor.proto @@ -4,6 +4,7 @@ package dymensionxyz.dymension.rollapp; option go_package = "github.com/dymensionxyz/dymension/v3/x/rollapp/types"; import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; // BlockDescriptor defines a single rollapp chain block description. @@ -12,10 +13,11 @@ message BlockDescriptor { uint64 height = 1; // stateRoot is a 32 byte array of the hash of the block (state root of the block) bytes stateRoot = 2; + // timestamp is the time of the block finalization + google.protobuf.Timestamp timestamp = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; } // BlockDescriptors defines list of BlockDescriptor. message BlockDescriptors { - repeated BlockDescriptor BD = 1 [(gogoproto.nullable) = false]; } \ No newline at end of file diff --git a/x/rollapp/types/block_descriptor.pb.go b/x/rollapp/types/block_descriptor.pb.go index 5817dcf5c..c0803dd20 100644 --- a/x/rollapp/types/block_descriptor.pb.go +++ b/x/rollapp/types/block_descriptor.pb.go @@ -7,15 +7,19 @@ import ( fmt "fmt" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" + github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "google.golang.org/protobuf/types/known/timestamppb" io "io" math "math" math_bits "math/bits" + time "time" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +var _ = time.Kitchen // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -29,6 +33,8 @@ type BlockDescriptor struct { Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` // stateRoot is a 32 byte array of the hash of the block (state root of the block) StateRoot []byte `protobuf:"bytes,2,opt,name=stateRoot,proto3" json:"stateRoot,omitempty"` + // timestamp is the time of the block finalization + Timestamp time.Time `protobuf:"bytes,3,opt,name=timestamp,proto3,stdtime" json:"timestamp"` } func (m *BlockDescriptor) Reset() { *m = BlockDescriptor{} } @@ -78,6 +84,13 @@ func (m *BlockDescriptor) GetStateRoot() []byte { return nil } +func (m *BlockDescriptor) GetTimestamp() time.Time { + if m != nil { + return m.Timestamp + } + return time.Time{} +} + // BlockDescriptors defines list of BlockDescriptor. type BlockDescriptors struct { BD []BlockDescriptor `protobuf:"bytes,1,rep,name=BD,proto3" json:"BD"` @@ -133,23 +146,26 @@ func init() { } var fileDescriptor_6eb4c1d0c21c2e68 = []byte{ - // 243 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4d, 0xa9, 0xcc, 0x4d, - 0xcd, 0x2b, 0xce, 0xcc, 0xcf, 0xab, 0xa8, 0xac, 0xd2, 0x87, 0x73, 0xf4, 0x8b, 0xf2, 0x73, 0x72, - 0x12, 0x0b, 0x0a, 0xf4, 0x93, 0x72, 0xf2, 0x93, 0xb3, 0xe3, 0x53, 0x52, 0x8b, 0x93, 0x8b, 0x32, - 0x0b, 0x4a, 0xf2, 0x8b, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0xe4, 0x90, 0xb5, 0xe9, 0xc1, - 0x39, 0x7a, 0x50, 0x6d, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa5, 0xfa, 0x20, 0x16, 0x44, - 0x97, 0x92, 0x3b, 0x17, 0xbf, 0x13, 0xc8, 0x3c, 0x17, 0xb8, 0x71, 0x42, 0x62, 0x5c, 0x6c, 0x19, - 0xa9, 0x99, 0xe9, 0x19, 0x25, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x2c, 0x41, 0x50, 0x9e, 0x90, 0x0c, - 0x17, 0x67, 0x71, 0x49, 0x62, 0x49, 0x6a, 0x50, 0x7e, 0x7e, 0x89, 0x04, 0x93, 0x02, 0xa3, 0x06, - 0x4f, 0x10, 0x42, 0x40, 0x29, 0x92, 0x4b, 0x00, 0xcd, 0xa0, 0x62, 0x21, 0x57, 0x2e, 0x26, 0x27, - 0x17, 0x09, 0x46, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x7d, 0x3d, 0xfc, 0xee, 0xd3, 0x43, 0xd3, 0xed, - 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x93, 0x93, 0x8b, 0x93, 0xdf, 0x89, 0x47, 0x72, 0x8c, - 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, - 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x99, 0xa4, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, - 0xe7, 0xea, 0xe3, 0x08, 0xb5, 0x32, 0x63, 0xfd, 0x0a, 0x78, 0xd0, 0x95, 0x54, 0x16, 0xa4, 0x16, - 0x27, 0xb1, 0x81, 0xbd, 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x40, 0x09, 0x0c, 0xda, 0x69, - 0x01, 0x00, 0x00, + // 303 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xc1, 0x4e, 0xc2, 0x30, + 0x18, 0xc7, 0x57, 0x20, 0x44, 0x8a, 0x89, 0x66, 0x31, 0x66, 0x21, 0xa6, 0x2c, 0x9c, 0x76, 0x6a, + 0x13, 0xd0, 0x17, 0x68, 0xf0, 0xea, 0x61, 0xf1, 0xa2, 0x17, 0xc3, 0xa0, 0x96, 0xc5, 0x8d, 0xaf, + 0x59, 0x8b, 0x61, 0xbe, 0x82, 0x17, 0x1e, 0x8b, 0x23, 0x47, 0x4f, 0x6a, 0xb6, 0x17, 0x31, 0xdb, + 0x60, 0x18, 0x12, 0xbd, 0xf5, 0xdf, 0xfc, 0x7f, 0xfd, 0xb5, 0xfd, 0xf0, 0xcd, 0x2c, 0x8d, 0xc5, + 0x42, 0x87, 0xb0, 0x58, 0xa5, 0x6f, 0xac, 0x0e, 0x2c, 0x81, 0x28, 0x9a, 0x28, 0xc5, 0x82, 0x08, + 0xa6, 0x2f, 0x4f, 0x33, 0xa1, 0xa7, 0x49, 0xa8, 0x0c, 0x24, 0x54, 0x25, 0x60, 0xc0, 0x26, 0xbf, + 0x31, 0x5a, 0x07, 0xba, 0xc3, 0x7a, 0x17, 0x12, 0x24, 0x94, 0x55, 0x56, 0xac, 0x2a, 0xaa, 0xd7, + 0x97, 0x00, 0x32, 0x12, 0xac, 0x4c, 0xc1, 0xf2, 0x99, 0x99, 0x30, 0x16, 0xda, 0x4c, 0x62, 0x55, + 0x15, 0x06, 0xef, 0x08, 0x9f, 0xf1, 0xc2, 0x38, 0xae, 0x85, 0xf6, 0x25, 0x6e, 0xcf, 0x45, 0x28, + 0xe7, 0xc6, 0x41, 0x2e, 0xf2, 0x5a, 0xfe, 0x2e, 0xd9, 0x57, 0xb8, 0xa3, 0xcd, 0xc4, 0x08, 0x1f, + 0xc0, 0x38, 0x0d, 0x17, 0x79, 0xa7, 0xfe, 0x61, 0xc3, 0xe6, 0xb8, 0x53, 0x1f, 0xee, 0x34, 0x5d, + 0xe4, 0x75, 0x87, 0x3d, 0x5a, 0xe9, 0xe9, 0x5e, 0x4f, 0xef, 0xf7, 0x0d, 0x7e, 0xb2, 0xf9, 0xec, + 0x5b, 0xeb, 0xaf, 0x3e, 0xf2, 0x0f, 0xd8, 0xe0, 0x01, 0x9f, 0x1f, 0x5d, 0x46, 0xdb, 0xb7, 0xb8, + 0xc1, 0xc7, 0x0e, 0x72, 0x9b, 0x5e, 0x77, 0xc8, 0xe8, 0xff, 0xbf, 0x40, 0x8f, 0x68, 0xde, 0x2a, + 0x2c, 0x7e, 0x83, 0x8f, 0xf9, 0xdd, 0x26, 0x23, 0x68, 0x9b, 0x11, 0xf4, 0x9d, 0x11, 0xb4, 0xce, + 0x89, 0xb5, 0xcd, 0x89, 0xf5, 0x91, 0x13, 0xeb, 0xf1, 0x5a, 0x86, 0x66, 0xbe, 0x0c, 0xe8, 0x14, + 0x62, 0xf6, 0xc7, 0x6c, 0x5e, 0x47, 0x6c, 0x55, 0x0f, 0xc8, 0xa4, 0x4a, 0xe8, 0xa0, 0x5d, 0xbe, + 0x69, 0xf4, 0x13, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x62, 0xb7, 0x2c, 0xcf, 0x01, 0x00, 0x00, } func (m *BlockDescriptor) Marshal() (dAtA []byte, err error) { @@ -172,6 +188,14 @@ func (m *BlockDescriptor) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + n1, err1 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintBlockDescriptor(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x1a if len(m.StateRoot) > 0 { i -= len(m.StateRoot) copy(dAtA[i:], m.StateRoot) @@ -248,6 +272,8 @@ func (m *BlockDescriptor) Size() (n int) { if l > 0 { n += 1 + l + sovBlockDescriptor(uint64(l)) } + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovBlockDescriptor(uint64(l)) return n } @@ -354,6 +380,39 @@ func (m *BlockDescriptor) Unmarshal(dAtA []byte) error { m.StateRoot = []byte{} } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBlockDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBlockDescriptor + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBlockDescriptor + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipBlockDescriptor(dAtA[iNdEx:]) From 37bb6cf912331316237cbcc0e9d145a9cade0bee Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:20:10 +0100 Subject: [PATCH 07/87] implementing AfterUpdateState hook on rollapp update state --- .../block_height_to_finalization_queue_test.go | 5 +++-- x/rollapp/keeper/msg_server_update_state.go | 5 +++++ x/rollapp/types/hooks.go | 12 ++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/x/rollapp/keeper/block_height_to_finalization_queue_test.go b/x/rollapp/keeper/block_height_to_finalization_queue_test.go index 2b5969a92..6af1a9e65 100644 --- a/x/rollapp/keeper/block_height_to_finalization_queue_test.go +++ b/x/rollapp/keeper/block_height_to_finalization_queue_test.go @@ -652,8 +652,9 @@ func (m mockRollappHooks) AfterStateFinalized(_ sdk.Context, _ string, stateInfo } return } -func (m mockRollappHooks) BeforeUpdateState(sdk.Context, string, string) error { return nil } -func (m mockRollappHooks) FraudSubmitted(sdk.Context, string, uint64, string) error { return nil } +func (m mockRollappHooks) BeforeUpdateState(sdk.Context, string, string) error { return nil } +func (m mockRollappHooks) AfterUpdateState(sdk.Context, string, *types.StateInfo) error { return nil } +func (m mockRollappHooks) FraudSubmitted(sdk.Context, string, uint64, string) error { return nil } func (m mockRollappHooks) RollappCreated(sdk.Context, string, string, sdk.AccAddress) error { return nil } diff --git a/x/rollapp/keeper/msg_server_update_state.go b/x/rollapp/keeper/msg_server_update_state.go index 6e225d50a..ebcc428a6 100644 --- a/x/rollapp/keeper/msg_server_update_state.go +++ b/x/rollapp/keeper/msg_server_update_state.go @@ -67,6 +67,11 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) // Write new state information to the store indexed by k.SetStateInfo(ctx, *stateInfo) + err = k.hooks.AfterUpdateState(ctx, msg.Creator, stateInfo) + if err != nil { + return nil, err + } + stateInfoIndex := stateInfo.GetIndex() newFinalizationQueue := []types.StateInfoIndex{stateInfoIndex} diff --git a/x/rollapp/types/hooks.go b/x/rollapp/types/hooks.go index fc4ec70cd..6bd8b2c4a 100644 --- a/x/rollapp/types/hooks.go +++ b/x/rollapp/types/hooks.go @@ -12,6 +12,7 @@ import ( // RollappHooks event hooks for rollapp object (noalias) type RollappHooks interface { BeforeUpdateState(ctx sdk.Context, seqAddr string, rollappId string) error // Must be called when a rollapp's state changes + AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error // Must be called when a rollapp's state changes AfterStateFinalized(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error // Must be called when a rollapp's state changes FraudSubmitted(ctx sdk.Context, rollappID string, height uint64, seqAddr string) error RollappCreated(ctx sdk.Context, rollappID, alias string, creator sdk.AccAddress) error @@ -37,6 +38,16 @@ func (h MultiRollappHooks) BeforeUpdateState(ctx sdk.Context, seqAddr string, ro return nil } +func (h MultiRollappHooks) AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error { + for i := range h { + err := h[i].AfterUpdateState(ctx, rollappID, stateInfo) + if err != nil { + return err + } + } + return nil +} + func (h MultiRollappHooks) AfterStateFinalized(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error { for i := range h { err := h[i].AfterStateFinalized(ctx, rollappID, stateInfo) @@ -74,6 +85,7 @@ func (StubRollappCreatedHooks) RollappCreated(sdk.Context, string, string, sdk.A return nil } func (StubRollappCreatedHooks) BeforeUpdateState(sdk.Context, string, string) error { return nil } +func (StubRollappCreatedHooks) AfterUpdateState(sdk.Context, string, *StateInfo) error { return nil } func (StubRollappCreatedHooks) FraudSubmitted(sdk.Context, string, uint64, string) error { return nil } func (StubRollappCreatedHooks) AfterStateFinalized(sdk.Context, string, *StateInfo) error { return nil From 6c12f5640f33a73a70c5b5974067e3c2eb9b34cd Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:33:40 +0100 Subject: [PATCH 08/87] adding AfterUpdateState hook --- x/lightclient/ante/ibc_msg_create_client.go | 34 +++------ .../ante/ibc_msg_submit_misbehaviour.go | 5 +- x/lightclient/ante/ibc_msg_update_client.go | 76 ++++++++++--------- x/lightclient/ante/ibc_msgs.go | 18 ++--- x/lightclient/keeper/hook_listener.go | 60 +++++++++++++++ x/lightclient/keeper/keeper.go | 29 ++++++- x/lightclient/post/ibc_msg_create_client.go | 28 ++++--- x/lightclient/post/ibc_msgs.go | 1 - x/lightclient/types/expected_keepers.go | 16 ++++ x/lightclient/types/keys.go | 12 ++- x/lightclient/types/state.go | 55 ++++++++++++++ x/rollapp/keeper/msg_server_update_state.go | 2 +- 12 files changed, 246 insertions(+), 90 deletions(-) create mode 100644 x/lightclient/keeper/hook_listener.go create mode 100644 x/lightclient/types/expected_keepers.go create mode 100644 x/lightclient/types/state.go diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index 09434047c..fe7cf3372 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -1,12 +1,11 @@ package ante import ( - "bytes" - sdk "github.com/cosmos/cosmos-sdk/types" 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/dymension/v3/x/lightclient/types" ) func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibcclienttypes.MsgCreateClient) { @@ -17,13 +16,13 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli } // Only tendermint client types can be set as canonical light client if clientState.ClientType() == exported.Tendermint { - // Cast client state to tendermint client state - we need this to check the chain id and latest height - tendmermintClientState, ok := clientState.(*ibctm.ClientState) + // Cast client state to tendermint client state - we need this to get the chain id and state height + tmClientState, ok := clientState.(*ibctm.ClientState) if !ok { return } // Check if rollapp exists for given chain id - rollappID := tendmermintClientState.ChainId + rollappID := tmClientState.ChainId _, found := i.rollappKeeper.GetRollapp(ctx, rollappID) if !found { return @@ -34,7 +33,7 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli return } // Check if there are existing block descriptors for the given height of client state - height := tendmermintClientState.GetLatestHeight() + height := tmClientState.GetLatestHeight() stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) if err != nil { return @@ -45,27 +44,16 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli if err != nil { return } - // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp - tendermintConsensusState, ok := consensusState.(*ibctm.ConsensusState) + // Cast consensus state to tendermint consensus state - we need this to get the state root and timestamp and nextValHash + tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) if !ok { return } - // Check if block descriptor state root matches tendermint consensus state root - if bytes.Equal(blockDescriptor.StateRoot, tendermintConsensusState.GetRoot().GetHash()) { - return + // Check if the consensus state is compatible with the block descriptor state + err = types.StateCompatible(*tmConsensusState, blockDescriptor) + if err != nil { + return // In case of incompatibility, the client will be created but not set as canonical } - // Check if block descriptor timestamp matches tendermint consensus state timestamp - // TODO: Add this field to BD struct - // if blockDescriptor.Timestamp != tendermintConsensusState.GetTimestamp() { - // return - // } - - // Check if the validator set hash matches the sequencer - // if len(tendermintConsensusState.GetNextValidators().Validators) == 1 && tendermintConsensusState.GetNextValidators().Validators[0].Address { - // return - // } - - // Check if the nextValidatorHash matches the sequencer for h+1 // Generate client id and begin canonical light client registration by storing it in transient store. // Will be confirmed after the client is created in post handler. diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go index 9ebb8d923..88620b2a5 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go @@ -12,15 +12,16 @@ func (i IBCMessagesDecorator) HandleMsgSubmitMisbehaviour(ctx sdk.Context, msg * if !found { return nil } - // Cast client state to tendermint client state - we need this to check the chain id + // Cast client state to tendermint client state - we need this to get the chain id tendmermintClientState, ok := clientState.(*ibctm.ClientState) if !ok { return nil } - // Check if the the client is the canonical client for the rollapp + // Check if the client is the canonical client for a rollapp rollappID := tendmermintClientState.ChainId canonicalClient, found := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) if !found || canonicalClient != msg.ClientId { + // The client is not a rollapp's canonical client. Continue with default behaviour. return nil } return errorsmod.Wrap(ibcclienttypes.ErrInvalidClient, "cannot submit misbehavour for a canonical client") diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 70a7438c4..5c244ba20 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -1,12 +1,11 @@ package ante import ( - "bytes" - - errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" 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/dymension/v3/x/lightclient/types" ) func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibcclienttypes.MsgUpdateClient) error { @@ -14,41 +13,44 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli if !found { return nil } - // Cast client state to tendermint client state - we need this to check the chain id - tendmermintClientState, ok := clientState.(*ibctm.ClientState) - if !ok { - return nil - } - // Check if the the client is the canonical client for the rollapp - rollappID := tendmermintClientState.ChainId - canonicalClient, found := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) - if !found || canonicalClient != msg.ClientId { - return nil - } - // Check if there are existing block descriptors for the given height of client state - if not return and optimistically accept the update - height := tendmermintClientState.GetLatestHeight() - stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) - if err != nil { - return nil - } - blockDescriptor := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] - clientMessage, err := ibcclienttypes.UnpackClientMessage(msg.ClientMessage) - if err != nil { - return nil - } - header, ok := clientMessage.(*ibctm.Header) - if !ok { - return nil - } - // Check if block descriptor state root matches tendermint header app hash - if !bytes.Equal(blockDescriptor.StateRoot, header.Header.AppHash) { - return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") + // Only continue if the client is a tendermint client as rollapp only supports tendermint clients as canoncial clients + if clientState.ClientType() == exported.Tendermint { + // Cast client state to tendermint client state - we need this to get the chain id(rollapp id) + tmClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return nil + } + // Check if the client is the canonical client for the rollapp + rollappID := tmClientState.ChainId + canonicalClient, found := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) + if !found || canonicalClient != msg.ClientId { + return nil // The client is not a rollapp's canonical client. Continue with default behaviour. + } + clientMessage, err := ibcclienttypes.UnpackClientMessage(msg.ClientMessage) + if err != nil { + return nil + } + header, ok := clientMessage.(*ibctm.Header) + if !ok { + return nil + } + // Check if there are existing block descriptors for the given height of client state + height := header.GetHeight() + stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) + if err != nil { + // No BDs found for given height. + // Will accept the update optimistically + // But also save the blockProposer address with the height for future verification + blockProposer := header.Header.ProposerAddress + i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), string(blockProposer)) + return nil + } + // Ensure that the ibc header is compatible with the existing rollapp state + err = types.HeaderCompatible(*header, stateInfo) + if err != nil { + return err + } } - // TODO: Check if block descriptor timestamp matches tendermint header timestamp - // Check if the validator set hash matches the sequencer - // if len(header.ValidatorSet.Validators) == 1 && header.ValidatorSet.Validators[0].Address { - - // } return nil } diff --git a/x/lightclient/ante/ibc_msgs.go b/x/lightclient/ante/ibc_msgs.go index a9ca5e323..f6a8f9d36 100644 --- a/x/lightclient/ante/ibc_msgs.go +++ b/x/lightclient/ante/ibc_msgs.go @@ -5,27 +5,25 @@ import ( ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" - rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) var _ sdk.AnteDecorator = IBCMessagesDecorator{} -type RollappKeeperExpected interface { - GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) - FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (rollapptypes.StateInfo, error) -} - type IBCMessagesDecorator struct { ibcKeeper ibckeeper.Keeper - rollappKeeper RollappKeeperExpected + rollappKeeper types.RollappKeeperExpected lightClientKeeper keeper.Keeper } -func NewIBCMessagesDecorator() IBCMessagesDecorator { - return IBCMessagesDecorator{} +func NewIBCMessagesDecorator(k keeper.Keeper, ibcK ibckeeper.Keeper, rk types.RollappKeeperExpected) IBCMessagesDecorator { + return IBCMessagesDecorator{ + ibcKeeper: ibcK, + rollappKeeper: rk, + lightClientKeeper: k, + } } -// AnteHandle implements types.AnteDecorator. func (i IBCMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { msgs := tx.GetMsgs() for _, m := range msgs { diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go new file mode 100644 index 000000000..fa976a73b --- /dev/null +++ b/x/lightclient/keeper/hook_listener.go @@ -0,0 +1,60 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "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/dymensionxyz/dymension/v3/x/lightclient/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +var _ rollapptypes.RollappHooks = rollappHook{} + +// Hooks wrapper struct for rollapp keeper. +type rollappHook struct { + rollapptypes.StubRollappCreatedHooks + k Keeper +} + +// RollappHooks returns the wrapper struct. +func (k Keeper) RollappHooks() rollapptypes.RollappHooks { + return rollappHook{k: k} +} + +func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) error { + canonicalClient, found := hook.k.GetCanonicalClient(ctx, rollappId) + if !found { + return nil + } + bds := stateInfo.GetBDs() + for _, bd := range bds.GetBD() { + // Check if any optimistic updates were made for the given height + signer, found := hook.k.GetConsensusStateSigner(ctx, canonicalClient, bd.GetHeight()) + if !found { + continue + } + height := ibcclienttypes.NewHeight(1, bd.GetHeight()) + consensusState, found := hook.k.ibcKeeper.ClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) + if !found { + return nil + } + // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash + tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) + if !ok { + return nil + } + + err := types.StateCompatible(*tmConsensusState, bd) + if err != nil { + // If the state is not compatible, + // Take this state update as source of truth over the IBC update + // Punish the block proposer of the IBC signed header + sequencerAddr := signer // todo: signer addr is sent from tm. so will be valconsaddr(?). check and then transform to valid address + err = hook.k.sequencerKeeper.SlashAndJailFraud(ctx, sequencerAddr) + if err != nil { + return err + } + } + } + return nil +} diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 1bbe43407..da065ed88 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -8,23 +8,30 @@ import ( "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) type Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + ibcKeeper ibckeeper.Keeper + sequencerKeeper types.SequencerKeeperExpected } func NewKeeper( cdc codec.BinaryCodec, storeKey storetypes.StoreKey, + ibcKeeper ibckeeper.Keeper, + sequencerKeeper types.SequencerKeeperExpected, ) *Keeper { k := &Keeper{ - cdc: cdc, - storeKey: storeKey, + cdc: cdc, + storeKey: storeKey, + ibcKeeper: ibcKeeper, + sequencerKeeper: sequencerKeeper, } return k } @@ -65,3 +72,17 @@ func (k Keeper) ClearCanonicalLightClientRegistration(ctx sdk.Context, rollappId store := ctx.KVStore(k.storeKey) store.Delete(types.CanonicalLightClientRegistrationKey(rollappId)) } + +func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencer string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), []byte(sequencer)) +} + +func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64) (string, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ConsensusStateSignerKeyByClientID(clientID, height)) + if bz == nil { + return "", false + } + return string(bz), true +} diff --git a/x/lightclient/post/ibc_msg_create_client.go b/x/lightclient/post/ibc_msg_create_client.go index cbe9af844..b2adcf78a 100644 --- a/x/lightclient/post/ibc_msg_create_client.go +++ b/x/lightclient/post/ibc_msg_create_client.go @@ -7,23 +7,29 @@ import ( ) func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibcclienttypes.MsgCreateClient, success bool) { + clientState, err := ibcclienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return + } + // Parse client state to tendermint client state to get the chain id + tmClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return + } + rollappID := tmClientState.ChainId + // If tx failed, no need to proceed with canonical client registration if success { - clientState, err := ibcclienttypes.UnpackClientState(msg.ClientState) - if err != nil { - return - } - tendmermintClientState, ok := clientState.(*ibctm.ClientState) - if !ok { - return - } - rollappID := tendmermintClientState.ChainId - nextClientID, registrationFound := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) + // Check if a client registration is in progress + nextClientID, registrationFound := i.lightClientKeeper.GetCanonicalLightClientRegistration(ctx, rollappID) if registrationFound { + // Check if the client was successfully created with given clientID _, clientFound := i.ibcKeeper.ClientKeeper.GetClientState(ctx, nextClientID) if clientFound { + // Set the client as the canonical client for the rollapp i.lightClientKeeper.SetCanonicalClient(ctx, rollappID, nextClientID) } - i.lightClientKeeper.ClearCanonicalLightClientRegistration(ctx, rollappID) + } } + i.lightClientKeeper.ClearCanonicalLightClientRegistration(ctx, rollappID) } diff --git a/x/lightclient/post/ibc_msgs.go b/x/lightclient/post/ibc_msgs.go index 903738b7f..dbc41b925 100644 --- a/x/lightclient/post/ibc_msgs.go +++ b/x/lightclient/post/ibc_msgs.go @@ -18,7 +18,6 @@ func NewIBCMessagesDecorator() IBCMessagesDecorator { return IBCMessagesDecorator{} } -// PostHandle implements types.PostDecorator. func (i IBCMessagesDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, success bool, next sdk.PostHandler) (newCtx sdk.Context, err error) { msgs := tx.GetMsgs() for _, m := range msgs { diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go new file mode 100644 index 000000000..44abf392f --- /dev/null +++ b/x/lightclient/types/expected_keepers.go @@ -0,0 +1,16 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +type SequencerKeeperExpected interface { + SlashAndJailFraud(ctx sdk.Context, seqAddr string) error +} + +type RollappKeeperExpected interface { + GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) + FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (rollapptypes.StateInfo, error) +} diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go index 4ffcabb26..243a781e0 100644 --- a/x/lightclient/types/keys.go +++ b/x/lightclient/types/keys.go @@ -1,5 +1,9 @@ package types +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + const ( // ModuleName defines the module name ModuleName = "lightclient" @@ -16,7 +20,8 @@ const ( // KV Store var ( - RollappClientKey = []byte{0x01} + RollappClientKey = []byte{0x01} + ConsensusStateSignerKey = []byte{0x03} ) // Transient Store @@ -31,3 +36,8 @@ func CanonicalClientKey(rollappId string) []byte { func CanonicalLightClientRegistrationKey(rollappId string) []byte { return append(LightClientRegistrationKey, []byte(rollappId)...) } + +func ConsensusStateSignerKeyByClientID(clientID string, height uint64) []byte { + prefix := append([]byte(clientID), sdk.Uint64ToBigEndian(height)...) + return append(ConsensusStateSignerKey, prefix...) +} diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go new file mode 100644 index 000000000..d78d92ae3 --- /dev/null +++ b/x/lightclient/types/state.go @@ -0,0 +1,55 @@ +package types + +import ( + "bytes" + + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + + errorsmod "cosmossdk.io/errors" +) + +func StateCompatible(consensusState ibctm.ConsensusState, rollappBD rollapptypes.BlockDescriptor) error { + // Check if block descriptor state root matches tendermint consensus state root + // if bytes.Equal(rollappBD.StateRoot, consensusState.GetRoot().GetHash()) { + // return nil + // } + // Check if block descriptor timestamp matches tendermint consensus state timestamp + // if blockDescriptor.Timestamp != tendermintConsensusState.GetTimestamp() { + // return + // } + + // Check if the validator set hash matches the sequencer + // if len(tendermintConsensusState.GetNextValidators().Validators) == 1 && tendermintConsensusState.GetNextValidators().Validators[0].Address { + // return + // } + + // Check if the nextValidatorHash matches the sequencer for h+1 + // todo: pass in all the stateinfo so we can lookup h+1 block descriptor + return nil +} + +func HeaderCompatible(header ibctm.Header, stateInfo rollapptypes.StateInfo) error { + height := header.GetHeight() + currentHeaderBD := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] + // Check if block descriptor state root matches tendermint header app hash + if !bytes.Equal(currentHeaderBD.StateRoot, header.Header.AppHash) { + return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") + } + if currentHeaderBD.Timestamp.Equal(header.Header.Time) { + return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp") + } + // Check if the validator set hash matches the sequencer + if len(header.ValidatorSet.Validators) == 1 && (string(header.ValidatorSet.Validators[0].Address) == stateInfo.Sequencer) { // todo: do proper data transformation before checking + return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator set hash does not match the sequencer") + } + // Check if the nextValidatorHash matches the sequencer for h+1 + nextBlockDescriptor := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()+1] + //if nextBlockDescriptor is part of same state info, the same sequencer + if !bytes.Equal([]byte(stateInfo.Sequencer), header.Header.NextValidatorsHash) { // todo: do proper data transformation before checking + return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1") + } + + return nil +} diff --git a/x/rollapp/keeper/msg_server_update_state.go b/x/rollapp/keeper/msg_server_update_state.go index ebcc428a6..babe33e69 100644 --- a/x/rollapp/keeper/msg_server_update_state.go +++ b/x/rollapp/keeper/msg_server_update_state.go @@ -67,7 +67,7 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) // Write new state information to the store indexed by k.SetStateInfo(ctx, *stateInfo) - err = k.hooks.AfterUpdateState(ctx, msg.Creator, stateInfo) + err = k.hooks.AfterUpdateState(ctx, msg.RollappId, stateInfo) if err != nil { return nil, err } From bf6575a7566e8015f25c0f0ee4e7456870a64ea0 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 16 Aug 2024 13:13:24 +0530 Subject: [PATCH 09/87] refactoring the state check logic to also handle when nextBD is in a diff stateInfo --- x/lightclient/ante/ibc_msg_create_client.go | 35 +++++++++++- x/lightclient/ante/ibc_msg_update_client.go | 29 +++++++++- x/lightclient/keeper/hook_listener.go | 25 ++++++++- x/lightclient/types/expected_keepers.go | 1 + x/lightclient/types/state.go | 60 ++++++++++----------- 5 files changed, 114 insertions(+), 36 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index fe7cf3372..526f8b7fb 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -1,6 +1,8 @@ package ante import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -49,8 +51,39 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli if !ok { return } + // Convert timestamp from nanoseconds to time.Time + timestamp := time.Unix(0, int64(tmConsensusState.GetTimestamp())) + + ibcState := types.IBCState{ + Root: tmConsensusState.GetRoot().GetHash(), + Height: tmClientState.GetLatestHeight().GetRevisionHeight(), + Validator: []byte{}, // not sure if this info is available in the tendermint consensus state + NextValidatorsHash: tmConsensusState.NextValidatorsHash, + Timestamp: timestamp, + } + rollappState := types.RollappState{ + BlockSequencer: stateInfo.Sequencer, + BlockDescriptor: blockDescriptor, + } + // Check if bd for next block exists and is part of same state info + nextHeight := height.GetRevisionHeight() + 1 + if stateInfo.StartHeight+stateInfo.NumBlocks >= nextHeight { + rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[nextHeight-stateInfo.StartHeight] + rollappState.NextBlockSequencer = stateInfo.Sequencer + } else { + // nextBD doesnt exist in same stateInfo. So lookup in the next StateInfo + currentStateInfoIndex := stateInfo.GetIndex().Index + nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, currentStateInfoIndex+1) + if !found { + // if next state info doesnt exist, ignore this one condition when performing state compatibility check + rollappState.NextBlockSequencer = "" + } else { + rollappState.NextBlockSequencer = nextStateInfo.Sequencer + rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] + } + } // Check if the consensus state is compatible with the block descriptor state - err = types.StateCompatible(*tmConsensusState, blockDescriptor) + err = types.CheckCompatibility(ibcState, rollappState) if err != nil { return // In case of incompatibility, the client will be created but not set as canonical } diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 5c244ba20..6f9bc8fb1 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -45,8 +45,35 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), string(blockProposer)) return nil } + bd := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] + ibcState := types.IBCState{ + Root: header.Header.GetAppHash(), + Height: height.GetRevisionHeight(), + Validator: header.Header.ValidatorsHash, + NextValidatorsHash: header.Header.NextValidatorsHash, + Timestamp: header.Header.Time, + } + rollappState := types.RollappState{ + BlockSequencer: stateInfo.Sequencer, + BlockDescriptor: bd, + } + // Check that BD for next block exists + if height.GetRevisionHeight()-stateInfo.GetStartHeight()+1 < uint64(len(stateInfo.GetBDs().BD)) { + rollappState.NextBlockSequencer = stateInfo.Sequencer + rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()+1] + } else { + // next BD does not exist in this state info, check the next state info + nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) + if !found { + // if next state info does not exist, then we can't verify the next block valhash. So we ignore this check + rollappState.NextBlockSequencer = "" + } else { + rollappState.NextBlockSequencer = nextStateInfo.Sequencer + rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] + } + } // Ensure that the ibc header is compatible with the existing rollapp state - err = types.HeaderCompatible(*header, stateInfo) + err = types.CheckCompatibility(ibcState, rollappState) if err != nil { return err } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index fa976a73b..a7a59f605 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -1,6 +1,8 @@ package keeper import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" @@ -27,7 +29,7 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat return nil } bds := stateInfo.GetBDs() - for _, bd := range bds.GetBD() { + for i, bd := range bds.GetBD() { // Check if any optimistic updates were made for the given height signer, found := hook.k.GetConsensusStateSigner(ctx, canonicalClient, bd.GetHeight()) if !found { @@ -44,7 +46,26 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat return nil } - err := types.StateCompatible(*tmConsensusState, bd) + // Convert timestamp from nanoseconds to time.Time + timestamp := time.Unix(0, int64(tmConsensusState.GetTimestamp())) + + ibcState := types.IBCState{ + Root: tmConsensusState.GetRoot().GetHash(), + Height: bd.GetHeight(), + Validator: []byte(signer), + NextValidatorsHash: tmConsensusState.NextValidatorsHash, + Timestamp: timestamp, + } + rollappState := types.RollappState{ + BlockSequencer: stateInfo.Sequencer, + BlockDescriptor: bd, + } + // check if bd for next block exists + if i+1 < len(bds.GetBD()) { + rollappState.NextBlockSequencer = stateInfo.Sequencer + rollappState.NextBlockDescriptor = bds.GetBD()[i+1] + } + err := types.CheckCompatibility(ibcState, rollappState) if err != nil { // If the state is not compatible, // Take this state update as source of truth over the IBC update diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 44abf392f..dbc57e58b 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -13,4 +13,5 @@ type SequencerKeeperExpected interface { type RollappKeeperExpected interface { GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (rollapptypes.StateInfo, error) + GetStateInfo(ctx sdk.Context, rollappId string, index uint64) (val rollapptypes.StateInfo, found bool) } diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index d78d92ae3..1f625f0a6 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -2,54 +2,50 @@ package types import ( "bytes" + "time" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" errorsmod "cosmossdk.io/errors" ) -func StateCompatible(consensusState ibctm.ConsensusState, rollappBD rollapptypes.BlockDescriptor) error { - // Check if block descriptor state root matches tendermint consensus state root - // if bytes.Equal(rollappBD.StateRoot, consensusState.GetRoot().GetHash()) { - // return nil - // } - // Check if block descriptor timestamp matches tendermint consensus state timestamp - // if blockDescriptor.Timestamp != tendermintConsensusState.GetTimestamp() { - // return - // } - - // Check if the validator set hash matches the sequencer - // if len(tendermintConsensusState.GetNextValidators().Validators) == 1 && tendermintConsensusState.GetNextValidators().Validators[0].Address { - // return - // } - - // Check if the nextValidatorHash matches the sequencer for h+1 - // todo: pass in all the stateinfo so we can lookup h+1 block descriptor - return nil -} - -func HeaderCompatible(header ibctm.Header, stateInfo rollapptypes.StateInfo) error { - height := header.GetHeight() - currentHeaderBD := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] - // Check if block descriptor state root matches tendermint header app hash - if !bytes.Equal(currentHeaderBD.StateRoot, header.Header.AppHash) { +func CheckCompatibility(ibcState IBCState, raState RollappState) error { + // Check if block descriptor state root matches IBC header app hash + if !bytes.Equal(ibcState.Root, raState.BlockDescriptor.StateRoot) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") } - if currentHeaderBD.Timestamp.Equal(header.Header.Time) { + // Check if block descriptor timestamp matches IBC header timestamp + if ibcState.Timestamp.Equal(raState.BlockDescriptor.Timestamp) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp") } + // in case of msgcreateclinet validator info is not available // Check if the validator set hash matches the sequencer - if len(header.ValidatorSet.Validators) == 1 && (string(header.ValidatorSet.Validators[0].Address) == stateInfo.Sequencer) { // todo: do proper data transformation before checking + if len(ibcState.Validator) == 1 && bytes.Equal(ibcState.Validator, []byte(raState.BlockSequencer)) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator set hash does not match the sequencer") } + // Check if second block descriptor exists, if it doesnt, this one condition is accepted optimistically + if raState.NextBlockSequencer == "" { + return nil + } // Check if the nextValidatorHash matches the sequencer for h+1 - nextBlockDescriptor := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()+1] - //if nextBlockDescriptor is part of same state info, the same sequencer - if !bytes.Equal([]byte(stateInfo.Sequencer), header.Header.NextValidatorsHash) { // todo: do proper data transformation before checking + if !bytes.Equal(ibcState.NextValidatorsHash, []byte(raState.NextBlockSequencer)) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1") } - return nil } + +type IBCState struct { + Root []byte + Height uint64 + Validator []byte + NextValidatorsHash []byte + Timestamp time.Time +} + +type RollappState struct { + BlockSequencer string + BlockDescriptor rollapptypes.BlockDescriptor + NextBlockSequencer string + NextBlockDescriptor rollapptypes.BlockDescriptor +} From 5a2c3487d2601ce9f88597eae0f667cbafb6b329 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:06:35 +0530 Subject: [PATCH 10/87] app wiring --- app/ante/ante_options.go | 5 +++ app/ante/ante_test.go | 17 ++++----- app/ante/handlers.go | 3 ++ app/app.go | 10 +++--- app/keepers/keepers.go | 11 ++++++ app/keepers/keys.go | 4 ++- app/keepers/modules.go | 7 ++++ app/post/post.go | 39 +++++++++++++++++++++ x/lightclient/ante/ibc_msg_update_client.go | 3 +- x/lightclient/module.go | 7 ++-- x/lightclient/post/ibc_msgs.go | 7 ++-- x/lightclient/types/expected_keepers.go | 2 +- x/lightclient/types/state.go | 5 +-- 13 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 app/post/post.go diff --git a/app/ante/ante_options.go b/app/ante/ante_options.go index 3abd009b7..aee765fb2 100644 --- a/app/ante/ante_options.go +++ b/app/ante/ante_options.go @@ -6,6 +6,7 @@ import ( authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + lightclientkeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" rollappkeeper "github.com/dymensionxyz/dymension/v3/x/rollapp/keeper" ethante "github.com/evmos/ethermint/app/ante" @@ -26,6 +27,7 @@ type HandlerOptions struct { MaxTxGasWanted uint64 ExtensionOptionChecker ante.ExtensionOptionChecker RollappKeeper rollappkeeper.Keeper + LightClientKeeper *lightclientkeeper.Keeper } func (options HandlerOptions) validate() error { @@ -47,5 +49,8 @@ func (options HandlerOptions) validate() error { if options.TxFeesKeeper == nil { return errorsmod.Wrap(errortypes.ErrLogic, "tx fees keeper is required for AnteHandler") } + if options.LightClientKeeper == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "light client keeper is required for AnteHandler") + } return nil } diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go index d0c382bd5..3b4304f90 100644 --- a/app/ante/ante_test.go +++ b/app/ante/ante_test.go @@ -56,14 +56,15 @@ func (s *AnteTestSuite) SetupTestCheckTx(isCheckTx bool) { anteHandler, err := ante.NewAnteHandler( ante.HandlerOptions{ - AccountKeeper: &s.app.AccountKeeper, - BankKeeper: s.app.BankKeeper, - IBCKeeper: s.app.IBCKeeper, - EvmKeeper: s.app.EvmKeeper, - FeeMarketKeeper: s.app.FeeMarketKeeper, - TxFeesKeeper: s.app.TxFeesKeeper, - FeegrantKeeper: s.app.FeeGrantKeeper, - SignModeHandler: txConfig.SignModeHandler(), + AccountKeeper: &s.app.AccountKeeper, + BankKeeper: s.app.BankKeeper, + IBCKeeper: s.app.IBCKeeper, + EvmKeeper: s.app.EvmKeeper, + FeeMarketKeeper: s.app.FeeMarketKeeper, + TxFeesKeeper: s.app.TxFeesKeeper, + FeegrantKeeper: s.app.FeeGrantKeeper, + SignModeHandler: txConfig.SignModeHandler(), + LightClientKeeper: &s.app.LightClientKeeper, }, ) diff --git a/app/ante/handlers.go b/app/ante/handlers.go index a1b5dacf4..a63b9189d 100644 --- a/app/ante/handlers.go +++ b/app/ante/handlers.go @@ -12,6 +12,7 @@ import ( evmtypes "github.com/evmos/ethermint/x/evm/types" delayedack "github.com/dymensionxyz/dymension/v3/x/delayedack" + lightclientante "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" ) func newEthAnteHandler(options HandlerOptions) sdk.AnteHandler { @@ -70,6 +71,7 @@ func newLegacyCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler { // Note: signature verification uses EIP instead of the cosmos signature validator NewLegacyEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), ante.NewIncrementSequenceDecorator(options.AccountKeeper), + lightclientante.NewIBCMessagesDecorator(*options.LightClientKeeper, *options.IBCKeeper, options.RollappKeeper), delayedack.NewIBCProofHeightDecorator(), ibcante.NewRedundantRelayDecorator(options.IBCKeeper), ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper), @@ -107,6 +109,7 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), ante.NewIncrementSequenceDecorator(options.AccountKeeper), delayedack.NewIBCProofHeightDecorator(), + lightclientante.NewIBCMessagesDecorator(*options.LightClientKeeper, *options.IBCKeeper, options.RollappKeeper), ibcante.NewRedundantRelayDecorator(options.IBCKeeper), ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper), diff --git a/app/app.go b/app/app.go index a00df4a3b..90beda3d5 100644 --- a/app/app.go +++ b/app/app.go @@ -15,7 +15,6 @@ import ( simappparams "cosmossdk.io/simapp/params" "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/x/auth/posthandler" "github.com/dymensionxyz/dymension/v3/app/keepers" "github.com/dymensionxyz/dymension/v3/app/upgrades" @@ -58,6 +57,7 @@ import ( "github.com/dymensionxyz/dymension/v3/app/ante" appparams "github.com/dymensionxyz/dymension/v3/app/params" + "github.com/dymensionxyz/dymension/v3/app/post" packetforwardmiddleware "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward" packetforwardkeeper "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/keeper" @@ -217,13 +217,15 @@ func New( MaxTxGasWanted: maxGasWanted, ExtensionOptionChecker: nil, // uses default RollappKeeper: *app.RollappKeeper, + LightClientKeeper: &app.LightClientKeeper, }) if err != nil { panic(err) } - postHandler, err := posthandler.NewPostHandler( - posthandler.HandlerOptions{}, - ) + postHandler, err := post.NewPostHandler(post.HandlerOptions{ + IBCKeeper: app.IBCKeeper, + LightClientKeeper: &app.LightClientKeeper, + }) if err != nil { panic(fmt.Errorf("failed to create PostHandler: %w", err)) } diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index e90ef4792..e64be398e 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -81,6 +81,8 @@ import ( eibcmoduletypes "github.com/dymensionxyz/dymension/v3/x/eibc/types" incentiveskeeper "github.com/dymensionxyz/dymension/v3/x/incentives/keeper" incentivestypes "github.com/dymensionxyz/dymension/v3/x/incentives/types" + lightclientmodulekeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + lightclientmoduletypes "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollappmodule "github.com/dymensionxyz/dymension/v3/x/rollapp" rollappmodulekeeper "github.com/dymensionxyz/dymension/v3/x/rollapp/keeper" "github.com/dymensionxyz/dymension/v3/x/rollapp/transfergenesis" @@ -140,6 +142,7 @@ type AppKeepers struct { SponsorshipKeeper sponsorshipkeeper.Keeper StreamerKeeper streamermodulekeeper.Keeper EIBCKeeper eibckeeper.Keeper + LightClientKeeper lightclientmodulekeeper.Keeper DelayedAckKeeper delayedackkeeper.Keeper DenomMetadataKeeper *denommetadatamodulekeeper.Keeper @@ -356,6 +359,13 @@ func (a *AppKeepers) InitKeepers( a.RollappKeeper, ) + a.LightClientKeeper = *lightclientmodulekeeper.NewKeeper( + appCodec, + a.keys[lightclientmoduletypes.StoreKey], + *a.IBCKeeper, + a.SequencerKeeper, + ) + a.RollappKeeper.SetSequencerKeeper(a.SequencerKeeper) a.IncentivesKeeper = incentiveskeeper.NewKeeper( @@ -558,6 +568,7 @@ func (a *AppKeepers) SetupHooks() { a.SequencerKeeper.RollappHooks(), a.delayedAckMiddleware, a.StreamerKeeper.Hooks(), + a.LightClientKeeper.RollappHooks(), )) } diff --git a/app/keepers/keys.go b/app/keepers/keys.go index f499487e7..5e5f1b775 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -32,6 +32,7 @@ import ( delayedacktypes "github.com/dymensionxyz/dymension/v3/x/delayedack/types" eibcmoduletypes "github.com/dymensionxyz/dymension/v3/x/eibc/types" incentivestypes "github.com/dymensionxyz/dymension/v3/x/incentives/types" + lightcliendmoduletypes "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollappmoduletypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" sequencermoduletypes "github.com/dymensionxyz/dymension/v3/x/sequencer/types" sponsorshiptypes "github.com/dymensionxyz/dymension/v3/x/sponsorship/types" @@ -45,7 +46,7 @@ func (a *AppKeepers) GenerateKeys() { a.keys = KVStoreKeys // Define transient store keys - a.tkeys = sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey, feemarkettypes.TransientKey) + a.tkeys = sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey, feemarkettypes.TransientKey, lightcliendmoduletypes.TransientKey) // MemKeys are for information that is stored only in RAM. a.memKeys = sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -128,4 +129,5 @@ var KVStoreKeys = sdk.NewKVStoreKeys( poolmanagertypes.StoreKey, incentivestypes.StoreKey, txfeestypes.StoreKey, + lightcliendmoduletypes.StoreKey, ) diff --git a/app/keepers/modules.go b/app/keepers/modules.go index c95d96acd..ea0c8dd2a 100644 --- a/app/keepers/modules.go +++ b/app/keepers/modules.go @@ -89,6 +89,8 @@ import ( incentivestypes "github.com/dymensionxyz/dymension/v3/x/incentives/types" "github.com/dymensionxyz/dymension/v3/x/rollapp" + lightclientmodule "github.com/dymensionxyz/dymension/v3/x/lightclient" + lightclientmoduletypes "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollappmoduleclient "github.com/dymensionxyz/dymension/v3/x/rollapp/client" rollappmoduletypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" "github.com/dymensionxyz/dymension/v3/x/sequencer" @@ -144,6 +146,7 @@ var ModuleBasics = module.NewBasicManager( packetforward.AppModuleBasic{}, delayedack.AppModuleBasic{}, eibc.AppModuleBasic{}, + lightclientmodule.AppModuleBasic{}, // Ethermint modules evm.AppModuleBasic{}, @@ -195,6 +198,7 @@ func (a *AppKeepers) SetupModules( delayedackmodule.NewAppModule(appCodec, a.DelayedAckKeeper), denommetadatamodule.NewAppModule(a.DenomMetadataKeeper, *a.EvmKeeper, a.BankKeeper), eibcmodule.NewAppModule(appCodec, a.EIBCKeeper, a.AccountKeeper, a.BankKeeper), + lightclientmodule.NewAppModule(appCodec, a.LightClientKeeper), // Ethermint app modules evm.NewAppModule(a.EvmKeeper, a.AccountKeeper, a.BankKeeper, a.GetSubspace(evmtypes.ModuleName).WithKeyTable(evmtypes.ParamKeyTable())), @@ -280,6 +284,7 @@ var BeginBlockers = []string{ incentivestypes.ModuleName, txfeestypes.ModuleName, consensusparamtypes.ModuleName, + lightclientmoduletypes.ModuleName, } var EndBlockers = []string{ @@ -318,6 +323,7 @@ var EndBlockers = []string{ incentivestypes.ModuleName, txfeestypes.ModuleName, consensusparamtypes.ModuleName, + lightclientmoduletypes.ModuleName, } var InitGenesis = []string{ @@ -356,4 +362,5 @@ var InitGenesis = []string{ incentivestypes.ModuleName, txfeestypes.ModuleName, consensusparamtypes.ModuleName, + lightclientmoduletypes.ModuleName, } diff --git a/app/post/post.go b/app/post/post.go new file mode 100644 index 000000000..268675b75 --- /dev/null +++ b/app/post/post.go @@ -0,0 +1,39 @@ +package post + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + lightclientkeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + + lightclientpost "github.com/dymensionxyz/dymension/v3/x/lightclient/post" +) + +// HandlerOptions are the options required for constructing a default SDK PostHandler. +type HandlerOptions struct { + IBCKeeper *ibckeeper.Keeper + LightClientKeeper *lightclientkeeper.Keeper +} + +// NewPostHandler returns an empty PostHandler chain. +func NewPostHandler(options HandlerOptions) (sdk.PostHandler, error) { + if err := options.validate(); err != nil { + return nil, err + } + postDecorators := []sdk.PostDecorator{ + lightclientpost.NewIBCMessagesDecorator(*options.LightClientKeeper, *options.IBCKeeper), + } + + return sdk.ChainPostDecorators(postDecorators...), nil +} + +func (options HandlerOptions) validate() error { + if options.IBCKeeper == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "ibc keeper is required for PostHandler") + } + if options.LightClientKeeper == nil { + return errorsmod.Wrap(errortypes.ErrLogic, "light client keeper is required for PostHandler") + } + return nil +} diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 6f9bc8fb1..695701fff 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -46,10 +46,11 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli return nil } bd := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] + ibcState := types.IBCState{ Root: header.Header.GetAppHash(), Height: height.GetRevisionHeight(), - Validator: header.Header.ValidatorsHash, + Validator: header.Header.ProposerAddress, NextValidatorsHash: header.Header.NextValidatorsHash, Timestamp: header.Header.Time, } diff --git a/x/lightclient/module.go b/x/lightclient/module.go index 189b853bc..3ae5139db 100644 --- a/x/lightclient/module.go +++ b/x/lightclient/module.go @@ -13,8 +13,7 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - "github.com/dymensionxyz/dymension/v3/x/eibc/client/cli" - "github.com/dymensionxyz/dymension/v3/x/eibc/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) @@ -71,12 +70,12 @@ func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *r // GetTxCmd returns the capability module's root tx command. func (a AppModuleBasic) GetTxCmd() *cobra.Command { - return cli.GetTxCmd() + return nil } // GetQueryCmd returns the capability module's root query command. func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return cli.GetQueryCmd(types.StoreKey) + return nil //cli.GetQueryCmd(types.StoreKey) } // ---------------------------------------------------------------------------- diff --git a/x/lightclient/post/ibc_msgs.go b/x/lightclient/post/ibc_msgs.go index dbc41b925..7e5095648 100644 --- a/x/lightclient/post/ibc_msgs.go +++ b/x/lightclient/post/ibc_msgs.go @@ -14,8 +14,11 @@ type IBCMessagesDecorator struct { lightClientKeeper keeper.Keeper } -func NewIBCMessagesDecorator() IBCMessagesDecorator { - return IBCMessagesDecorator{} +func NewIBCMessagesDecorator(k keeper.Keeper, ibcK ibckeeper.Keeper) IBCMessagesDecorator { + return IBCMessagesDecorator{ + ibcKeeper: ibcK, + lightClientKeeper: k, + } } func (i IBCMessagesDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, success bool, next sdk.PostHandler) (newCtx sdk.Context, err error) { diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index dbc57e58b..6625342e7 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -12,6 +12,6 @@ type SequencerKeeperExpected interface { type RollappKeeperExpected interface { GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) - FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (rollapptypes.StateInfo, error) + FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (*rollapptypes.StateInfo, error) GetStateInfo(ctx sdk.Context, rollappId string, index uint64) (val rollapptypes.StateInfo, found bool) } diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 1f625f0a6..db7855007 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -4,10 +4,10 @@ import ( "bytes" "time" + errorsmod "cosmossdk.io/errors" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" - - errorsmod "cosmossdk.io/errors" ) func CheckCompatibility(ibcState IBCState, raState RollappState) error { @@ -28,6 +28,7 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { if raState.NextBlockSequencer == "" { return nil } + // Check if the nextValidatorHash matches the sequencer for h+1 if !bytes.Equal(ibcState.NextValidatorsHash, []byte(raState.NextBlockSequencer)) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1") From 7315362baafcd4dc2cd99dfd61a0828b6f708587 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:12:34 +0530 Subject: [PATCH 11/87] adding migrations --- app/upgrades/v4/constants.go | 2 ++ app/upgrades/v4/upgrade.go | 23 +++++++++++++++++++++ x/lightclient/ante/ibc_msg_create_client.go | 3 +-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/app/upgrades/v4/constants.go b/app/upgrades/v4/constants.go index dfa8303f1..18cd32e53 100644 --- a/app/upgrades/v4/constants.go +++ b/app/upgrades/v4/constants.go @@ -5,6 +5,7 @@ import ( consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" "github.com/dymensionxyz/dymension/v3/app/upgrades" + lightclienttypes "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) const ( @@ -18,6 +19,7 @@ var Upgrade = upgrades.Upgrade{ Added: []string{ consensustypes.ModuleName, crisistypes.ModuleName, + lightclienttypes.ModuleName, }, }, } diff --git a/app/upgrades/v4/upgrade.go b/app/upgrades/v4/upgrade.go index f80548933..d7d3556f5 100644 --- a/app/upgrades/v4/upgrade.go +++ b/app/upgrades/v4/upgrade.go @@ -17,6 +17,8 @@ import ( slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibcchannelkeeper "github.com/cosmos/ibc-go/v7/modules/core/04-channel/keeper" evmtypes "github.com/evmos/ethermint/x/evm/types" feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" @@ -26,6 +28,7 @@ import ( "github.com/dymensionxyz/dymension/v3/app/upgrades" delayedackkeeper "github.com/dymensionxyz/dymension/v3/x/delayedack/keeper" delayedacktypes "github.com/dymensionxyz/dymension/v3/x/delayedack/types" + lightclientkeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" rollappkeeper "github.com/dymensionxyz/dymension/v3/x/rollapp/keeper" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" sequencerkeeper "github.com/dymensionxyz/dymension/v3/x/sequencer/keeper" @@ -49,6 +52,7 @@ func CreateUpgradeHandler( return nil, err } migrateSequencers(ctx, keepers.SequencerKeeper) + migrateRollappLightClients(ctx, keepers.RollappKeeper, keepers.LightClientKeeper, keepers.IBCKeeper.ChannelKeeper) // TODO: create rollapp gauges for each existing rollapp @@ -132,6 +136,25 @@ func migrateSequencers(ctx sdk.Context, sequencerkeeper sequencerkeeper.Keeper) } } +func migrateRollappLightClients(ctx sdk.Context, rollappkeeper *rollappkeeper.Keeper, lightClientKeeper lightclientkeeper.Keeper, ibcChannelKeeper ibcchannelkeeper.Keeper) { + list := rollappkeeper.GetAllRollapps(ctx) + for _, rollapp := range list { + // check if the rollapp has a canonical channel already + if rollapp.ChannelId == "" { + return + } + // get the client ID the channel belongs to + _, connection, err := ibcChannelKeeper.GetChannelConnection(ctx, ibctransfertypes.PortID, rollapp.ChannelId) + if err != nil { + // if could not find a connection, skip the cannonical client assignment + return + } + clientID := connection.GetClientID() + // todo: should this be canonical client? or candidate client? + lightClientKeeper.SetCanonicalClient(ctx, rollapp.RollappId, clientID) + } +} + func ConvertOldRollappToNew(oldRollapp rollapptypes.Rollapp) rollapptypes.Rollapp { bech32Prefix := oldRollapp.RollappId[:5] return rollapptypes.Rollapp{ diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index 526f8b7fb..1d2de2fff 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -75,8 +75,7 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli currentStateInfoIndex := stateInfo.GetIndex().Index nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, currentStateInfoIndex+1) if !found { - // if next state info doesnt exist, ignore this one condition when performing state compatibility check - rollappState.NextBlockSequencer = "" + return // There is no BD for h+1, so we can't verify the next block valhash. So we cant mark this client as canonical } else { rollappState.NextBlockSequencer = nextStateInfo.Sequencer rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] From 46638781d09d4b7596ef843a5a10dee6129362ef Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:55:54 +0530 Subject: [PATCH 12/87] implementing ibc msg channel open ack --- .../ante/ibc_msg_channel_open_ack.go | 36 +++++++++++++++++++ x/lightclient/ante/ibc_msgs.go | 5 +++ x/lightclient/keeper/keeper.go | 14 ++++++-- x/lightclient/types/expected_keepers.go | 1 + x/lightclient/types/keys.go | 11 ++++-- 5 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 x/lightclient/ante/ibc_msg_channel_open_ack.go diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack.go b/x/lightclient/ante/ibc_msg_channel_open_ack.go new file mode 100644 index 000000000..af8a43b95 --- /dev/null +++ b/x/lightclient/ante/ibc_msg_channel_open_ack.go @@ -0,0 +1,36 @@ +package ante + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" +) + +func (i IBCMessagesDecorator) HandleMsgChannelOpenAck(ctx sdk.Context, msg *ibcchanneltypes.MsgChannelOpenAck) error { + if msg.PortId != ibctransfertypes.PortID { // We only care about transfer channels to mark them as canonical + return nil + } + // Check if this channel is being opened on a known canonical client + _, connection, err := i.ibcKeeper.ChannelKeeper.GetChannelConnection(ctx, msg.PortId, msg.ChannelId) + if err != nil { + return err + } + rollappID, found := i.lightClientKeeper.GetRollappForClientID(ctx, connection.GetClientID()) + if found { + // Check if canon channel already exists for rollapp, if yes, return err + rollapp, found := i.rollappKeeper.GetRollapp(ctx, rollappID) + if !found { + return nil + } + if rollapp.ChannelId != "" { + // canonical channel already exists, return error + return errorsmod.Wrap(ibcchanneltypes.ErrChannelExists, "cannot create a new channel when a canonical channel already exists for the rollapp") + + } + // Set this channel as the canonical channel for the rollapp + rollapp.ChannelId = msg.ChannelId + i.rollappKeeper.SetRollapp(ctx, rollapp) + } + return nil +} diff --git a/x/lightclient/ante/ibc_msgs.go b/x/lightclient/ante/ibc_msgs.go index f6a8f9d36..ace09ea17 100644 --- a/x/lightclient/ante/ibc_msgs.go +++ b/x/lightclient/ante/ibc_msgs.go @@ -3,6 +3,7 @@ package ante import ( sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" @@ -38,6 +39,10 @@ func (i IBCMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo if err := i.HandleMsgUpdateClient(ctx, msg); err != nil { return ctx, err } + case *ibcchanneltypes.MsgChannelOpenAck: + if err := i.HandleMsgChannelOpenAck(ctx, msg); err != nil { + return ctx, err + } default: continue } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index da065ed88..a03039cbc 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -42,7 +42,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) GetCanonicalClient(ctx sdk.Context, rollappId string) (string, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.CanonicalClientKey(rollappId)) + bz := store.Get(types.RollappClientKey(rollappId)) if bz == nil { return "", false } @@ -51,7 +51,8 @@ func (k Keeper) GetCanonicalClient(ctx sdk.Context, rollappId string) (string, b func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID string) { store := ctx.KVStore(k.storeKey) - store.Set(types.CanonicalClientKey(rollappId), []byte(clientID)) + store.Set(types.RollappClientKey(rollappId), []byte(clientID)) + store.Set(types.CanonicalClientKey(clientID), []byte(rollappId)) } func (k Keeper) BeginCanonicalLightClientRegistration(ctx sdk.Context, rollappId string, clientID string) { @@ -86,3 +87,12 @@ func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height } return string(bz), true } + +func (k Keeper) GetRollappForClientID(ctx sdk.Context, clientID string) (string, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.CanonicalClientKey(clientID)) + if bz == nil { + return "", false + } + return string(bz), true +} diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 6625342e7..41ec557cc 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -14,4 +14,5 @@ type RollappKeeperExpected interface { GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (*rollapptypes.StateInfo, error) GetStateInfo(ctx sdk.Context, rollappId string, index uint64) (val rollapptypes.StateInfo, found bool) + SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rollapp) } diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go index 243a781e0..2e3a4f9ea 100644 --- a/x/lightclient/types/keys.go +++ b/x/lightclient/types/keys.go @@ -20,8 +20,9 @@ const ( // KV Store var ( - RollappClientKey = []byte{0x01} + rollappClientKey = []byte{0x01} ConsensusStateSignerKey = []byte{0x03} + canonicalClientKey = []byte{0x04} ) // Transient Store @@ -29,8 +30,8 @@ var ( LightClientRegistrationKey = []byte{0x02} ) -func CanonicalClientKey(rollappId string) []byte { - return append(RollappClientKey, []byte(rollappId)...) +func RollappClientKey(rollappId string) []byte { + return append(rollappClientKey, []byte(rollappId)...) } func CanonicalLightClientRegistrationKey(rollappId string) []byte { @@ -41,3 +42,7 @@ func ConsensusStateSignerKeyByClientID(clientID string, height uint64) []byte { prefix := append([]byte(clientID), sdk.Uint64ToBigEndian(height)...) return append(ConsensusStateSignerKey, prefix...) } + +func CanonicalClientKey(clientID string) []byte { + return append(canonicalClientKey, []byte(clientID)...) +} From cceb5f7828100eb62994a0a46c45e452d60cece4 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 19 Aug 2024 13:51:28 +0530 Subject: [PATCH 13/87] adding light client keeper testutil --- testutil/keeper/lightclient.go | 46 +++++++++++++++++++++++++ x/lightclient/keeper/hook_listener.go | 2 +- x/lightclient/keeper/keeper.go | 7 ++-- x/lightclient/types/expected_keepers.go | 5 +++ 4 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 testutil/keeper/lightclient.go diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go new file mode 100644 index 000000000..754ab6d55 --- /dev/null +++ b/testutil/keeper/lightclient.go @@ -0,0 +1,46 @@ +package keeper + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" + + cometbftdb "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + cometbftproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/stretchr/testify/require" +) + +func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { + storeKey := sdk.NewKVStoreKey(types.StoreKey) + memStoreKey := storetypes.NewMemoryStoreKey(types.StoreKey + "_mem") + transientStoreKey := storetypes.NewTransientStoreKey(types.TransientKey) + + db := cometbftdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) + stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) + stateStore.MountStoreWithDB(transientStoreKey, storetypes.StoreTypeTransient, nil) + require.NoError(t, stateStore.LoadLatestVersion()) + + registry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(registry) + + k := keeper.NewKeeper( + cdc, + storeKey, + nil, + nil, + ) + + ctx := sdk.NewContext(stateStore, cometbftproto.Header{}, false, log.NewNopLogger()) + + return k, ctx +} diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index a7a59f605..65dc31afb 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -36,7 +36,7 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat continue } height := ibcclienttypes.NewHeight(1, bd.GetHeight()) - consensusState, found := hook.k.ibcKeeper.ClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) + consensusState, found := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) if !found { return nil } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index a03039cbc..a69d31d4e 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) @@ -16,21 +15,21 @@ import ( type Keeper struct { cdc codec.BinaryCodec storeKey storetypes.StoreKey - ibcKeeper ibckeeper.Keeper + ibcClientKeeper types.IBCClientKeeper sequencerKeeper types.SequencerKeeperExpected } func NewKeeper( cdc codec.BinaryCodec, storeKey storetypes.StoreKey, - ibcKeeper ibckeeper.Keeper, + ibcKeeper types.IBCClientKeeper, sequencerKeeper types.SequencerKeeperExpected, ) *Keeper { k := &Keeper{ cdc: cdc, storeKey: storeKey, - ibcKeeper: ibcKeeper, + ibcClientKeeper: ibcKeeper, sequencerKeeper: sequencerKeeper, } return k diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 41ec557cc..69d63dac0 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -2,6 +2,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) @@ -16,3 +17,7 @@ type RollappKeeperExpected interface { GetStateInfo(ctx sdk.Context, rollappId string, index uint64) (val rollapptypes.StateInfo, found bool) SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rollapp) } + +type IBCClientKeeper interface { + GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) +} From 7f335889d86d08b7ff7771a4ce3a6751a08f80e4 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 19 Aug 2024 16:28:08 +0530 Subject: [PATCH 14/87] Adding `AfterUpdateState` hook test --- app/keepers/keepers.go | 2 +- testutil/keeper/lightclient.go | 47 ++++++- x/lightclient/keeper/hook_listener_test.go | 142 +++++++++++++++++++++ 3 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 x/lightclient/keeper/hook_listener_test.go diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index e64be398e..bb4989e8d 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -362,7 +362,7 @@ func (a *AppKeepers) InitKeepers( a.LightClientKeeper = *lightclientmodulekeeper.NewKeeper( appCodec, a.keys[lightclientmoduletypes.StoreKey], - *a.IBCKeeper, + a.IBCKeeper.ClientKeeper, a.SequencerKeeper, ) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 754ab6d55..6c6ea04cf 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -2,13 +2,17 @@ package keeper import ( "testing" + "time" + cmttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/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/dymension/v3/x/lightclient/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" @@ -33,14 +37,51 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { registry := codectypes.NewInterfaceRegistry() cdc := codec.NewProtoCodec(registry) + mockIBCKeeper := NewMockIBCClientKeeper() + mockSequencerKeeper := NewMockSequencerKeeper() k := keeper.NewKeeper( cdc, storeKey, - nil, - nil, + mockIBCKeeper, + mockSequencerKeeper, ) ctx := sdk.NewContext(stateStore, cometbftproto.Header{}, false, log.NewNopLogger()) return k, ctx } + +type MockIBCCLientKeeper struct { +} + +func NewMockIBCClientKeeper() *MockIBCCLientKeeper { + return &MockIBCCLientKeeper{} +} + +func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { + if clientID == "canon-client-id-no-state" { + return nil, false + } + if clientID == "canon-client-id" && height.GetRevisionHeight() == 2 { + val := cmttypes.NewMockPV() + pk, _ := val.GetPubKey() + cs := ibctm.NewConsensusState( + time.Now().UTC(), + commitmenttypes.NewMerkleRoot([]byte("test2")), + cmttypes.NewValidatorSet([]*cmttypes.Validator{cmttypes.NewValidator(pk, 1)}).Hash(), + ) + return cs, true + } + return nil, false +} + +type MockSequencerKeeper struct { +} + +func NewMockSequencerKeeper() *MockSequencerKeeper { + return &MockSequencerKeeper{} +} + +func (m *MockSequencerKeeper) SlashAndJailFraud(ctx sdk.Context, seqAddr string) error { + return nil +} diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go new file mode 100644 index 000000000..a4b594216 --- /dev/null +++ b/x/lightclient/keeper/hook_listener_test.go @@ -0,0 +1,142 @@ +package keeper_test + +import ( + "testing" + "time" + + keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/stretchr/testify/require" +) + +type testInput struct { + rollappId string + stateInfo *rollapptypes.StateInfo +} + +func TestAfterUpdateState(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client-but-no-state", "canon-client-id-no-state") + keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + + testCases := []struct { + name string + input testInput + }{ + { + name: "canonical client does not exist for rollapp", + input: testInput{ + rollappId: "rollapp-no-canon-client", + stateInfo: &rollapptypes.StateInfo{}, + }, + }, + { + name: "canonical client exists but the BDs are empty", + input: testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{}, + }, + }, + { + name: "canonical client exists but consensus state is not found", + input: testInput{ + rollappId: "rollapp-has-canon-client-but-no-state", + stateInfo: &rollapptypes.StateInfo{ + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + }, + }, + }, + }, + }, + { + name: "BD does not include next block in state info", + input: testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{ + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("test2"), + Timestamp: time.Now().Add(1).UTC(), + }, + }, + }, + }, + }, + }, + { + name: "both states are not compatible - slash the sequencer who signed", + input: testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{ + Sequencer: "sequencer1", + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("this is not compatible"), + Timestamp: time.Now().Add(1).UTC(), + }, + { + Height: 3, + StateRoot: []byte("test3"), + Timestamp: time.Now().Add(2).UTC(), + }, + }, + }, + }, + }, + }, + { + name: "state is compatible - happy path", + input: testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{ + Sequencer: "sequencer1", + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("test2"), + Timestamp: time.Now().Add(1).UTC(), + }, + { + Height: 3, + StateRoot: []byte("test3"), + Timestamp: time.Now().Add(2).UTC(), + }, + }, + }, + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := keeper.RollappHooks().AfterUpdateState(ctx, tc.input.rollappId, tc.input.stateInfo) + require.NoError(t, err) + }) + } +} From 454c514abd19f5447150c3c342a322acf5b30369 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 08:49:20 +0530 Subject: [PATCH 15/87] Create ibc_msg_submit_misbehaviour_test.go --- .../ante/ibc_msg_submit_misbehaviour_test.go | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go new file mode 100644 index 000000000..21b3e74f6 --- /dev/null +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go @@ -0,0 +1,59 @@ +package ante_test + +import ( + "testing" + + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + + keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" + "github.com/stretchr/testify/require" +) + +func TestHandleMsgSubmitMisbehaviour(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + rollappKeeper := NewMockRollappKeeper() + keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, keeper, rollappKeeper) + testCases := []struct { + name string + inputMsg ibcclienttypes.MsgSubmitMisbehaviour + err error + }{ + { + name: "Could not unpack light client state", + inputMsg: ibcclienttypes.MsgSubmitMisbehaviour{ + ClientId: "client-id", + Misbehaviour: nil, + }, + err: nil, + }, + { + name: "Client is a known canonical client for a rollapp", + inputMsg: ibcclienttypes.MsgSubmitMisbehaviour{ + ClientId: "client-id", + Misbehaviour: nil, + }, + err: nil, + }, + { + name: "Client is not a known canonical client", + inputMsg: ibcclienttypes.MsgSubmitMisbehaviour{ + ClientId: "client-id", + Misbehaviour: nil, + }, + err: nil, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := ibcMsgDecorator.HandleMsgSubmitMisbehaviour(ctx, &tc.inputMsg) + if tc.err != nil { + require.ErrorIs(t, err, tc.err) + } else { + require.NoError(t, err) + } + }) + } + +} From 4f7fff437ea2607cc281501fe2e4d108630c82c5 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 09:08:40 +0530 Subject: [PATCH 16/87] refactoring IBC keepers --- app/ante/handlers.go | 4 ++-- testutil/keeper/lightclient.go | 8 ++++++++ x/lightclient/ante/ibc_msg_channel_open_ack.go | 2 +- x/lightclient/ante/ibc_msg_create_client.go | 2 +- x/lightclient/ante/ibc_msg_submit_misbehaviour.go | 2 +- .../ante/ibc_msg_submit_misbehaviour_test.go | 12 +++++++----- x/lightclient/ante/ibc_msg_update_client.go | 2 +- x/lightclient/ante/ibc_msgs.go | 9 +++++---- x/lightclient/keeper/keeper.go | 4 ++-- x/lightclient/types/expected_keepers.go | 10 +++++++++- 10 files changed, 37 insertions(+), 18 deletions(-) diff --git a/app/ante/handlers.go b/app/ante/handlers.go index a63b9189d..444fe1b0f 100644 --- a/app/ante/handlers.go +++ b/app/ante/handlers.go @@ -71,7 +71,7 @@ func newLegacyCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler { // Note: signature verification uses EIP instead of the cosmos signature validator NewLegacyEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), ante.NewIncrementSequenceDecorator(options.AccountKeeper), - lightclientante.NewIBCMessagesDecorator(*options.LightClientKeeper, *options.IBCKeeper, options.RollappKeeper), + lightclientante.NewIBCMessagesDecorator(*options.LightClientKeeper, options.IBCKeeper.ClientKeeper, options.IBCKeeper.ChannelKeeper, options.RollappKeeper), delayedack.NewIBCProofHeightDecorator(), ibcante.NewRedundantRelayDecorator(options.IBCKeeper), ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper), @@ -109,7 +109,7 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), ante.NewIncrementSequenceDecorator(options.AccountKeeper), delayedack.NewIBCProofHeightDecorator(), - lightclientante.NewIBCMessagesDecorator(*options.LightClientKeeper, *options.IBCKeeper, options.RollappKeeper), + lightclientante.NewIBCMessagesDecorator(*options.LightClientKeeper, options.IBCKeeper.ClientKeeper, options.IBCKeeper.ChannelKeeper, options.RollappKeeper), ibcante.NewRedundantRelayDecorator(options.IBCKeeper), ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper), diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 6c6ea04cf..95a52eef2 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -75,6 +75,14 @@ func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID return nil, false } +func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { + return "" +} + +func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { + return nil, false +} + type MockSequencerKeeper struct { } diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack.go b/x/lightclient/ante/ibc_msg_channel_open_ack.go index af8a43b95..17b7c100a 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack.go @@ -12,7 +12,7 @@ func (i IBCMessagesDecorator) HandleMsgChannelOpenAck(ctx sdk.Context, msg *ibcc return nil } // Check if this channel is being opened on a known canonical client - _, connection, err := i.ibcKeeper.ChannelKeeper.GetChannelConnection(ctx, msg.PortId, msg.ChannelId) + _, connection, err := i.ibcChannelKeeper.GetChannelConnection(ctx, msg.PortId, msg.ChannelId) if err != nil { return err } diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index 1d2de2fff..b6d7b5f36 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -89,7 +89,7 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli // Generate client id and begin canonical light client registration by storing it in transient store. // Will be confirmed after the client is created in post handler. - nextClientID := i.ibcKeeper.ClientKeeper.GenerateClientIdentifier(ctx, exported.Tendermint) + nextClientID := i.ibcClientKeeper.GenerateClientIdentifier(ctx, exported.Tendermint) i.lightClientKeeper.BeginCanonicalLightClientRegistration(ctx, rollappID, nextClientID) } } diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go index 88620b2a5..99e77bc96 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go @@ -8,7 +8,7 @@ import ( ) func (i IBCMessagesDecorator) HandleMsgSubmitMisbehaviour(ctx sdk.Context, msg *ibcclienttypes.MsgSubmitMisbehaviour) error { - clientState, found := i.ibcKeeper.ClientKeeper.GetClientState(ctx, msg.ClientId) + clientState, found := i.ibcClientKeeper.GetClientState(ctx, msg.ClientId) if !found { return nil } diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go index 21b3e74f6..b892ae363 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go @@ -13,17 +13,19 @@ import ( func TestHandleMsgSubmitMisbehaviour(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) rollappKeeper := NewMockRollappKeeper() + ibcclientKeeper := NewMockIBCClientKeeper() + ibcchannelKeeper := NewMockIBCChannelKeeper() keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, keeper, rollappKeeper) + ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) testCases := []struct { name string inputMsg ibcclienttypes.MsgSubmitMisbehaviour err error }{ { - name: "Could not unpack light client state", + name: "Could not unpack light client state as tendermint client state", inputMsg: ibcclienttypes.MsgSubmitMisbehaviour{ - ClientId: "client-id", + ClientId: "non-tm-client-id", Misbehaviour: nil, }, err: nil, @@ -31,10 +33,10 @@ func TestHandleMsgSubmitMisbehaviour(t *testing.T) { { name: "Client is a known canonical client for a rollapp", inputMsg: ibcclienttypes.MsgSubmitMisbehaviour{ - ClientId: "client-id", + ClientId: "canon-client-id", Misbehaviour: nil, }, - err: nil, + err: ibcclienttypes.ErrInvalidClient, }, { name: "Client is not a known canonical client", diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 695701fff..f15e58a95 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -9,7 +9,7 @@ import ( ) func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibcclienttypes.MsgUpdateClient) error { - clientState, found := i.ibcKeeper.ClientKeeper.GetClientState(ctx, msg.ClientId) + clientState, found := i.ibcClientKeeper.GetClientState(ctx, msg.ClientId) if !found { return nil } diff --git a/x/lightclient/ante/ibc_msgs.go b/x/lightclient/ante/ibc_msgs.go index ace09ea17..5145f101c 100644 --- a/x/lightclient/ante/ibc_msgs.go +++ b/x/lightclient/ante/ibc_msgs.go @@ -4,7 +4,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) @@ -12,14 +11,16 @@ import ( var _ sdk.AnteDecorator = IBCMessagesDecorator{} type IBCMessagesDecorator struct { - ibcKeeper ibckeeper.Keeper + ibcClientKeeper types.IBCClientKeeperExpected + ibcChannelKeeper types.IBCChannelKeeperExpected rollappKeeper types.RollappKeeperExpected lightClientKeeper keeper.Keeper } -func NewIBCMessagesDecorator(k keeper.Keeper, ibcK ibckeeper.Keeper, rk types.RollappKeeperExpected) IBCMessagesDecorator { +func NewIBCMessagesDecorator(k keeper.Keeper, ibcClient types.IBCClientKeeperExpected, ibcChannel types.IBCChannelKeeperExpected, rk types.RollappKeeperExpected) IBCMessagesDecorator { return IBCMessagesDecorator{ - ibcKeeper: ibcK, + ibcClientKeeper: ibcClient, + ibcChannelKeeper: ibcChannel, rollappKeeper: rk, lightClientKeeper: k, } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index a69d31d4e..72c0f0c3b 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -15,14 +15,14 @@ import ( type Keeper struct { cdc codec.BinaryCodec storeKey storetypes.StoreKey - ibcClientKeeper types.IBCClientKeeper + ibcClientKeeper types.IBCClientKeeperExpected sequencerKeeper types.SequencerKeeperExpected } func NewKeeper( cdc codec.BinaryCodec, storeKey storetypes.StoreKey, - ibcKeeper types.IBCClientKeeper, + ibcKeeper types.IBCClientKeeperExpected, sequencerKeeper types.SequencerKeeperExpected, ) *Keeper { diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 69d63dac0..b78325300 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -4,6 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) @@ -18,6 +19,13 @@ type RollappKeeperExpected interface { SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rollapp) } -type IBCClientKeeper interface { +type IBCClientKeeperExpected interface { GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) + GenerateClientIdentifier(ctx sdk.Context, clientType string) string + GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) +} + +type IBCChannelKeeperExpected interface { + GetChannel(ctx sdk.Context, portID, channelID string) (channel ibcchanneltypes.Channel, found bool) + GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) } From 03072c56d75d8930db4f2e717a24e3578ef1a77b Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 09:32:21 +0530 Subject: [PATCH 17/87] implementing ibc_msg_channel_open_ack test --- .../ante/ibc_msg_channel_open_ack_test.go | 78 +++++++++++++ x/lightclient/ante/ibc_msgs_test.go | 107 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 x/lightclient/ante/ibc_msg_channel_open_ack_test.go create mode 100644 x/lightclient/ante/ibc_msgs_test.go diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go new file mode 100644 index 000000000..09e4cc023 --- /dev/null +++ b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go @@ -0,0 +1,78 @@ +package ante_test + +import ( + "testing" + + ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" + "github.com/stretchr/testify/require" +) + +func TestHandleMsgChannelOpenAck(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + rollappKeeper := NewMockRollappKeeper() + ibcclientKeeper := NewMockIBCClientKeeper() + ibcchannelKeeper := NewMockIBCChannelKeeper() + keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + keeper.SetCanonicalClient(ctx, "rollapp-no-canon-channel", "canon-client-id-2") + ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) + testCases := []struct { + name string + inputMsg ibcchanneltypes.MsgChannelOpenAck + err error + canonClientSet bool + }{ + { + name: "port id is not transfer port", + inputMsg: ibcchanneltypes.MsgChannelOpenAck{ + PortId: "not-transfer-port", + ChannelId: "channel-id", + }, + err: nil, + canonClientSet: false, + }, + { + name: "channel not on a canonical client", + inputMsg: ibcchanneltypes.MsgChannelOpenAck{ + PortId: "transfer", + ChannelId: "non-canon-channel-id", + }, + err: nil, + canonClientSet: false, + }, + { + name: "canonical channel already exists for rollapp", + inputMsg: ibcchanneltypes.MsgChannelOpenAck{ + PortId: "transfer", + ChannelId: "new-channel-on-canon-client", + }, + err: ibcchanneltypes.ErrChannelExists, + canonClientSet: false, + }, + { + name: "canonical channel does not exist - set new channel as canonical", + inputMsg: ibcchanneltypes.MsgChannelOpenAck{ + PortId: "transfer", + ChannelId: "first-channel-on-canon-client", + }, + err: nil, + canonClientSet: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := ibcMsgDecorator.HandleMsgChannelOpenAck(ctx, &tc.inputMsg) + if tc.err != nil { + require.ErrorIs(t, err, tc.err) + } else { + require.NoError(t, err) + } + if tc.canonClientSet { + rollapp, found := rollappKeeper.GetRollapp(ctx, "rollapp-no-canon-channel") + require.True(t, found) + require.Equal(t, tc.inputMsg.ChannelId, rollapp.ChannelId) + } + }) + } +} diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go new file mode 100644 index 000000000..bd4f09562 --- /dev/null +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -0,0 +1,107 @@ +package ante_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + + ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +type MockRollappKeeper struct { + rollapps map[string]rollapptypes.Rollapp +} + +func NewMockRollappKeeper() *MockRollappKeeper { + testRollappWithCanonChannel := rollapptypes.Rollapp{ + RollappId: "rollapp-has-canon-client", + ChannelId: "channel-on-canon-client", + } + testRollappWithNoCanonChannel := rollapptypes.Rollapp{ + RollappId: "rollapp-no-canon-channel", + ChannelId: "", + } + return &MockRollappKeeper{ + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-has-canon-client": testRollappWithCanonChannel, + "rollapp-no-canon-channel": testRollappWithNoCanonChannel, + }, + } +} + +func (m *MockRollappKeeper) GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) { + val, found = m.rollapps[rollappId] + return val, found +} + +func (m *MockRollappKeeper) SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rollapp) { + m.rollapps[rollapp.RollappId] = rollapp +} + +func (m *MockRollappKeeper) FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (*rollapptypes.StateInfo, error) { + return nil, nil +} + +func (m *MockRollappKeeper) GetStateInfo(ctx sdk.Context, rollappId string, index uint64) (val rollapptypes.StateInfo, found bool) { + return rollapptypes.StateInfo{}, false +} + +type MockIBCCLientKeeper struct{} + +func NewMockIBCClientKeeper() *MockIBCCLientKeeper { + return &MockIBCCLientKeeper{} +} + +func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { + return nil, false +} + +func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { + switch clientID { + case "non-tm-client-id": + clientState := ibcsolomachine.ClientState{} + return &clientState, true + case "canon-client-id": + clientState := ibctm.ClientState{ + ChainId: "rollapp-has-canon-client", + } + return &clientState, true + } + return nil, false +} + +func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { + return "" +} + +type MockIBCChannelKeeper struct{} + +func NewMockIBCChannelKeeper() *MockIBCChannelKeeper { + return &MockIBCChannelKeeper{} +} + +func (m *MockIBCChannelKeeper) GetChannel(ctx sdk.Context, portID, channelID string) (channel ibcchanneltypes.Channel, found bool) { + return ibcchanneltypes.Channel{}, false +} + +func (m *MockIBCChannelKeeper) GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) { + if portID == "transfer" { + if channelID == "new-channel-on-canon-client" { + return "", ibcconnectiontypes.ConnectionEnd{ + ClientId: "canon-client-id", + }, nil + } + if channelID == "first-channel-on-canon-client" { + return "", ibcconnectiontypes.ConnectionEnd{ + ClientId: "canon-client-id-2", + }, nil + } + return "", ibcconnectiontypes.ConnectionEnd{ + ClientId: "non-canon-client-id", + }, nil + } + return "", nil, nil +} From ca2e201b4b3d3c9ad3605c29fc577bf437f5f6c5 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:18:57 +0530 Subject: [PATCH 18/87] adding posthandler msg_create_client test --- x/lightclient/post/ibc_msg_create_client.go | 2 +- .../post/ibc_msg_create_client_test.go | 125 ++++++++++++++++++ x/lightclient/post/ibc_msgs.go | 8 +- x/lightclient/post/ibc_msgs_test.go | 37 ++++++ 4 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 x/lightclient/post/ibc_msg_create_client_test.go create mode 100644 x/lightclient/post/ibc_msgs_test.go diff --git a/x/lightclient/post/ibc_msg_create_client.go b/x/lightclient/post/ibc_msg_create_client.go index b2adcf78a..2152f3c88 100644 --- a/x/lightclient/post/ibc_msg_create_client.go +++ b/x/lightclient/post/ibc_msg_create_client.go @@ -23,7 +23,7 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli nextClientID, registrationFound := i.lightClientKeeper.GetCanonicalLightClientRegistration(ctx, rollappID) if registrationFound { // Check if the client was successfully created with given clientID - _, clientFound := i.ibcKeeper.ClientKeeper.GetClientState(ctx, nextClientID) + _, clientFound := i.ibcClientKeeper.GetClientState(ctx, nextClientID) if clientFound { // Set the client as the canonical client for the rollapp i.lightClientKeeper.SetCanonicalClient(ctx, rollappID, nextClientID) diff --git a/x/lightclient/post/ibc_msg_create_client_test.go b/x/lightclient/post/ibc_msg_create_client_test.go new file mode 100644 index 000000000..b53a83a29 --- /dev/null +++ b/x/lightclient/post/ibc_msg_create_client_test.go @@ -0,0 +1,125 @@ +package post_test + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/post" + "github.com/stretchr/testify/require" +) + +var ( + testClientState = ibctm.NewClientState("chain-id", + ibctm.DefaultTrustLevel, time.Hour*24*7*2, time.Hour*24*7*2, time.Second*10, + ibcclienttypes.MustParseHeight("1-1"), commitmenttypes.GetSDKSpecs(), []string{}, + ) +) + +type testInput struct { + msg *ibcclienttypes.MsgCreateClient + success bool +} + +func TestHandleMsgCreateClient(t *testing.T) { + + testCases := []struct { + name string + prepare func(ctx sdk.Context, k keeper.Keeper) testInput + assert func(ctx sdk.Context, k keeper.Keeper) + }{ + { + name: "Could not unpack light client state to tendermint state", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{}, + success: true, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) {}, + }, + { + name: "Canonical client registration not in progress", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "not-a-rollapp" + cs, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: cs, + }, + success: true, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) {}, + }, + { + name: "Canonical client registration in progress - tx was failure", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "rollapp-client-registration-in-progress" + cs, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + expectedClientID := "new-client-1" + k.BeginCanonicalLightClientRegistration( + ctx, + "rollapp-client-registration-in-progress", + expectedClientID, + ) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: cs, + }, + success: false, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-client-registration-in-progress") + require.False(t, found) + }, + }, + { + name: "Canonical client registration in progress - client was found with name", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "rollapp-client-registration-in-progress" + cs, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + expectedClientID := "canon-client-id" + k.BeginCanonicalLightClientRegistration( + ctx, + "rollapp-client-registration-in-progress", + expectedClientID, + ) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: cs, + }, + success: true, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + clientID, found := k.GetCanonicalClient(ctx, "rollapp-client-registration-in-progress") + require.True(t, found) + require.Equal(t, "canon-client-id", clientID) + _, found = k.GetCanonicalLightClientRegistration(ctx, "rollapp-client-registration-in-progress") + require.False(t, found) + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + ibcclientKeeper := NewMockIBCClientKeeper() + ibcMsgDecorator := post.NewIBCMessagesDecorator(*keeper, ibcclientKeeper) + + input := tc.prepare(ctx, *keeper) + ibcMsgDecorator.HandleMsgCreateClient(ctx, input.msg, input.success) + tc.assert(ctx, *keeper) + }) + } + +} diff --git a/x/lightclient/post/ibc_msgs.go b/x/lightclient/post/ibc_msgs.go index 7e5095648..2ab0f6886 100644 --- a/x/lightclient/post/ibc_msgs.go +++ b/x/lightclient/post/ibc_msgs.go @@ -3,20 +3,20 @@ package post import ( sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) var _ sdk.PostDecorator = IBCMessagesDecorator{} type IBCMessagesDecorator struct { - ibcKeeper ibckeeper.Keeper + ibcClientKeeper types.IBCClientKeeperExpected lightClientKeeper keeper.Keeper } -func NewIBCMessagesDecorator(k keeper.Keeper, ibcK ibckeeper.Keeper) IBCMessagesDecorator { +func NewIBCMessagesDecorator(k keeper.Keeper, ibcK types.IBCClientKeeperExpected) IBCMessagesDecorator { return IBCMessagesDecorator{ - ibcKeeper: ibcK, + ibcClientKeeper: ibcK, lightClientKeeper: k, } } diff --git a/x/lightclient/post/ibc_msgs_test.go b/x/lightclient/post/ibc_msgs_test.go new file mode 100644 index 000000000..c20b2330f --- /dev/null +++ b/x/lightclient/post/ibc_msgs_test.go @@ -0,0 +1,37 @@ +package post_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + + ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" +) + +type MockIBCCLientKeeper struct{} + +func NewMockIBCClientKeeper() *MockIBCCLientKeeper { + return &MockIBCCLientKeeper{} +} + +func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { + return nil, false +} + +func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { + switch clientID { + case "non-tm-client-id": + clientState := ibcsolomachine.ClientState{} + return &clientState, true + case "canon-client-id": + clientState := ibctm.ClientState{ + ChainId: "rollapp-has-canon-client", + } + return &clientState, true + } + return nil, false +} + +func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { + return "" +} From a5d215984ad8a36cafc60f2e5ad2ffdd0d7e6559 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:41:45 +0530 Subject: [PATCH 19/87] refactor mock rollapp keeper --- .../ante/ibc_msg_channel_open_ack_test.go | 13 ++++++++++++- .../ante/ibc_msg_submit_misbehaviour_test.go | 2 +- x/lightclient/ante/ibc_msgs_test.go | 15 ++------------- x/lightclient/post/ibc_msg_create_client_test.go | 1 - 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go index 09e4cc023..814db21d3 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go @@ -6,12 +6,23 @@ import ( ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" "github.com/stretchr/testify/require" ) func TestHandleMsgChannelOpenAck(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) - rollappKeeper := NewMockRollappKeeper() + testRollapps := map[string]rollapptypes.Rollapp{ + "rollapp-has-canon-client": rollapptypes.Rollapp{ + RollappId: "rollapp-has-canon-client", + ChannelId: "channel-on-canon-client", + }, + "rollapp-no-canon-channel": rollapptypes.Rollapp{ + RollappId: "rollapp-no-canon-channel", + ChannelId: "", + }, + } + rollappKeeper := NewMockRollappKeeper(testRollapps) ibcclientKeeper := NewMockIBCClientKeeper() ibcchannelKeeper := NewMockIBCChannelKeeper() keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go index b892ae363..3a4f83d35 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go @@ -12,7 +12,7 @@ import ( func TestHandleMsgSubmitMisbehaviour(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) - rollappKeeper := NewMockRollappKeeper() + rollappKeeper := NewMockRollappKeeper(nil) ibcclientKeeper := NewMockIBCClientKeeper() ibcchannelKeeper := NewMockIBCChannelKeeper() keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index bd4f09562..525a08f68 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -15,20 +15,9 @@ type MockRollappKeeper struct { rollapps map[string]rollapptypes.Rollapp } -func NewMockRollappKeeper() *MockRollappKeeper { - testRollappWithCanonChannel := rollapptypes.Rollapp{ - RollappId: "rollapp-has-canon-client", - ChannelId: "channel-on-canon-client", - } - testRollappWithNoCanonChannel := rollapptypes.Rollapp{ - RollappId: "rollapp-no-canon-channel", - ChannelId: "", - } +func NewMockRollappKeeper(rollapps map[string]rollapptypes.Rollapp) *MockRollappKeeper { return &MockRollappKeeper{ - rollapps: map[string]rollapptypes.Rollapp{ - "rollapp-has-canon-client": testRollappWithCanonChannel, - "rollapp-no-canon-channel": testRollappWithNoCanonChannel, - }, + rollapps: rollapps, } } diff --git a/x/lightclient/post/ibc_msg_create_client_test.go b/x/lightclient/post/ibc_msg_create_client_test.go index b53a83a29..2bce4683c 100644 --- a/x/lightclient/post/ibc_msg_create_client_test.go +++ b/x/lightclient/post/ibc_msg_create_client_test.go @@ -27,7 +27,6 @@ type testInput struct { } func TestHandleMsgCreateClient(t *testing.T) { - testCases := []struct { name string prepare func(ctx sdk.Context, k keeper.Keeper) testInput From fcbf1255a2d9c31c6a9ebf42fa2835d00890365f Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:12:51 +0530 Subject: [PATCH 20/87] adding ibc_msg_create_client test --- .../ante/ibc_msg_channel_open_ack_test.go | 6 +- .../ante/ibc_msg_create_client_test.go | 297 ++++++++++++++++++ .../ante/ibc_msg_submit_misbehaviour_test.go | 2 +- x/lightclient/ante/ibc_msgs_test.go | 25 +- 4 files changed, 321 insertions(+), 9 deletions(-) create mode 100644 x/lightclient/ante/ibc_msg_create_client_test.go diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go index 814db21d3..645f978b8 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go @@ -13,16 +13,16 @@ import ( func TestHandleMsgChannelOpenAck(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) testRollapps := map[string]rollapptypes.Rollapp{ - "rollapp-has-canon-client": rollapptypes.Rollapp{ + "rollapp-has-canon-client": { RollappId: "rollapp-has-canon-client", ChannelId: "channel-on-canon-client", }, - "rollapp-no-canon-channel": rollapptypes.Rollapp{ + "rollapp-no-canon-channel": { RollappId: "rollapp-no-canon-channel", ChannelId: "", }, } - rollappKeeper := NewMockRollappKeeper(testRollapps) + rollappKeeper := NewMockRollappKeeper(testRollapps, nil) ibcclientKeeper := NewMockIBCClientKeeper() ibcchannelKeeper := NewMockIBCChannelKeeper() keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") diff --git a/x/lightclient/ante/ibc_msg_create_client_test.go b/x/lightclient/ante/ibc_msg_create_client_test.go new file mode 100644 index 000000000..8306279a1 --- /dev/null +++ b/x/lightclient/ante/ibc_msg_create_client_test.go @@ -0,0 +1,297 @@ +package ante_test + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" + "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +var ( + testClientState = ibctm.NewClientState("chain-id", + ibctm.DefaultTrustLevel, time.Hour*24*7*2, time.Hour*24*7*2, time.Second*10, + ibcclienttypes.MustParseHeight("1-1"), commitmenttypes.GetSDKSpecs(), []string{}, + ) +) + +type testInput struct { + msg *ibcclienttypes.MsgCreateClient + rollapps map[string]rollapptypes.Rollapp + stateInfos map[string]map[uint64]rollapptypes.StateInfo +} + +func TestHandleMsgCreateClient(t *testing.T) { + testCases := []struct { + name string + prepare func(ctx sdk.Context, k keeper.Keeper) testInput + assert func(ctx sdk.Context, k keeper.Keeper) + }{ + { + name: "Could not unpack light client state to tendermint state", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{}, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) {}, + }, + { + name: "Client is not a tendermint client", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + solomachineClientState := ibcsolomachine.NewClientState(0, nil) + cs, err := ibcclienttypes.PackClientState(solomachineClientState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: cs, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) {}, + }, + { + name: "Rollapp with given chainID does not exist", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "not-a-rollapp" + cs, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: cs, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + _, found := k.GetCanonicalClient(ctx, "not-a-rollapp") + require.False(t, found) + }, + }, + { + name: "Canonical client for the rollapp already exists", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "rollapp-has-canon-client" + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + cs, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: cs, + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-has-canon-client": { + RollappId: "rollapp-has-canon-client", + ChannelId: "channel-on-canon-client", + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + clientID, found := k.GetCanonicalClient(ctx, "rollapp-has-canon-client") + require.True(t, found) + require.Equal(t, "canon-client-id", clientID) + }, + }, + { + name: "Could not find block desc for given height - continue without setting as canonical client", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "rollapp-wants-canon-client" + cs, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: cs, + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-wants-canon-client": { + RollappId: "rollapp-wants-canon-client", + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") + require.False(t, found) + }, + }, + { + name: "Could not find block descriptor for the next height (h+1)", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "rollapp-wants-canon-client" + cs, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: cs, + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-wants-canon-client": { + RollappId: "rollapp-wants-canon-client", + }, + }, + stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ + "rollapp-wants-canon-client": { + 1: { + StartHeight: 1, + NumBlocks: 1, + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 1, + }, + Sequencer: "sequencerAddr", + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + }, + }, + }, + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") + require.False(t, found) + }, + }, + { + name: "State incompatible", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "rollapp-wants-canon-client" + clientState, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + testConsensusState := ibctm.NewConsensusState( + time.Now().UTC(), + commitmenttypes.NewMerkleRoot([]byte{}), + ctx.BlockHeader().ValidatorsHash, + ) + consState, err := ibcclienttypes.PackConsensusState(testConsensusState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: clientState, + ConsensusState: consState, + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-wants-canon-client": { + RollappId: "rollapp-wants-canon-client", + }, + }, + stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ + "rollapp-wants-canon-client": { + 1: { + StartHeight: 1, + NumBlocks: 2, + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 1, + }, + Sequencer: "sequencerAddr", + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + }, + }, + }, + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") + require.False(t, found) + }, + }, + { + name: "State compatible - Candidate canonical client set", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + testClientState.ChainId = "rollapp-wants-canon-client" + clientState, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + testConsensusState := ibctm.NewConsensusState( + time.Now().UTC(), + commitmenttypes.NewMerkleRoot([]byte{}), + ctx.BlockHeader().ValidatorsHash, + ) + consState, err := ibcclienttypes.PackConsensusState(testConsensusState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: clientState, + ConsensusState: consState, + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-wants-canon-client": { + RollappId: "rollapp-wants-canon-client", + }, + }, + stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ + "rollapp-wants-canon-client": { + 1: { + StartHeight: 1, + NumBlocks: 2, + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 1, + }, + Sequencer: "sequencerAddr", + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + }, + }, + }, + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + clientID, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") + require.True(t, found) + require.Equal(t, "client-1", clientID) + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + ibcclientKeeper := NewMockIBCClientKeeper() + ibcchannelKeeper := NewMockIBCChannelKeeper() + input := tc.prepare(ctx, *keeper) + rollappKeeper := NewMockRollappKeeper(input.rollapps, input.stateInfos) + ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) + + ibcMsgDecorator.HandleMsgCreateClient(ctx, input.msg) + tc.assert(ctx, *keeper) + }) + } + +} diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go index 3a4f83d35..9621a93da 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go @@ -12,7 +12,7 @@ import ( func TestHandleMsgSubmitMisbehaviour(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) - rollappKeeper := NewMockRollappKeeper(nil) + rollappKeeper := NewMockRollappKeeper(nil, nil) ibcclientKeeper := NewMockIBCClientKeeper() ibcchannelKeeper := NewMockIBCChannelKeeper() keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index 525a08f68..6426ca6c7 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -12,12 +12,14 @@ import ( ) type MockRollappKeeper struct { - rollapps map[string]rollapptypes.Rollapp + rollapps map[string]rollapptypes.Rollapp + stateInfos map[string]map[uint64]rollapptypes.StateInfo } -func NewMockRollappKeeper(rollapps map[string]rollapptypes.Rollapp) *MockRollappKeeper { +func NewMockRollappKeeper(rollapps map[string]rollapptypes.Rollapp, stateInfos map[string]map[uint64]rollapptypes.StateInfo) *MockRollappKeeper { return &MockRollappKeeper{ - rollapps: rollapps, + rollapps: rollapps, + stateInfos: stateInfos, } } @@ -31,11 +33,24 @@ func (m *MockRollappKeeper) SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rol } func (m *MockRollappKeeper) FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (*rollapptypes.StateInfo, error) { - return nil, nil + stateInfos, found := m.stateInfos[rollappId] + if !found { + return nil, rollapptypes.ErrUnknownRollappID + } + stateInfo, found := stateInfos[height] + if !found { + return nil, rollapptypes.ErrNotFound + } + return &stateInfo, nil } func (m *MockRollappKeeper) GetStateInfo(ctx sdk.Context, rollappId string, index uint64) (val rollapptypes.StateInfo, found bool) { - return rollapptypes.StateInfo{}, false + stateInfos, found := m.stateInfos[rollappId] + if !found { + return val, false + } + val, found = stateInfos[index] + return val, found } type MockIBCCLientKeeper struct{} From 0e557c01d0f892d3dd3e89c4c1ad2cf779234fc4 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:30:29 +0530 Subject: [PATCH 21/87] adding ibc msg update client ante tests --- .../ante/ibc_msg_create_client_test.go | 27 +- x/lightclient/ante/ibc_msg_update_client.go | 10 +- .../ante/ibc_msg_update_client_test.go | 347 ++++++++++++++++++ x/lightclient/types/state.go | 6 +- 4 files changed, 369 insertions(+), 21 deletions(-) create mode 100644 x/lightclient/ante/ibc_msg_update_client_test.go diff --git a/x/lightclient/ante/ibc_msg_create_client_test.go b/x/lightclient/ante/ibc_msg_create_client_test.go index 8306279a1..d7c010236 100644 --- a/x/lightclient/ante/ibc_msg_create_client_test.go +++ b/x/lightclient/ante/ibc_msg_create_client_test.go @@ -24,13 +24,13 @@ var ( ) ) -type testInput struct { - msg *ibcclienttypes.MsgCreateClient - rollapps map[string]rollapptypes.Rollapp - stateInfos map[string]map[uint64]rollapptypes.StateInfo -} - func TestHandleMsgCreateClient(t *testing.T) { + type testInput struct { + msg *ibcclienttypes.MsgCreateClient + rollapps map[string]rollapptypes.Rollapp + stateInfos map[string]map[uint64]rollapptypes.StateInfo + } + testCases := []struct { name string prepare func(ctx sdk.Context, k keeper.Keeper) testInput @@ -225,13 +225,14 @@ func TestHandleMsgCreateClient(t *testing.T) { { name: "State compatible - Candidate canonical client set", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + blocktimestamp := time.Now().UTC() testClientState.ChainId = "rollapp-wants-canon-client" clientState, err := ibcclienttypes.PackClientState(testClientState) require.NoError(t, err) testConsensusState := ibctm.NewConsensusState( - time.Now().UTC(), - commitmenttypes.NewMerkleRoot([]byte{}), - ctx.BlockHeader().ValidatorsHash, + blocktimestamp, + commitmenttypes.NewMerkleRoot([]byte("appHash")), + []byte("sequencerAddr"), ) consState, err := ibcclienttypes.PackConsensusState(testConsensusState) require.NoError(t, err) @@ -258,13 +259,13 @@ func TestHandleMsgCreateClient(t *testing.T) { BD: []rollapptypes.BlockDescriptor{ { Height: 1, - StateRoot: []byte{}, - Timestamp: time.Now().UTC(), + StateRoot: []byte("appHash"), + Timestamp: blocktimestamp, }, { Height: 2, - StateRoot: []byte{}, - Timestamp: time.Now().UTC(), + StateRoot: []byte("appHash2"), + Timestamp: blocktimestamp.Add(1), }, }, }, diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index f15e58a95..5dce98256 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -35,7 +35,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli return nil } // Check if there are existing block descriptors for the given height of client state - height := header.GetHeight() + height := header.TrustedHeight stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) if err != nil { // No BDs found for given height. @@ -66,8 +66,12 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // next BD does not exist in this state info, check the next state info nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) if !found { - // if next state info does not exist, then we can't verify the next block valhash. So we ignore this check - rollappState.NextBlockSequencer = "" + // if next state info does not exist, then we can't verify the next block valhash. + // Will accept the update optimistically + // But also save the blockProposer address with the height for future verification + blockProposer := header.Header.ProposerAddress + i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), string(blockProposer)) + return nil } else { rollappState.NextBlockSequencer = nextStateInfo.Sequencer rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go new file mode 100644 index 000000000..d865939f8 --- /dev/null +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -0,0 +1,347 @@ +package ante_test + +import ( + "testing" + "time" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" + "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/stretchr/testify/require" +) + +func TestHandleMsgUpdateClient(t *testing.T) { + type testInput struct { + msg *ibcclienttypes.MsgUpdateClient + rollapps map[string]rollapptypes.Rollapp + stateInfos map[string]map[uint64]rollapptypes.StateInfo + } + testCases := []struct { + name string + prepare func(ctx sdk.Context, k keeper.Keeper) testInput + assert func(ctx sdk.Context, k keeper.Keeper, err error) + }{ + { + name: "Could not find a client with given client id", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + return testInput{ + msg: &ibcclienttypes.MsgUpdateClient{ + ClientId: "non-existent-client", + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper, err error) { + require.NoError(t, err) + }, + }, + { + name: "Could not unpack as tendermint client state", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + return testInput{ + msg: &ibcclienttypes.MsgUpdateClient{ + ClientId: "non-tm-client-id", + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper, err error) { + require.NoError(t, err) + }, + }, + { + name: "Client is not a known canonical client of a rollapp", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + return testInput{ + msg: &ibcclienttypes.MsgUpdateClient{ + ClientId: "canon-client-id", + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper, err error) { + require.NoError(t, err) + }, + }, + { + name: "Could not find state info for height - ensure optimistically accepted and signer stored in state", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + var ( + valSet *cmtproto.ValidatorSet + trustedVals *cmtproto.ValidatorSet + ) + signedHeader := &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + ProposerAddress: []byte("sequencerAddr"), + }, + Commit: &cmtproto.Commit{}, + } + header := ibctm.Header{ + SignedHeader: signedHeader, + ValidatorSet: valSet, + TrustedHeight: ibcclienttypes.MustParseHeight("1-1"), + TrustedValidators: trustedVals, + } + clientMsg, err := ibcclienttypes.PackClientMessage(&header) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgUpdateClient{ + ClientId: "canon-client-id", + ClientMessage: clientMsg, + Signer: "sequencerAddr", + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-has-canon-client": { + RollappId: "rollapp-has-canon-client", + }, + }, + stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ + "rollapp-has-canon-client": { + 3: { + Sequencer: "sequencerAddr", + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 3, + }, + StartHeight: 3, + NumBlocks: 1, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 3, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + }, + }, + }, + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper, err error) { + require.NoError(t, err) + signer, found := k.GetConsensusStateSigner(ctx, "canon-client-id", 1) + require.True(t, found) + require.Equal(t, "sequencerAddr", signer) + }, + }, + { + name: "BD does not include next block in state info - ensure optimistically accepted and signer stored in state", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + var ( + valSet *cmtproto.ValidatorSet + trustedVals *cmtproto.ValidatorSet + ) + signedHeader := &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + AppHash: []byte("appHash"), + ProposerAddress: []byte("sequencerAddr"), + Time: time.Now().UTC(), + NextValidatorsHash: []byte("nextValHash"), + }, + Commit: &cmtproto.Commit{}, + } + header := ibctm.Header{ + SignedHeader: signedHeader, + ValidatorSet: valSet, + TrustedHeight: ibcclienttypes.MustParseHeight("1-1"), + TrustedValidators: trustedVals, + } + clientMsg, err := ibcclienttypes.PackClientMessage(&header) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgUpdateClient{ + ClientId: "canon-client-id", + ClientMessage: clientMsg, + Signer: "sequencerAddr", + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-has-canon-client": { + RollappId: "rollapp-has-canon-client", + }, + }, + stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ + "rollapp-has-canon-client": { + 1: { + Sequencer: "sequencerAddr", + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 1, + }, + StartHeight: 1, + NumBlocks: 1, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + }, + }, + }, + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper, err error) { + require.NoError(t, err) + signer, found := k.GetConsensusStateSigner(ctx, "canon-client-id", 1) + require.True(t, found) + require.Equal(t, "sequencerAddr", signer) + }, + }, + { + name: "Ensure state is incompatible - err", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + var ( + valSet *cmtproto.ValidatorSet + trustedVals *cmtproto.ValidatorSet + ) + signedHeader := &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + AppHash: []byte("appHash"), + ProposerAddress: []byte("sequencerAddr"), + Time: time.Now().UTC(), + NextValidatorsHash: []byte("nextValHash"), + }, + Commit: &cmtproto.Commit{}, + } + header := ibctm.Header{ + SignedHeader: signedHeader, + ValidatorSet: valSet, + TrustedHeight: ibcclienttypes.MustParseHeight("1-1"), + TrustedValidators: trustedVals, + } + clientMsg, err := ibcclienttypes.PackClientMessage(&header) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgUpdateClient{ + ClientId: "canon-client-id", + ClientMessage: clientMsg, + Signer: "sequencerAddr", + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-has-canon-client": { + RollappId: "rollapp-has-canon-client", + }, + }, + stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ + "rollapp-has-canon-client": { + 1: { + Sequencer: "sequencerAddr", + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 1, + }, + StartHeight: 1, + NumBlocks: 2, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte{}, + Timestamp: time.Now().UTC(), + }, + }, + }, + }, + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper, err error) { + require.ErrorIs(t, err, ibcclienttypes.ErrInvalidConsensus) + }, + }, + { + name: "Ensure state is compatible - happy path", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + var ( + valSet *cmtproto.ValidatorSet + trustedVals *cmtproto.ValidatorSet + ) + signedHeader := &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + AppHash: []byte("appHash"), + ProposerAddress: []byte("sequencerAddr"), + Time: time.Now().UTC(), + NextValidatorsHash: []byte("sequencerAddr"), + }, + Commit: &cmtproto.Commit{}, + } + header := ibctm.Header{ + SignedHeader: signedHeader, + ValidatorSet: valSet, + TrustedHeight: ibcclienttypes.MustParseHeight("1-1"), + TrustedValidators: trustedVals, + } + clientMsg, err := ibcclienttypes.PackClientMessage(&header) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgUpdateClient{ + ClientId: "canon-client-id", + ClientMessage: clientMsg, + Signer: "sequencerAddr", + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-has-canon-client": { + RollappId: "rollapp-has-canon-client", + }, + }, + stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ + "rollapp-has-canon-client": { + 1: { + Sequencer: "sequencerAddr", + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 1, + }, + StartHeight: 1, + NumBlocks: 2, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("appHash"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("appHash2"), + Timestamp: time.Now().UTC(), + }, + }, + }, + }, + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper, err error) { + require.NoError(t, err) + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + ibcclientKeeper := NewMockIBCClientKeeper() + ibcchannelKeeper := NewMockIBCChannelKeeper() + input := tc.prepare(ctx, *keeper) + rollappKeeper := NewMockRollappKeeper(input.rollapps, input.stateInfos) + ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) + + err := ibcMsgDecorator.HandleMsgUpdateClient(ctx, input.msg) + tc.assert(ctx, *keeper, err) + }) + } +} diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index db7855007..6edf32dda 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -16,7 +16,7 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") } // Check if block descriptor timestamp matches IBC header timestamp - if ibcState.Timestamp.Equal(raState.BlockDescriptor.Timestamp) { + if !ibcState.Timestamp.Equal(raState.BlockDescriptor.Timestamp) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp") } // in case of msgcreateclinet validator info is not available @@ -24,10 +24,6 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { if len(ibcState.Validator) == 1 && bytes.Equal(ibcState.Validator, []byte(raState.BlockSequencer)) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator set hash does not match the sequencer") } - // Check if second block descriptor exists, if it doesnt, this one condition is accepted optimistically - if raState.NextBlockSequencer == "" { - return nil - } // Check if the nextValidatorHash matches the sequencer for h+1 if !bytes.Equal(ibcState.NextValidatorsHash, []byte(raState.NextBlockSequencer)) { From ec0471769de6b9750d0632cae30e919472b5a6f3 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:52:55 +0530 Subject: [PATCH 22/87] adding state compatibility check tests --- x/lightclient/types/state.go | 6 +- x/lightclient/types/state_test.go | 132 ++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 x/lightclient/types/state_test.go diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 6edf32dda..80f240126 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -19,14 +19,14 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { if !ibcState.Timestamp.Equal(raState.BlockDescriptor.Timestamp) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp") } - // in case of msgcreateclinet validator info is not available + // in case of msgcreateclient, validator info is not available. it is only send in msgupdateclient as header info // Check if the validator set hash matches the sequencer - if len(ibcState.Validator) == 1 && bytes.Equal(ibcState.Validator, []byte(raState.BlockSequencer)) { + if len(ibcState.Validator) > 0 && string(ibcState.Validator) != raState.BlockSequencer { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator set hash does not match the sequencer") } // Check if the nextValidatorHash matches the sequencer for h+1 - if !bytes.Equal(ibcState.NextValidatorsHash, []byte(raState.NextBlockSequencer)) { + if string(ibcState.NextValidatorsHash) != raState.NextBlockSequencer { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1") } return nil diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go new file mode 100644 index 000000000..cf11da171 --- /dev/null +++ b/x/lightclient/types/state_test.go @@ -0,0 +1,132 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/stretchr/testify/require" +) + +func TestCheckCompatibility(t *testing.T) { + type input struct { + ibcState types.IBCState + raState types.RollappState + } + timestamp := time.Now().UTC() + testCases := []struct { + name string + input input + err string + }{ + { + name: "roots are not equal", + input: input{ + ibcState: types.IBCState{ + Root: []byte("root"), + }, + raState: types.RollappState{ + BlockSequencer: "sequencer", + BlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("not root"), + }, + }, + }, + err: "block descriptor state root does not match tendermint header app hash", + }, + { + name: "timestamps are not equal", + input: input{ + ibcState: types.IBCState{ + Root: []byte("root"), + Timestamp: time.Now().UTC(), + }, + raState: types.RollappState{ + BlockSequencer: "sequencer", + BlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root"), + Timestamp: time.Now().UTC().Add(1), + }, + }, + }, + err: "block descriptor timestamp does not match tendermint header timestamp", + }, + { + name: "validator who signed the block header is not the sequencer who submitted the block", + input: input{ + ibcState: types.IBCState{ + Root: []byte("root"), + Timestamp: timestamp, + Validator: []byte("validator"), + }, + raState: types.RollappState{ + BlockSequencer: "sequencer", + BlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root"), + Timestamp: timestamp, + }, + }, + }, + err: "validator set hash does not match the sequencer", + }, + { + name: "nextValidatorHash does not match the sequencer who submitted the next block descriptor", + input: input{ + ibcState: types.IBCState{ + Root: []byte("root"), + Timestamp: timestamp, + NextValidatorsHash: []byte("next validator hash"), + Validator: []byte("sequencer"), + }, + raState: types.RollappState{ + BlockSequencer: "sequencer", + BlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root"), + Timestamp: timestamp, + }, + NextBlockSequencer: "next sequencer", + NextBlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root2"), + Timestamp: timestamp, + }, + }, + }, + err: "next validator hash does not match the sequencer for h+1", + }, + { + name: "all fields are compatible", + input: input{ + ibcState: types.IBCState{ + Root: []byte("root"), + Timestamp: timestamp, + NextValidatorsHash: []byte("next sequencer"), + Validator: []byte("sequencer"), + }, + raState: types.RollappState{ + BlockSequencer: "sequencer", + BlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root"), + Timestamp: timestamp, + }, + NextBlockSequencer: "next sequencer", + NextBlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root2"), + Timestamp: timestamp, + }, + }, + }, + err: "", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := types.CheckCompatibility(tc.input.ibcState, tc.input.raState) + if tc.err == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tc.err) + } + }) + } +} From c01a72f941f4f987d46b484987b9bb2cfb5b664d Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:30:30 +0530 Subject: [PATCH 23/87] perform data transform to get tm pubkeys from cosmos pubkeys for sequencer --- app/keepers/keepers.go | 1 + app/post/post.go | 2 +- testutil/keeper/lightclient.go | 27 +++++++++++ x/lightclient/ante/ibc_msg_create_client.go | 16 +++++-- x/lightclient/ante/ibc_msg_update_client.go | 14 ++++-- .../ante/ibc_msg_update_client_test.go | 40 ++++++++++++----- x/lightclient/keeper/hook_listener.go | 20 ++++++--- x/lightclient/keeper/hook_listener_test.go | 4 +- x/lightclient/keeper/keeper.go | 45 ++++++++++++++++++- x/lightclient/types/expected_keepers.go | 5 +++ x/lightclient/types/state.go | 33 ++++++++++++-- x/lightclient/types/state_test.go | 14 +++--- 12 files changed, 181 insertions(+), 40 deletions(-) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index bb4989e8d..48077a367 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -364,6 +364,7 @@ func (a *AppKeepers) InitKeepers( a.keys[lightclientmoduletypes.StoreKey], a.IBCKeeper.ClientKeeper, a.SequencerKeeper, + a.AccountKeeper, ) a.RollappKeeper.SetSequencerKeeper(a.SequencerKeeper) diff --git a/app/post/post.go b/app/post/post.go index 268675b75..e269bc187 100644 --- a/app/post/post.go +++ b/app/post/post.go @@ -22,7 +22,7 @@ func NewPostHandler(options HandlerOptions) (sdk.PostHandler, error) { return nil, err } postDecorators := []sdk.PostDecorator{ - lightclientpost.NewIBCMessagesDecorator(*options.LightClientKeeper, *options.IBCKeeper), + lightclientpost.NewIBCMessagesDecorator(*options.LightClientKeeper, *&options.IBCKeeper.ClientKeeper), } return sdk.ChainPostDecorators(postDecorators...), nil diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 95a52eef2..ba2e1e88b 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -7,9 +7,11 @@ import ( cmttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" @@ -19,9 +21,14 @@ import ( cometbftdb "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/libs/log" cometbftproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/stretchr/testify/require" ) +const ( + Alice = "cosmos1c4k24jzduc365kywrsvf5ujz4ya6mwymy8vq4q" +) + func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { storeKey := sdk.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.StoreKey + "_mem") @@ -39,11 +46,13 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { mockIBCKeeper := NewMockIBCClientKeeper() mockSequencerKeeper := NewMockSequencerKeeper() + mockAccountKeeper := NewMockAccountKeeper() k := keeper.NewKeeper( cdc, storeKey, mockIBCKeeper, mockSequencerKeeper, + mockAccountKeeper, ) ctx := sdk.NewContext(stateStore, cometbftproto.Header{}, false, log.NewNopLogger()) @@ -93,3 +102,21 @@ func NewMockSequencerKeeper() *MockSequencerKeeper { func (m *MockSequencerKeeper) SlashAndJailFraud(ctx sdk.Context, seqAddr string) error { return nil } + +type MockAccountKeeper struct { + pubkey cryptotypes.PubKey +} + +func NewMockAccountKeeper() *MockAccountKeeper { + pubkey := ed25519.GenPrivKey().PubKey() + return &MockAccountKeeper{ + pubkey: pubkey, + } +} + +func (m *MockAccountKeeper) GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (cryptotypes.PubKey, error) { + if addr.String() == Alice { + return m.pubkey, nil + } + return nil, sdkerrors.ErrUnknownAddress +} diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index b6d7b5f36..2e6db6026 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -57,19 +57,23 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), Height: tmClientState.GetLatestHeight().GetRevisionHeight(), - Validator: []byte{}, // not sure if this info is available in the tendermint consensus state + Validator: []byte{}, // not sure if this info is available in the tendermint consensus state as no header has been shared yet NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: timestamp, } + sequencerPk, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) + if err != nil { + return + } rollappState := types.RollappState{ - BlockSequencer: stateInfo.Sequencer, + BlockSequencer: sequencerPk, BlockDescriptor: blockDescriptor, } // Check if bd for next block exists and is part of same state info nextHeight := height.GetRevisionHeight() + 1 if stateInfo.StartHeight+stateInfo.NumBlocks >= nextHeight { rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[nextHeight-stateInfo.StartHeight] - rollappState.NextBlockSequencer = stateInfo.Sequencer + rollappState.NextBlockSequencer = sequencerPk } else { // nextBD doesnt exist in same stateInfo. So lookup in the next StateInfo currentStateInfoIndex := stateInfo.GetIndex().Index @@ -77,7 +81,11 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli if !found { return // There is no BD for h+1, so we can't verify the next block valhash. So we cant mark this client as canonical } else { - rollappState.NextBlockSequencer = nextStateInfo.Sequencer + nextSequencerPk, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, nextStateInfo.Sequencer) + if err != nil { + return + } + rollappState.NextBlockSequencer = nextSequencerPk rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] } } diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 5dce98256..6f2a0e0e6 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -54,13 +54,17 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli NextValidatorsHash: header.Header.NextValidatorsHash, Timestamp: header.Header.Time, } + sequencerPubKey, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) + if err != nil { + return err + } rollappState := types.RollappState{ - BlockSequencer: stateInfo.Sequencer, + BlockSequencer: sequencerPubKey, BlockDescriptor: bd, } // Check that BD for next block exists if height.GetRevisionHeight()-stateInfo.GetStartHeight()+1 < uint64(len(stateInfo.GetBDs().BD)) { - rollappState.NextBlockSequencer = stateInfo.Sequencer + rollappState.NextBlockSequencer = sequencerPubKey rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()+1] } else { // next BD does not exist in this state info, check the next state info @@ -73,7 +77,11 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), string(blockProposer)) return nil } else { - rollappState.NextBlockSequencer = nextStateInfo.Sequencer + nextSequencerPk, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, nextStateInfo.Sequencer) + if err != nil { + return err + } + rollappState.NextBlockSequencer = nextSequencerPk rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] } } diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index d865939f8..7dfdd1f73 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -4,7 +4,11 @@ import ( "testing" "time" + abci "github.com/cometbft/cometbft/abci/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" @@ -91,7 +95,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { msg: &ibcclienttypes.MsgUpdateClient{ ClientId: "canon-client-id", ClientMessage: clientMsg, - Signer: "sequencerAddr", + Signer: "relayerAddr", }, rollapps: map[string]rollapptypes.Rollapp{ "rollapp-has-canon-client": { @@ -101,7 +105,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ "rollapp-has-canon-client": { 3: { - Sequencer: "sequencerAddr", + Sequencer: keepertest.Alice, StateInfoIndex: rollapptypes.StateInfoIndex{ Index: 3, }, @@ -157,7 +161,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { msg: &ibcclienttypes.MsgUpdateClient{ ClientId: "canon-client-id", ClientMessage: clientMsg, - Signer: "sequencerAddr", + Signer: "relayerAddr", }, rollapps: map[string]rollapptypes.Rollapp{ "rollapp-has-canon-client": { @@ -167,7 +171,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ "rollapp-has-canon-client": { 1: { - Sequencer: "sequencerAddr", + Sequencer: keepertest.Alice, StateInfoIndex: rollapptypes.StateInfoIndex{ Index: 1, }, @@ -233,7 +237,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ "rollapp-has-canon-client": { 1: { - Sequencer: "sequencerAddr", + Sequencer: keepertest.Alice, StateInfoIndex: rollapptypes.StateInfoIndex{ Index: 1, }, @@ -265,17 +269,29 @@ func TestHandleMsgUpdateClient(t *testing.T) { { name: "Ensure state is compatible - happy path", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + sequencer := keepertest.Alice + proposerAddr, err := k.GetTmPubkeyAsBytes(ctx, sequencer) + require.NoError(t, err) + blocktimestamp := time.Now().UTC() k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") var ( valSet *cmtproto.ValidatorSet trustedVals *cmtproto.ValidatorSet + nextVals cmttypes.ValidatorSet ) + var tmpk tmprotocrypto.PublicKey + err = tmpk.Unmarshal(proposerAddr) + require.NoError(t, err) + updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: tmpk}}) + require.NoError(t, err) + err = nextVals.UpdateWithChangeSet(updates) + require.NoError(t, err) signedHeader := &cmtproto.SignedHeader{ Header: &cmtproto.Header{ AppHash: []byte("appHash"), - ProposerAddress: []byte("sequencerAddr"), - Time: time.Now().UTC(), - NextValidatorsHash: []byte("sequencerAddr"), + ProposerAddress: proposerAddr, + Time: blocktimestamp, + NextValidatorsHash: nextVals.Hash(), }, Commit: &cmtproto.Commit{}, } @@ -291,7 +307,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { msg: &ibcclienttypes.MsgUpdateClient{ ClientId: "canon-client-id", ClientMessage: clientMsg, - Signer: "sequencerAddr", + Signer: "relayerAddr", }, rollapps: map[string]rollapptypes.Rollapp{ "rollapp-has-canon-client": { @@ -301,7 +317,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ "rollapp-has-canon-client": { 1: { - Sequencer: "sequencerAddr", + Sequencer: keepertest.Alice, StateInfoIndex: rollapptypes.StateInfoIndex{ Index: 1, }, @@ -312,12 +328,12 @@ func TestHandleMsgUpdateClient(t *testing.T) { { Height: 1, StateRoot: []byte("appHash"), - Timestamp: time.Now().UTC(), + Timestamp: blocktimestamp, }, { Height: 2, StateRoot: []byte("appHash2"), - Timestamp: time.Now().UTC(), + Timestamp: blocktimestamp.Add(1), }, }, }, diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 65dc31afb..53d2fe3d2 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -7,6 +7,7 @@ import ( ibcclienttypes "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/dymensionxyz/dymension/v3/x/lightclient/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) @@ -31,7 +32,7 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat bds := stateInfo.GetBDs() for i, bd := range bds.GetBD() { // Check if any optimistic updates were made for the given height - signer, found := hook.k.GetConsensusStateSigner(ctx, canonicalClient, bd.GetHeight()) + tmHeaderSigner, found := hook.k.GetConsensusStateSigner(ctx, canonicalClient, bd.GetHeight()) if !found { continue } @@ -52,25 +53,32 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), Height: bd.GetHeight(), - Validator: []byte(signer), + Validator: []byte(tmHeaderSigner), NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: timestamp, } + sequencerPk, err := hook.k.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) + if err != nil { + return err + } rollappState := types.RollappState{ - BlockSequencer: stateInfo.Sequencer, + BlockSequencer: sequencerPk, BlockDescriptor: bd, } // check if bd for next block exists if i+1 < len(bds.GetBD()) { - rollappState.NextBlockSequencer = stateInfo.Sequencer + rollappState.NextBlockSequencer = sequencerPk rollappState.NextBlockDescriptor = bds.GetBD()[i+1] } - err := types.CheckCompatibility(ibcState, rollappState) + err = types.CheckCompatibility(ibcState, rollappState) if err != nil { // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header - sequencerAddr := signer // todo: signer addr is sent from tm. so will be valconsaddr(?). check and then transform to valid address + sequencerAddr, err := hook.k.getAddress([]byte(tmHeaderSigner)) + if err != nil { + return err + } err = hook.k.sequencerKeeper.SlashAndJailFraud(ctx, sequencerAddr) if err != nil { return err diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index a4b594216..546d23280 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -81,7 +81,7 @@ func TestAfterUpdateState(t *testing.T) { input: testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ - Sequencer: "sequencer1", + Sequencer: keepertest.Alice, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { @@ -109,7 +109,7 @@ func TestAfterUpdateState(t *testing.T) { input: testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ - Sequencer: "sequencer1", + Sequencer: keepertest.Alice, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 72c0f0c3b..432ab496f 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -5,10 +5,11 @@ import ( "github.com/cometbft/cometbft/libs/log" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/dymensionxyz/dymension/v3/x/lightclient/types" ) @@ -17,6 +18,7 @@ type Keeper struct { storeKey storetypes.StoreKey ibcClientKeeper types.IBCClientKeeperExpected sequencerKeeper types.SequencerKeeperExpected + accountKeepr types.AccountKeeperExpected } func NewKeeper( @@ -24,6 +26,7 @@ func NewKeeper( storeKey storetypes.StoreKey, ibcKeeper types.IBCClientKeeperExpected, sequencerKeeper types.SequencerKeeperExpected, + accountKeeper types.AccountKeeperExpected, ) *Keeper { k := &Keeper{ @@ -31,10 +34,50 @@ func NewKeeper( storeKey: storeKey, ibcClientKeeper: ibcKeeper, sequencerKeeper: sequencerKeeper, + accountKeepr: accountKeeper, } return k } +func (k Keeper) GetTmPubkeyAsBytes(ctx sdk.Context, sequencerAddr string) ([]byte, error) { + tmPk, err := k.GetTmPubkey(ctx, sequencerAddr) + if err != nil { + return nil, err + } + tmPkBytes, err := tmPk.Marshal() + return tmPkBytes, err +} + +func (k Keeper) GetTmPubkey(ctx sdk.Context, sequencerAddr string) (tmprotocrypto.PublicKey, error) { + acc, err := sdk.AccAddressFromBech32(sequencerAddr) + if err != nil { + return tmprotocrypto.PublicKey{}, err + } + pk, err := k.accountKeepr.GetPubKey(ctx, acc) + if err != nil { + return tmprotocrypto.PublicKey{}, err + } + tmPk, err := cryptocodec.ToTmProtoPublicKey(pk) + if err != nil { + return tmprotocrypto.PublicKey{}, err + } + return tmPk, nil +} + +func (k Keeper) getAddress(tmPubkeyBz []byte) (string, error) { + var tmpk tmprotocrypto.PublicKey + err := tmpk.Unmarshal(tmPubkeyBz) + if err != nil { + return "", err + } + pubkey, err := cryptocodec.FromTmProtoPublicKey(tmpk) + if err != nil { + return "", err + } + acc := sdk.AccAddress(pubkey.Address().Bytes()) + return acc.String(), nil +} + func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index b78325300..f036f128d 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -1,6 +1,7 @@ package types import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -29,3 +30,7 @@ type IBCChannelKeeperExpected interface { GetChannel(ctx sdk.Context, portID, channelID string) (channel ibcchanneltypes.Channel, found bool) GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) } + +type AccountKeeperExpected interface { + GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (cryptotypes.PubKey, error) +} diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 80f240126..2507665d5 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -5,6 +5,9 @@ import ( "time" errorsmod "cosmossdk.io/errors" + abci "github.com/cometbft/cometbft/abci/types" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + cmttypes "github.com/cometbft/cometbft/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" @@ -21,17 +24,39 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { } // in case of msgcreateclient, validator info is not available. it is only send in msgupdateclient as header info // Check if the validator set hash matches the sequencer - if len(ibcState.Validator) > 0 && string(ibcState.Validator) != raState.BlockSequencer { + if len(ibcState.Validator) > 0 && !bytes.Equal(ibcState.Validator, raState.BlockSequencer) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator set hash does not match the sequencer") } // Check if the nextValidatorHash matches the sequencer for h+1 - if string(ibcState.NextValidatorsHash) != raState.NextBlockSequencer { + nextValHashFromStateInfo, err := getValHashForSequencer(raState.NextBlockSequencer) + if err != nil { + return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, err.Error()) + } + if !bytes.Equal(ibcState.NextValidatorsHash, nextValHashFromStateInfo) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1") } return nil } +func getValHashForSequencer(sequencerTmPubKeyBz []byte) ([]byte, error) { + var tmpk tmprotocrypto.PublicKey + err := tmpk.Unmarshal(sequencerTmPubKeyBz) + if err != nil { + return nil, err + } + var nextValSet cmttypes.ValidatorSet + updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: tmpk}}) + if err != nil { + return nil, err + } + err = nextValSet.UpdateWithChangeSet(updates) + if err != nil { + return nil, err + } + return nextValSet.Hash(), nil +} + type IBCState struct { Root []byte Height uint64 @@ -41,8 +66,8 @@ type IBCState struct { } type RollappState struct { - BlockSequencer string + BlockSequencer []byte BlockDescriptor rollapptypes.BlockDescriptor - NextBlockSequencer string + NextBlockSequencer []byte NextBlockDescriptor rollapptypes.BlockDescriptor } diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index cf11da171..5bcacc211 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -27,7 +27,7 @@ func TestCheckCompatibility(t *testing.T) { Root: []byte("root"), }, raState: types.RollappState{ - BlockSequencer: "sequencer", + BlockSequencer: []byte("sequencer"), BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("not root"), }, @@ -43,7 +43,7 @@ func TestCheckCompatibility(t *testing.T) { Timestamp: time.Now().UTC(), }, raState: types.RollappState{ - BlockSequencer: "sequencer", + BlockSequencer: []byte("sequencer"), BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), Timestamp: time.Now().UTC().Add(1), @@ -61,7 +61,7 @@ func TestCheckCompatibility(t *testing.T) { Validator: []byte("validator"), }, raState: types.RollappState{ - BlockSequencer: "sequencer", + BlockSequencer: []byte("sequencer"), BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), Timestamp: timestamp, @@ -80,12 +80,12 @@ func TestCheckCompatibility(t *testing.T) { Validator: []byte("sequencer"), }, raState: types.RollappState{ - BlockSequencer: "sequencer", + BlockSequencer: []byte("sequencer"), BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), Timestamp: timestamp, }, - NextBlockSequencer: "next sequencer", + NextBlockSequencer: []byte("next sequencer"), NextBlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root2"), Timestamp: timestamp, @@ -104,12 +104,12 @@ func TestCheckCompatibility(t *testing.T) { Validator: []byte("sequencer"), }, raState: types.RollappState{ - BlockSequencer: "sequencer", + BlockSequencer: []byte("sequencer"), BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), Timestamp: timestamp, }, - NextBlockSequencer: "next sequencer", + NextBlockSequencer: []byte("next sequencer"), NextBlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root2"), Timestamp: timestamp, From 763538d431d9c8d84f4ddbebeaab9e764de64097 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:50:28 +0530 Subject: [PATCH 24/87] fixing create client test --- .../ante/ibc_msg_create_client_test.go | 19 ++++++++++++++----- x/lightclient/ante/ibc_msgs_test.go | 2 +- x/lightclient/types/state.go | 1 + 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_create_client_test.go b/x/lightclient/ante/ibc_msg_create_client_test.go index d7c010236..a4cfc86dc 100644 --- a/x/lightclient/ante/ibc_msg_create_client_test.go +++ b/x/lightclient/ante/ibc_msg_create_client_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + abci "github.com/cometbft/cometbft/abci/types" + cmttypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -146,7 +148,7 @@ func TestHandleMsgCreateClient(t *testing.T) { StateInfoIndex: rollapptypes.StateInfoIndex{ Index: 1, }, - Sequencer: "sequencerAddr", + Sequencer: keepertest.Alice, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { @@ -197,7 +199,7 @@ func TestHandleMsgCreateClient(t *testing.T) { StateInfoIndex: rollapptypes.StateInfoIndex{ Index: 1, }, - Sequencer: "sequencerAddr", + Sequencer: keepertest.Alice, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { @@ -229,10 +231,17 @@ func TestHandleMsgCreateClient(t *testing.T) { testClientState.ChainId = "rollapp-wants-canon-client" clientState, err := ibcclienttypes.PackClientState(testClientState) require.NoError(t, err) + var nextVals cmttypes.ValidatorSet + tmPk, err := k.GetTmPubkey(ctx, keepertest.Alice) + require.NoError(t, err) + updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: tmPk}}) + require.NoError(t, err) + err = nextVals.UpdateWithChangeSet(updates) + require.NoError(t, err) testConsensusState := ibctm.NewConsensusState( blocktimestamp, commitmenttypes.NewMerkleRoot([]byte("appHash")), - []byte("sequencerAddr"), + nextVals.Hash(), ) consState, err := ibcclienttypes.PackConsensusState(testConsensusState) require.NoError(t, err) @@ -254,7 +263,7 @@ func TestHandleMsgCreateClient(t *testing.T) { StateInfoIndex: rollapptypes.StateInfoIndex{ Index: 1, }, - Sequencer: "sequencerAddr", + Sequencer: keepertest.Alice, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { @@ -277,7 +286,7 @@ func TestHandleMsgCreateClient(t *testing.T) { assert: func(ctx sdk.Context, k keeper.Keeper) { clientID, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") require.True(t, found) - require.Equal(t, "client-1", clientID) + require.Equal(t, "new-canon-client-1", clientID) }, }, } diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index 6426ca6c7..d02afd4f0 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -78,7 +78,7 @@ func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) ( } func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { - return "" + return "new-canon-client-1" } type MockIBCChannelKeeper struct{} diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 2507665d5..82036d7c2 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -40,6 +40,7 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { } func getValHashForSequencer(sequencerTmPubKeyBz []byte) ([]byte, error) { + // Creating a dummy tendermint validatorset to calculate the nextValHash var tmpk tmprotocrypto.PublicKey err := tmpk.Unmarshal(sequencerTmPubKeyBz) if err != nil { From 9000976438e4444fb2e17d81b451fb6cd8847106 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:52:18 +0530 Subject: [PATCH 25/87] fixing state compatibility tests --- x/lightclient/types/state_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 5bcacc211..1f97d7785 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -85,7 +85,7 @@ func TestCheckCompatibility(t *testing.T) { StateRoot: []byte("root"), Timestamp: timestamp, }, - NextBlockSequencer: []byte("next sequencer"), + NextBlockSequencer: []byte{10, 32, 6, 234, 170, 31, 60, 164, 237, 129, 237, 38, 152, 233, 81, 240, 243, 121, 79, 108, 152, 75, 27, 247, 76, 48, 15, 132, 61, 27, 161, 82, 197, 249}, NextBlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root2"), Timestamp: timestamp, @@ -100,7 +100,7 @@ func TestCheckCompatibility(t *testing.T) { ibcState: types.IBCState{ Root: []byte("root"), Timestamp: timestamp, - NextValidatorsHash: []byte("next sequencer"), + NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, Validator: []byte("sequencer"), }, raState: types.RollappState{ @@ -109,7 +109,7 @@ func TestCheckCompatibility(t *testing.T) { StateRoot: []byte("root"), Timestamp: timestamp, }, - NextBlockSequencer: []byte("next sequencer"), + NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, NextBlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root2"), Timestamp: timestamp, From 14ef08467d8623c324d4116f2c416b6c2c7b48a4 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:58:41 +0530 Subject: [PATCH 26/87] ensuring canon client params matches expected values --- x/lightclient/ante/ibc_msg_create_client.go | 11 +++ .../ante/ibc_msg_create_client_test.go | 74 ++++++++++++++++++- x/lightclient/types/params.go | 72 ++++++++++++++++++ 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 x/lightclient/types/params.go diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index 2e6db6026..96a6a38c2 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -95,6 +95,17 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli return // In case of incompatibility, the client will be created but not set as canonical } + // Ensure the light client params conform to expected values + if !types.IsCanonicalClientParamsValid(types.CanonicalClientParams{ + TrustLevel: tmClientState.TrustLevel, + TrustingPeriod: tmClientState.TrustingPeriod, + UnbondingPeriod: tmClientState.UnbondingPeriod, + MaxClockDrift: tmClientState.MaxClockDrift, + AllowUpdateAfterExpiry: tmClientState.AllowUpdateAfterExpiry, + AllowUpdateAfterMisbehaviour: tmClientState.AllowUpdateAfterMisbehaviour, + }) { + return + } // Generate client id and begin canonical light client registration by storing it in transient store. // Will be confirmed after the client is created in post handler. nextClientID := i.ibcClientKeeper.GenerateClientIdentifier(ctx, exported.Tendermint) diff --git a/x/lightclient/ante/ibc_msg_create_client_test.go b/x/lightclient/ante/ibc_msg_create_client_test.go index a4cfc86dc..084737f2c 100644 --- a/x/lightclient/ante/ibc_msg_create_client_test.go +++ b/x/lightclient/ante/ibc_msg_create_client_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + "github.com/cometbft/cometbft/libs/math" + abci "github.com/cometbft/cometbft/abci/types" cmttypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -225,10 +227,80 @@ func TestHandleMsgCreateClient(t *testing.T) { }, }, { - name: "State compatible - Candidate canonical client set", + name: "State compatible but client params not conforming to expected params", + prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { + blocktimestamp := time.Now().UTC() + testClientState.ChainId = "rollapp-wants-canon-client" + clientState, err := ibcclienttypes.PackClientState(testClientState) + require.NoError(t, err) + var nextVals cmttypes.ValidatorSet + tmPk, err := k.GetTmPubkey(ctx, keepertest.Alice) + require.NoError(t, err) + updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: tmPk}}) + require.NoError(t, err) + err = nextVals.UpdateWithChangeSet(updates) + require.NoError(t, err) + testConsensusState := ibctm.NewConsensusState( + blocktimestamp, + commitmenttypes.NewMerkleRoot([]byte("appHash")), + nextVals.Hash(), + ) + consState, err := ibcclienttypes.PackConsensusState(testConsensusState) + require.NoError(t, err) + return testInput{ + msg: &ibcclienttypes.MsgCreateClient{ + ClientState: clientState, + ConsensusState: consState, + }, + rollapps: map[string]rollapptypes.Rollapp{ + "rollapp-wants-canon-client": { + RollappId: "rollapp-wants-canon-client", + }, + }, + stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ + "rollapp-wants-canon-client": { + 1: { + StartHeight: 1, + NumBlocks: 2, + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 1, + }, + Sequencer: keepertest.Alice, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("appHash"), + Timestamp: blocktimestamp, + }, + { + Height: 2, + StateRoot: []byte("appHash2"), + Timestamp: blocktimestamp.Add(1), + }, + }, + }, + }, + }, + }, + } + }, + assert: func(ctx sdk.Context, k keeper.Keeper) { + _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") + require.False(t, found) + }, + }, + { + name: "State compatible + expected client params - Candidate canonical client set", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { blocktimestamp := time.Now().UTC() testClientState.ChainId = "rollapp-wants-canon-client" + testClientState.TrustLevel = ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}) + testClientState.TrustingPeriod = time.Hour * 24 * 7 * 2 + testClientState.UnbondingPeriod = time.Hour * 24 * 7 * 3 + testClientState.MaxClockDrift = time.Minute * 10 + testClientState.AllowUpdateAfterExpiry = false + testClientState.AllowUpdateAfterMisbehaviour = false clientState, err := ibcclienttypes.PackClientState(testClientState) require.NoError(t, err) var nextVals cmttypes.ValidatorSet diff --git a/x/lightclient/types/params.go b/x/lightclient/types/params.go new file mode 100644 index 000000000..228eab493 --- /dev/null +++ b/x/lightclient/types/params.go @@ -0,0 +1,72 @@ +package types + +import ( + "time" + + "github.com/cometbft/cometbft/libs/math" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ics23 "github.com/cosmos/ics23/go" +) + +type CanonicalClientParams struct { + // Trust level is the fraction of the trusted validator set + // that must sign over a new untrusted header before it is accepted. + // For a rollapp should be 1/1. + TrustLevel ibctm.Fraction + // TrustingPeriod is the duration of the period since the + // LatestTimestamp during which the submitted headers are valid for update. + TrustingPeriod time.Duration + // Unbonding period is the duration of the sequencer unbonding period. + UnbondingPeriod time.Duration + // MaxClockDrift defines how much new (untrusted) header's Time + // can drift into the future relative to our local clock. + MaxClockDrift time.Duration + // ProofSpecs defines the ICS-23 standard proof specifications used by + // the light client. It is used configure a proof for either existence + // or non-existence of a key value pair in state + ProofSpecs []*ics23.ProofSpec + AllowUpdateAfterExpiry bool + AllowUpdateAfterMisbehaviour bool +} + +var ( + ExpectedCanonicalClientParams = CanonicalClientParams{ + TrustLevel: ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}), + TrustingPeriod: time.Hour * 24 * 7 * 2, + UnbondingPeriod: time.Hour * 24 * 7 * 3, + MaxClockDrift: time.Minute * 10, + ProofSpecs: []*ics23.ProofSpec{ // the proofspecs for a SDK chain + ics23.IavlSpec, + ics23.TendermintSpec, + }, + AllowUpdateAfterExpiry: false, + AllowUpdateAfterMisbehaviour: false, + } +) + +func IsCanonicalClientParamsValid(params CanonicalClientParams) bool { + if params.TrustLevel != ExpectedCanonicalClientParams.TrustLevel { + return false + } + if params.TrustingPeriod > ExpectedCanonicalClientParams.TrustingPeriod { + return false + } + if params.UnbondingPeriod > ExpectedCanonicalClientParams.UnbondingPeriod { + return false + } + if params.MaxClockDrift > ExpectedCanonicalClientParams.MaxClockDrift { + return false + } + if params.AllowUpdateAfterExpiry != ExpectedCanonicalClientParams.AllowUpdateAfterExpiry { + return false + } + if params.AllowUpdateAfterMisbehaviour != ExpectedCanonicalClientParams.AllowUpdateAfterMisbehaviour { + return false + } + for i, proofSpec := range params.ProofSpecs { + if proofSpec != ExpectedCanonicalClientParams.ProofSpecs[i] { + return false + } + } + return true +} From 188e17ff47ad2d2c7b93473c895af78301b244d7 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:25:05 +0530 Subject: [PATCH 27/87] add case for when timestamp is not present as rollapp is not upgraded --- x/lightclient/keeper/hook_listener.go | 7 ++- x/lightclient/keeper/hook_listener_test.go | 52 ++++++++++++++--- x/lightclient/types/errors.go | 9 +++ x/lightclient/types/state.go | 13 +++-- x/lightclient/types/state_test.go | 57 ++++++++++++++----- ...block_height_to_finalization_queue_test.go | 8 ++- x/rollapp/keeper/msg_server_update_state.go | 8 ++- x/rollapp/types/hooks.go | 16 +++--- 8 files changed, 131 insertions(+), 39 deletions(-) create mode 100644 x/lightclient/types/errors.go diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 53d2fe3d2..be6f3fdbd 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -24,7 +24,7 @@ func (k Keeper) RollappHooks() rollapptypes.RollappHooks { return rollappHook{k: k} } -func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) error { +func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo, isFirstStateUpdate bool, previousStateHasTimestamp bool) error { canonicalClient, found := hook.k.GetCanonicalClient(ctx, rollappId) if !found { return nil @@ -72,6 +72,11 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat } err = types.CheckCompatibility(ibcState, rollappState) if err != nil { + // Only require timestamp on BD if first ever update, or the previous update had BD + if err == types.ErrTimestampNotFound && !isFirstStateUpdate && !previousStateHasTimestamp { + continue + } + // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index 546d23280..ef0c8b87d 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -10,8 +10,10 @@ import ( ) type testInput struct { - rollappId string - stateInfo *rollapptypes.StateInfo + rollappId string + stateInfo *rollapptypes.StateInfo + isFirstStateUpdate bool + previousStateHasTimestamp bool } func TestAfterUpdateState(t *testing.T) { @@ -26,15 +28,17 @@ func TestAfterUpdateState(t *testing.T) { { name: "canonical client does not exist for rollapp", input: testInput{ - rollappId: "rollapp-no-canon-client", - stateInfo: &rollapptypes.StateInfo{}, + rollappId: "rollapp-no-canon-client", + stateInfo: &rollapptypes.StateInfo{}, + isFirstStateUpdate: true, }, }, { name: "canonical client exists but the BDs are empty", input: testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{}, + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{}, + isFirstStateUpdate: true, }, }, { @@ -52,6 +56,7 @@ func TestAfterUpdateState(t *testing.T) { }, }, }, + isFirstStateUpdate: true, }, }, { @@ -74,6 +79,7 @@ func TestAfterUpdateState(t *testing.T) { }, }, }, + isFirstStateUpdate: true, }, }, { @@ -102,10 +108,40 @@ func TestAfterUpdateState(t *testing.T) { }, }, }, + isFirstStateUpdate: true, + }, + }, + { + name: "timestamp is missing and its first state update", + input: testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{ + Sequencer: keepertest.Alice, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("test2"), + Timestamp: time.Now().Add(1).UTC(), + }, + { + Height: 3, + StateRoot: []byte("test3"), + Timestamp: time.Now().Add(2).UTC(), + }, + }, + }, + }, + isFirstStateUpdate: true, }, }, { - name: "state is compatible - happy path", + name: "state is compatible", input: testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ @@ -135,7 +171,7 @@ func TestAfterUpdateState(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := keeper.RollappHooks().AfterUpdateState(ctx, tc.input.rollappId, tc.input.stateInfo) + err := keeper.RollappHooks().AfterUpdateState(ctx, tc.input.rollappId, tc.input.stateInfo, tc.input.isFirstStateUpdate, tc.input.previousStateHasTimestamp) require.NoError(t, err) }) } diff --git a/x/lightclient/types/errors.go b/x/lightclient/types/errors.go new file mode 100644 index 000000000..b0710a0b2 --- /dev/null +++ b/x/lightclient/types/errors.go @@ -0,0 +1,9 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + ErrTimestampNotFound = sdkerrors.Register(ModuleName, 2, "block descriptors do not contain block timestamp") +) diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 82036d7c2..13f579a34 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -18,16 +18,11 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { if !bytes.Equal(ibcState.Root, raState.BlockDescriptor.StateRoot) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") } - // Check if block descriptor timestamp matches IBC header timestamp - if !ibcState.Timestamp.Equal(raState.BlockDescriptor.Timestamp) { - return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp") - } // in case of msgcreateclient, validator info is not available. it is only send in msgupdateclient as header info // Check if the validator set hash matches the sequencer if len(ibcState.Validator) > 0 && !bytes.Equal(ibcState.Validator, raState.BlockSequencer) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator set hash does not match the sequencer") } - // Check if the nextValidatorHash matches the sequencer for h+1 nextValHashFromStateInfo, err := getValHashForSequencer(raState.NextBlockSequencer) if err != nil { @@ -36,6 +31,14 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { if !bytes.Equal(ibcState.NextValidatorsHash, nextValHashFromStateInfo) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1") } + // Check if block descriptor timestamp is not present - this happens if the rollapp has not upgraded yet + if raState.BlockDescriptor.Timestamp.IsZero() { + return ErrTimestampNotFound + } + // Check if block descriptor timestamp matches IBC header timestamp + if !ibcState.Timestamp.Equal(raState.BlockDescriptor.Timestamp) { + return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp") + } return nil } diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 1f97d7785..7dd9c5cfa 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -36,29 +36,31 @@ func TestCheckCompatibility(t *testing.T) { err: "block descriptor state root does not match tendermint header app hash", }, { - name: "timestamps are not equal", + name: "validator who signed the block header is not the sequencer who submitted the block", input: input{ ibcState: types.IBCState{ Root: []byte("root"), - Timestamp: time.Now().UTC(), + Timestamp: timestamp, + Validator: []byte("validator"), }, raState: types.RollappState{ BlockSequencer: []byte("sequencer"), BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), - Timestamp: time.Now().UTC().Add(1), + Timestamp: timestamp, }, }, }, - err: "block descriptor timestamp does not match tendermint header timestamp", + err: "validator set hash does not match the sequencer", }, { - name: "validator who signed the block header is not the sequencer who submitted the block", + name: "nextValidatorHash does not match the sequencer who submitted the next block descriptor", input: input{ ibcState: types.IBCState{ - Root: []byte("root"), - Timestamp: timestamp, - Validator: []byte("validator"), + Root: []byte("root"), + Timestamp: timestamp, + NextValidatorsHash: []byte("next validator hash"), + Validator: []byte("sequencer"), }, raState: types.RollappState{ BlockSequencer: []byte("sequencer"), @@ -66,33 +68,60 @@ func TestCheckCompatibility(t *testing.T) { StateRoot: []byte("root"), Timestamp: timestamp, }, + NextBlockSequencer: []byte{10, 32, 6, 234, 170, 31, 60, 164, 237, 129, 237, 38, 152, 233, 81, 240, 243, 121, 79, 108, 152, 75, 27, 247, 76, 48, 15, 132, 61, 27, 161, 82, 197, 249}, + NextBlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root2"), + Timestamp: timestamp, + }, }, }, - err: "validator set hash does not match the sequencer", + err: "next validator hash does not match the sequencer for h+1", }, { - name: "nextValidatorHash does not match the sequencer who submitted the next block descriptor", + name: "timestamps is empty", input: input{ ibcState: types.IBCState{ Root: []byte("root"), Timestamp: timestamp, - NextValidatorsHash: []byte("next validator hash"), + NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, Validator: []byte("sequencer"), }, raState: types.RollappState{ BlockSequencer: []byte("sequencer"), BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), - Timestamp: timestamp, }, - NextBlockSequencer: []byte{10, 32, 6, 234, 170, 31, 60, 164, 237, 129, 237, 38, 152, 233, 81, 240, 243, 121, 79, 108, 152, 75, 27, 247, 76, 48, 15, 132, 61, 27, 161, 82, 197, 249}, + NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, + NextBlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root2"), + }, + }, + }, + err: "block descriptors do not contain block timestamp", + }, + { + name: "timestamps are not equal", + input: input{ + ibcState: types.IBCState{ + Root: []byte("root"), + Timestamp: timestamp, + NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, + Validator: []byte("sequencer"), + }, + raState: types.RollappState{ + BlockSequencer: []byte("sequencer"), + BlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root"), + Timestamp: timestamp.Add(1), + }, + NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, NextBlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root2"), Timestamp: timestamp, }, }, }, - err: "next validator hash does not match the sequencer for h+1", + err: "block descriptor timestamp does not match tendermint header timestamp", }, { name: "all fields are compatible", diff --git a/x/rollapp/keeper/block_height_to_finalization_queue_test.go b/x/rollapp/keeper/block_height_to_finalization_queue_test.go index 6af1a9e65..dc3f8aa0f 100644 --- a/x/rollapp/keeper/block_height_to_finalization_queue_test.go +++ b/x/rollapp/keeper/block_height_to_finalization_queue_test.go @@ -652,9 +652,11 @@ func (m mockRollappHooks) AfterStateFinalized(_ sdk.Context, _ string, stateInfo } return } -func (m mockRollappHooks) BeforeUpdateState(sdk.Context, string, string) error { return nil } -func (m mockRollappHooks) AfterUpdateState(sdk.Context, string, *types.StateInfo) error { return nil } -func (m mockRollappHooks) FraudSubmitted(sdk.Context, string, uint64, string) error { return nil } +func (m mockRollappHooks) BeforeUpdateState(sdk.Context, string, string) error { return nil } +func (m mockRollappHooks) AfterUpdateState(sdk.Context, string, *types.StateInfo, bool, bool) error { + return nil +} +func (m mockRollappHooks) FraudSubmitted(sdk.Context, string, uint64, string) error { return nil } func (m mockRollappHooks) RollappCreated(sdk.Context, string, string, sdk.AccAddress) error { return nil } diff --git a/x/rollapp/keeper/msg_server_update_state.go b/x/rollapp/keeper/msg_server_update_state.go index babe33e69..1f266dfc8 100644 --- a/x/rollapp/keeper/msg_server_update_state.go +++ b/x/rollapp/keeper/msg_server_update_state.go @@ -31,7 +31,9 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) // retrieve last updating index var newIndex, lastIndex uint64 + var previousStateHasTimestamp bool latestStateInfoIndex, found := k.GetLatestStateInfoIndex(ctx, msg.RollappId) + isFirstStateUpdate := !found if found { // retrieve last updating index stateInfo, found := k.GetStateInfo(ctx, msg.RollappId, latestStateInfoIndex.Index) @@ -43,6 +45,10 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) latestStateInfoIndex.Index, msg.RollappId) } + // check if previous state last block desc has timestamp + lastBD := stateInfo.GetBDs().BD[stateInfo.NumBlocks-1] + previousStateHasTimestamp = !lastBD.Timestamp.IsZero() + // check to see if received height is the one we expected expectedStartHeight := stateInfo.StartHeight + stateInfo.NumBlocks if expectedStartHeight != msg.StartHeight { @@ -67,7 +73,7 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) // Write new state information to the store indexed by k.SetStateInfo(ctx, *stateInfo) - err = k.hooks.AfterUpdateState(ctx, msg.RollappId, stateInfo) + err = k.hooks.AfterUpdateState(ctx, msg.RollappId, stateInfo, isFirstStateUpdate, previousStateHasTimestamp) if err != nil { return nil, err } diff --git a/x/rollapp/types/hooks.go b/x/rollapp/types/hooks.go index 6bd8b2c4a..96fcb5038 100644 --- a/x/rollapp/types/hooks.go +++ b/x/rollapp/types/hooks.go @@ -11,9 +11,9 @@ import ( // RollappHooks event hooks for rollapp object (noalias) type RollappHooks interface { - BeforeUpdateState(ctx sdk.Context, seqAddr string, rollappId string) error // Must be called when a rollapp's state changes - AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error // Must be called when a rollapp's state changes - AfterStateFinalized(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error // Must be called when a rollapp's state changes + BeforeUpdateState(ctx sdk.Context, seqAddr string, rollappId string) error // Must be called when a rollapp's state changes + AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo, isFirstStateUpdate bool, previousStateHasTimestamp bool) error // Must be called when a rollapp's state changes + AfterStateFinalized(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error // Must be called when a rollapp's state changes FraudSubmitted(ctx sdk.Context, rollappID string, height uint64, seqAddr string) error RollappCreated(ctx sdk.Context, rollappID, alias string, creator sdk.AccAddress) error } @@ -38,9 +38,9 @@ func (h MultiRollappHooks) BeforeUpdateState(ctx sdk.Context, seqAddr string, ro return nil } -func (h MultiRollappHooks) AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error { +func (h MultiRollappHooks) AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo, isFirstStateUpdate bool, previousStateHasTimestamp bool) error { for i := range h { - err := h[i].AfterUpdateState(ctx, rollappID, stateInfo) + err := h[i].AfterUpdateState(ctx, rollappID, stateInfo, isFirstStateUpdate, previousStateHasTimestamp) if err != nil { return err } @@ -84,8 +84,10 @@ type StubRollappCreatedHooks struct{} func (StubRollappCreatedHooks) RollappCreated(sdk.Context, string, string, sdk.AccAddress) error { return nil } -func (StubRollappCreatedHooks) BeforeUpdateState(sdk.Context, string, string) error { return nil } -func (StubRollappCreatedHooks) AfterUpdateState(sdk.Context, string, *StateInfo) error { return nil } +func (StubRollappCreatedHooks) BeforeUpdateState(sdk.Context, string, string) error { return nil } +func (StubRollappCreatedHooks) AfterUpdateState(sdk.Context, string, *StateInfo, bool, bool) error { + return nil +} func (StubRollappCreatedHooks) FraudSubmitted(sdk.Context, string, uint64, string) error { return nil } func (StubRollappCreatedHooks) AfterStateFinalized(sdk.Context, string, *StateInfo) error { return nil From 937611712ac86b9447d74143aeb24dd047496607 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 21 Aug 2024 20:06:19 +0530 Subject: [PATCH 28/87] cleaning up the code --- app/post/post.go | 1 - app/upgrades/v4/upgrade.go | 2 +- x/lightclient/ante/ibc_msg_create_client.go | 16 ++++----------- x/lightclient/ante/ibc_msg_update_client.go | 22 ++++++++++----------- x/lightclient/module.go | 2 +- x/lightclient/post/ibc_msg_create_client.go | 1 + x/lightclient/types/keys.go | 11 ++++------- x/lightclient/types/params.go | 18 ++++++++--------- x/lightclient/types/state.go | 2 +- 9 files changed, 32 insertions(+), 43 deletions(-) diff --git a/app/post/post.go b/app/post/post.go index e269bc187..3e307d7a2 100644 --- a/app/post/post.go +++ b/app/post/post.go @@ -16,7 +16,6 @@ type HandlerOptions struct { LightClientKeeper *lightclientkeeper.Keeper } -// NewPostHandler returns an empty PostHandler chain. func NewPostHandler(options HandlerOptions) (sdk.PostHandler, error) { if err := options.validate(); err != nil { return nil, err diff --git a/app/upgrades/v4/upgrade.go b/app/upgrades/v4/upgrade.go index d7d3556f5..1838dc45d 100644 --- a/app/upgrades/v4/upgrade.go +++ b/app/upgrades/v4/upgrade.go @@ -150,7 +150,7 @@ func migrateRollappLightClients(ctx sdk.Context, rollappkeeper *rollappkeeper.Ke return } clientID := connection.GetClientID() - // todo: should this be canonical client? or candidate client? + // store the rollapp to canonical light client ID mapping lightClientKeeper.SetCanonicalClient(ctx, rollapp.RollappId, clientID) } } diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go index 96a6a38c2..a7b163252 100644 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ b/x/lightclient/ante/ibc_msg_create_client.go @@ -11,7 +11,6 @@ import ( ) func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibcclienttypes.MsgCreateClient) { - // Unpack client state from message clientState, err := ibcclienttypes.UnpackClientState(msg.ClientState) if err != nil { return @@ -41,7 +40,7 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli return } blockDescriptor := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] - // Unpack consensus state from message + consensusState, err := ibcclienttypes.UnpackConsensusState(msg.ConsensusState) if err != nil { return @@ -57,7 +56,7 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), Height: tmClientState.GetLatestHeight().GetRevisionHeight(), - Validator: []byte{}, // not sure if this info is available in the tendermint consensus state as no header has been shared yet + Validator: []byte{}, // This info is available in the tendermint consensus state as no header has been shared yet NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: timestamp, } @@ -96,15 +95,8 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli } // Ensure the light client params conform to expected values - if !types.IsCanonicalClientParamsValid(types.CanonicalClientParams{ - TrustLevel: tmClientState.TrustLevel, - TrustingPeriod: tmClientState.TrustingPeriod, - UnbondingPeriod: tmClientState.UnbondingPeriod, - MaxClockDrift: tmClientState.MaxClockDrift, - AllowUpdateAfterExpiry: tmClientState.AllowUpdateAfterExpiry, - AllowUpdateAfterMisbehaviour: tmClientState.AllowUpdateAfterMisbehaviour, - }) { - return + if !types.IsCanonicalClientParamsValid(tmClientState) { + return // In case of invalid params, the client will be created but not set as canonical } // Generate client id and begin canonical light client registration by storing it in transient store. // Will be confirmed after the client is created in post handler. diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 6f2a0e0e6..fdbb0b881 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -13,7 +13,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli if !found { return nil } - // Only continue if the client is a tendermint client as rollapp only supports tendermint clients as canoncial clients + // Only continue if the client is a tendermint client as rollapp only supports tendermint clients as canonical clients if clientState.ClientType() == exported.Tendermint { // Cast client state to tendermint client state - we need this to get the chain id(rollapp id) tmClientState, ok := clientState.(*ibctm.ClientState) @@ -62,35 +62,35 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli BlockSequencer: sequencerPubKey, BlockDescriptor: bd, } - // Check that BD for next block exists + // Check that BD for next block exists in same stateinfo if height.GetRevisionHeight()-stateInfo.GetStartHeight()+1 < uint64(len(stateInfo.GetBDs().BD)) { rollappState.NextBlockSequencer = sequencerPubKey rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()+1] } else { // next BD does not exist in this state info, check the next state info nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) - if !found { - // if next state info does not exist, then we can't verify the next block valhash. - // Will accept the update optimistically - // But also save the blockProposer address with the height for future verification - blockProposer := header.Header.ProposerAddress - i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), string(blockProposer)) - return nil - } else { + if found { nextSequencerPk, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, nextStateInfo.Sequencer) if err != nil { return err } rollappState.NextBlockSequencer = nextSequencerPk rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] + } else { + // if next state info does not exist, then we can't verify the next block valhash. + // Will accept the update optimistically + // But also save the blockProposer address with the height for future verification + blockProposer := header.Header.ProposerAddress + i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), string(blockProposer)) + return nil } } // Ensure that the ibc header is compatible with the existing rollapp state + // If its not, we error and prevent the MsgUpdateClient from being processed err = types.CheckCompatibility(ibcState, rollappState) if err != nil { return err } } - return nil } diff --git a/x/lightclient/module.go b/x/lightclient/module.go index 3ae5139db..4b4f59a94 100644 --- a/x/lightclient/module.go +++ b/x/lightclient/module.go @@ -75,7 +75,7 @@ func (a AppModuleBasic) GetTxCmd() *cobra.Command { // GetQueryCmd returns the capability module's root query command. func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return nil //cli.GetQueryCmd(types.StoreKey) + return nil } // ---------------------------------------------------------------------------- diff --git a/x/lightclient/post/ibc_msg_create_client.go b/x/lightclient/post/ibc_msg_create_client.go index 2152f3c88..631458dc0 100644 --- a/x/lightclient/post/ibc_msg_create_client.go +++ b/x/lightclient/post/ibc_msg_create_client.go @@ -31,5 +31,6 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli } } + // Always clear the registration after the tx as the transient store is shared amoung all txs in the block i.lightClientKeeper.ClearCanonicalLightClientRegistration(ctx, rollappID) } diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go index 2e3a4f9ea..3ca68e029 100644 --- a/x/lightclient/types/keys.go +++ b/x/lightclient/types/keys.go @@ -11,9 +11,6 @@ const ( // StoreKey defines the primary module store key StoreKey = ModuleName - // QuerierRoute defines the module's query routing key - QuerierRoute = ModuleName - // TransientKey defines the module's transient store key TransientKey = "t_lightclient" ) @@ -21,13 +18,13 @@ const ( // KV Store var ( rollappClientKey = []byte{0x01} - ConsensusStateSignerKey = []byte{0x03} + consensusStateSignerKey = []byte{0x03} canonicalClientKey = []byte{0x04} ) // Transient Store var ( - LightClientRegistrationKey = []byte{0x02} + lightClientRegistrationKey = []byte{0x02} ) func RollappClientKey(rollappId string) []byte { @@ -35,12 +32,12 @@ func RollappClientKey(rollappId string) []byte { } func CanonicalLightClientRegistrationKey(rollappId string) []byte { - return append(LightClientRegistrationKey, []byte(rollappId)...) + return append(lightClientRegistrationKey, []byte(rollappId)...) } func ConsensusStateSignerKeyByClientID(clientID string, height uint64) []byte { prefix := append([]byte(clientID), sdk.Uint64ToBigEndian(height)...) - return append(ConsensusStateSignerKey, prefix...) + return append(consensusStateSignerKey, prefix...) } func CanonicalClientKey(clientID string) []byte { diff --git a/x/lightclient/types/params.go b/x/lightclient/types/params.go index 228eab493..287ed9e2d 100644 --- a/x/lightclient/types/params.go +++ b/x/lightclient/types/params.go @@ -23,7 +23,7 @@ type CanonicalClientParams struct { MaxClockDrift time.Duration // ProofSpecs defines the ICS-23 standard proof specifications used by // the light client. It is used configure a proof for either existence - // or non-existence of a key value pair in state + // or non-existence of a key value pair ProofSpecs []*ics23.ProofSpec AllowUpdateAfterExpiry bool AllowUpdateAfterMisbehaviour bool @@ -44,26 +44,26 @@ var ( } ) -func IsCanonicalClientParamsValid(params CanonicalClientParams) bool { - if params.TrustLevel != ExpectedCanonicalClientParams.TrustLevel { +func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { + if clientState.TrustLevel != ExpectedCanonicalClientParams.TrustLevel { return false } - if params.TrustingPeriod > ExpectedCanonicalClientParams.TrustingPeriod { + if clientState.TrustingPeriod > ExpectedCanonicalClientParams.TrustingPeriod { return false } - if params.UnbondingPeriod > ExpectedCanonicalClientParams.UnbondingPeriod { + if clientState.UnbondingPeriod > ExpectedCanonicalClientParams.UnbondingPeriod { return false } - if params.MaxClockDrift > ExpectedCanonicalClientParams.MaxClockDrift { + if clientState.MaxClockDrift > ExpectedCanonicalClientParams.MaxClockDrift { return false } - if params.AllowUpdateAfterExpiry != ExpectedCanonicalClientParams.AllowUpdateAfterExpiry { + if clientState.AllowUpdateAfterExpiry != ExpectedCanonicalClientParams.AllowUpdateAfterExpiry { return false } - if params.AllowUpdateAfterMisbehaviour != ExpectedCanonicalClientParams.AllowUpdateAfterMisbehaviour { + if clientState.AllowUpdateAfterMisbehaviour != ExpectedCanonicalClientParams.AllowUpdateAfterMisbehaviour { return false } - for i, proofSpec := range params.ProofSpecs { + for i, proofSpec := range clientState.ProofSpecs { if proofSpec != ExpectedCanonicalClientParams.ProofSpecs[i] { return false } diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 13f579a34..81edfbc3e 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -18,7 +18,7 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { if !bytes.Equal(ibcState.Root, raState.BlockDescriptor.StateRoot) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") } - // in case of msgcreateclient, validator info is not available. it is only send in msgupdateclient as header info + // in case of msgcreateclient, validator info is not available. it is only sent in msgupdateclient as header info // Check if the validator set hash matches the sequencer if len(ibcState.Validator) > 0 && !bytes.Equal(ibcState.Validator, raState.BlockSequencer) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator set hash does not match the sequencer") From 09e9d001abb302e6525042de90465bb8e5e605dd Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:16:53 +0530 Subject: [PATCH 29/87] cleanup tests --- testutil/keeper/lightclient.go | 58 +++-- .../ante/ibc_msg_channel_open_ack_test.go | 16 +- .../ante/ibc_msg_create_client_test.go | 14 +- .../ante/ibc_msg_submit_misbehaviour_test.go | 13 +- .../ante/ibc_msg_update_client_test.go | 85 +----- x/lightclient/ante/ibc_msgs_test.go | 55 ++-- x/lightclient/keeper/hook_listener.go | 20 +- x/lightclient/keeper/hook_listener_test.go | 241 +++++++++--------- .../post/ibc_msg_create_client_test.go | 16 +- x/lightclient/post/ibc_msgs_test.go | 26 +- x/lightclient/types/errors.go | 3 +- x/lightclient/types/state.go | 37 ++- x/lightclient/types/state_test.go | 27 +- 13 files changed, 306 insertions(+), 305 deletions(-) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index ba2e1e88b..6c320ea21 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -4,9 +4,9 @@ import ( "testing" "time" - cmttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -21,7 +21,6 @@ import ( cometbftdb "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/libs/log" cometbftproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/stretchr/testify/require" ) @@ -44,9 +43,23 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { registry := codectypes.NewInterfaceRegistry() cdc := codec.NewProtoCodec(registry) - mockIBCKeeper := NewMockIBCClientKeeper() + testConsensusStates := map[string]map[uint64]exported.ConsensusState{ + "canon-client-id": { + 2: &ibctm.ConsensusState{ + Timestamp: time.Now().UTC(), + Root: commitmenttypes.NewMerkleRoot([]byte("test2")), + NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, + }, + }, + } + sequencerPubKey := ed25519.GenPrivKey().PubKey() + testPubKeys := map[string]cryptotypes.PubKey{ + Alice: sequencerPubKey, + } + + mockIBCKeeper := NewMockIBCClientKeeper(testConsensusStates) mockSequencerKeeper := NewMockSequencerKeeper() - mockAccountKeeper := NewMockAccountKeeper() + mockAccountKeeper := NewMockAccountKeeper(testPubKeys) k := keeper.NewKeeper( cdc, storeKey, @@ -61,27 +74,18 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { } type MockIBCCLientKeeper struct { + clientConsensusState map[string]map[uint64]exported.ConsensusState } -func NewMockIBCClientKeeper() *MockIBCCLientKeeper { - return &MockIBCCLientKeeper{} +func NewMockIBCClientKeeper(clientCS map[string]map[uint64]exported.ConsensusState) *MockIBCCLientKeeper { + return &MockIBCCLientKeeper{ + clientConsensusState: clientCS, + } } func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { - if clientID == "canon-client-id-no-state" { - return nil, false - } - if clientID == "canon-client-id" && height.GetRevisionHeight() == 2 { - val := cmttypes.NewMockPV() - pk, _ := val.GetPubKey() - cs := ibctm.NewConsensusState( - time.Now().UTC(), - commitmenttypes.NewMerkleRoot([]byte("test2")), - cmttypes.NewValidatorSet([]*cmttypes.Validator{cmttypes.NewValidator(pk, 1)}).Hash(), - ) - return cs, true - } - return nil, false + cs, ok := m.clientConsensusState[clientID][height.GetRevisionHeight()] + return cs, ok } func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { @@ -104,19 +108,19 @@ func (m *MockSequencerKeeper) SlashAndJailFraud(ctx sdk.Context, seqAddr string) } type MockAccountKeeper struct { - pubkey cryptotypes.PubKey + pubkeys map[string]cryptotypes.PubKey } -func NewMockAccountKeeper() *MockAccountKeeper { - pubkey := ed25519.GenPrivKey().PubKey() +func NewMockAccountKeeper(pubkeys map[string]cryptotypes.PubKey) *MockAccountKeeper { return &MockAccountKeeper{ - pubkey: pubkey, + pubkeys: pubkeys, } } func (m *MockAccountKeeper) GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (cryptotypes.PubKey, error) { - if addr.String() == Alice { - return m.pubkey, nil + pubkey, ok := m.pubkeys[addr.String()] + if !ok { + return nil, sdkerrors.ErrUnknownAddress } - return nil, sdkerrors.ErrUnknownAddress + return pubkey, nil } diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go index 645f978b8..9a243bc04 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go @@ -3,6 +3,7 @@ package ante_test import ( "testing" + ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" @@ -22,9 +23,20 @@ func TestHandleMsgChannelOpenAck(t *testing.T) { ChannelId: "", }, } + testConnections := map[string]ibcconnectiontypes.ConnectionEnd{ + "new-channel-on-canon-client": { + ClientId: "canon-client-id", + }, + "first-channel-on-canon-client": { + ClientId: "canon-client-id-2", + }, + "non-canon-channel-id": { + ClientId: "non-canon-client-id", + }, + } rollappKeeper := NewMockRollappKeeper(testRollapps, nil) - ibcclientKeeper := NewMockIBCClientKeeper() - ibcchannelKeeper := NewMockIBCChannelKeeper() + ibcclientKeeper := NewMockIBCClientKeeper(nil) + ibcchannelKeeper := NewMockIBCChannelKeeper(testConnections) keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") keeper.SetCanonicalClient(ctx, "rollapp-no-canon-channel", "canon-client-id-2") ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) diff --git a/x/lightclient/ante/ibc_msg_create_client_test.go b/x/lightclient/ante/ibc_msg_create_client_test.go index 084737f2c..5cc88d781 100644 --- a/x/lightclient/ante/ibc_msg_create_client_test.go +++ b/x/lightclient/ante/ibc_msg_create_client_test.go @@ -76,7 +76,7 @@ func TestHandleMsgCreateClient(t *testing.T) { } }, assert: func(ctx sdk.Context, k keeper.Keeper) { - _, found := k.GetCanonicalClient(ctx, "not-a-rollapp") + _, found := k.GetCanonicalLightClientRegistration(ctx, "not-a-rollapp") require.False(t, found) }, }, @@ -103,6 +103,8 @@ func TestHandleMsgCreateClient(t *testing.T) { clientID, found := k.GetCanonicalClient(ctx, "rollapp-has-canon-client") require.True(t, found) require.Equal(t, "canon-client-id", clientID) + _, registrationPending := k.GetCanonicalLightClientRegistration(ctx, "rollapp-has-canon-client") + require.False(t, registrationPending) }, }, { @@ -128,7 +130,7 @@ func TestHandleMsgCreateClient(t *testing.T) { }, }, { - name: "Could not find block descriptor for the next height (h+1)", + name: "Could not find block descriptor for the next height (h+1) - continue without setting as canonical client", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { testClientState.ChainId = "rollapp-wants-canon-client" cs, err := ibcclienttypes.PackClientState(testClientState) @@ -171,7 +173,7 @@ func TestHandleMsgCreateClient(t *testing.T) { }, }, { - name: "State incompatible", + name: "State incompatible - continue without setting as canonical client", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { testClientState.ChainId = "rollapp-wants-canon-client" clientState, err := ibcclienttypes.PackClientState(testClientState) @@ -227,7 +229,7 @@ func TestHandleMsgCreateClient(t *testing.T) { }, }, { - name: "State compatible but client params not conforming to expected params", + name: "State compatible but client params not conforming to expected params - continue without setting as canonical client", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { blocktimestamp := time.Now().UTC() testClientState.ChainId = "rollapp-wants-canon-client" @@ -365,8 +367,8 @@ func TestHandleMsgCreateClient(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) - ibcclientKeeper := NewMockIBCClientKeeper() - ibcchannelKeeper := NewMockIBCChannelKeeper() + ibcclientKeeper := NewMockIBCClientKeeper(nil) + ibcchannelKeeper := NewMockIBCChannelKeeper(nil) input := tc.prepare(ctx, *keeper) rollappKeeper := NewMockRollappKeeper(input.rollapps, input.stateInfos) ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go index 9621a93da..66cb97296 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go @@ -4,6 +4,9 @@ import ( "testing" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" @@ -13,8 +16,14 @@ import ( func TestHandleMsgSubmitMisbehaviour(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) rollappKeeper := NewMockRollappKeeper(nil, nil) - ibcclientKeeper := NewMockIBCClientKeeper() - ibcchannelKeeper := NewMockIBCChannelKeeper() + testClientStates := map[string]exported.ClientState{ + "non-tm-client-id": &ibcsolomachine.ClientState{}, + "canon-client-id": &ibctm.ClientState{ + ChainId: "rollapp-has-canon-client", + }, + } + ibcclientKeeper := NewMockIBCClientKeeper(testClientStates) + ibcchannelKeeper := NewMockIBCChannelKeeper(nil) keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) testCases := []struct { diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index 7dfdd1f73..1f212246e 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -11,6 +11,8 @@ import ( cmttypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" @@ -73,10 +75,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { name: "Could not find state info for height - ensure optimistically accepted and signer stored in state", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - var ( - valSet *cmtproto.ValidatorSet - trustedVals *cmtproto.ValidatorSet - ) + var valSet, trustedVals *cmtproto.ValidatorSet signedHeader := &cmtproto.SignedHeader{ Header: &cmtproto.Header{ ProposerAddress: []byte("sequencerAddr"), @@ -133,73 +132,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { }, }, { - name: "BD does not include next block in state info - ensure optimistically accepted and signer stored in state", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - var ( - valSet *cmtproto.ValidatorSet - trustedVals *cmtproto.ValidatorSet - ) - signedHeader := &cmtproto.SignedHeader{ - Header: &cmtproto.Header{ - AppHash: []byte("appHash"), - ProposerAddress: []byte("sequencerAddr"), - Time: time.Now().UTC(), - NextValidatorsHash: []byte("nextValHash"), - }, - Commit: &cmtproto.Commit{}, - } - header := ibctm.Header{ - SignedHeader: signedHeader, - ValidatorSet: valSet, - TrustedHeight: ibcclienttypes.MustParseHeight("1-1"), - TrustedValidators: trustedVals, - } - clientMsg, err := ibcclienttypes.PackClientMessage(&header) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgUpdateClient{ - ClientId: "canon-client-id", - ClientMessage: clientMsg, - Signer: "relayerAddr", - }, - rollapps: map[string]rollapptypes.Rollapp{ - "rollapp-has-canon-client": { - RollappId: "rollapp-has-canon-client", - }, - }, - stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ - "rollapp-has-canon-client": { - 1: { - Sequencer: keepertest.Alice, - StateInfoIndex: rollapptypes.StateInfoIndex{ - Index: 1, - }, - StartHeight: 1, - NumBlocks: 1, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte{}, - Timestamp: time.Now().UTC(), - }, - }, - }, - }, - }, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper, err error) { - require.NoError(t, err) - signer, found := k.GetConsensusStateSigner(ctx, "canon-client-id", 1) - require.True(t, found) - require.Equal(t, "sequencerAddr", signer) - }, - }, - { - name: "Ensure state is incompatible - err", + name: "State is incompatible", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") var ( @@ -350,8 +283,14 @@ func TestHandleMsgUpdateClient(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) - ibcclientKeeper := NewMockIBCClientKeeper() - ibcchannelKeeper := NewMockIBCChannelKeeper() + testClientStates := map[string]exported.ClientState{ + "non-tm-client-id": &ibcsolomachine.ClientState{}, + "canon-client-id": &ibctm.ClientState{ + ChainId: "rollapp-has-canon-client", + }, + } + ibcclientKeeper := NewMockIBCClientKeeper(testClientStates) + ibcchannelKeeper := NewMockIBCChannelKeeper(nil) input := tc.prepare(ctx, *keeper) rollappKeeper := NewMockRollappKeeper(input.rollapps, input.stateInfos) ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index d02afd4f0..f10c52eca 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -6,8 +6,6 @@ import ( ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) @@ -53,38 +51,37 @@ func (m *MockRollappKeeper) GetStateInfo(ctx sdk.Context, rollappId string, inde return val, found } -type MockIBCCLientKeeper struct{} +type MockIBCClientKeeper struct { + clientStates map[string]exported.ClientState +} -func NewMockIBCClientKeeper() *MockIBCCLientKeeper { - return &MockIBCCLientKeeper{} +func NewMockIBCClientKeeper(cs map[string]exported.ClientState) *MockIBCClientKeeper { + return &MockIBCClientKeeper{ + clientStates: cs, + } } -func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { +func (m *MockIBCClientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { return nil, false } -func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { - switch clientID { - case "non-tm-client-id": - clientState := ibcsolomachine.ClientState{} - return &clientState, true - case "canon-client-id": - clientState := ibctm.ClientState{ - ChainId: "rollapp-has-canon-client", - } - return &clientState, true - } - return nil, false +func (m *MockIBCClientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { + val, found := m.clientStates[clientID] + return val, found } -func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { +func (m *MockIBCClientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { return "new-canon-client-1" } -type MockIBCChannelKeeper struct{} +type MockIBCChannelKeeper struct { + channelConnections map[string]ibcconnectiontypes.ConnectionEnd +} -func NewMockIBCChannelKeeper() *MockIBCChannelKeeper { - return &MockIBCChannelKeeper{} +func NewMockIBCChannelKeeper(connections map[string]ibcconnectiontypes.ConnectionEnd) *MockIBCChannelKeeper { + return &MockIBCChannelKeeper{ + channelConnections: connections, + } } func (m *MockIBCChannelKeeper) GetChannel(ctx sdk.Context, portID, channelID string) (channel ibcchanneltypes.Channel, found bool) { @@ -93,19 +90,7 @@ func (m *MockIBCChannelKeeper) GetChannel(ctx sdk.Context, portID, channelID str func (m *MockIBCChannelKeeper) GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) { if portID == "transfer" { - if channelID == "new-channel-on-canon-client" { - return "", ibcconnectiontypes.ConnectionEnd{ - ClientId: "canon-client-id", - }, nil - } - if channelID == "first-channel-on-canon-client" { - return "", ibcconnectiontypes.ConnectionEnd{ - ClientId: "canon-client-id-2", - }, nil - } - return "", ibcconnectiontypes.ConnectionEnd{ - ClientId: "non-canon-client-id", - }, nil + return "", m.channelConnections[channelID], nil } return "", nil, nil } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index be6f3fdbd..baa846a0e 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -24,7 +24,13 @@ func (k Keeper) RollappHooks() rollapptypes.RollappHooks { return rollappHook{k: k} } -func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo, isFirstStateUpdate bool, previousStateHasTimestamp bool) error { +func (hook rollappHook) AfterUpdateState( + ctx sdk.Context, + rollappId string, + stateInfo *rollapptypes.StateInfo, + isFirstStateUpdate bool, + previousStateHasTimestamp bool, +) error { canonicalClient, found := hook.k.GetCanonicalClient(ctx, rollappId) if !found { return nil @@ -37,10 +43,7 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat continue } height := ibcclienttypes.NewHeight(1, bd.GetHeight()) - consensusState, found := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) - if !found { - return nil - } + consensusState, _ := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) if !ok { @@ -65,7 +68,7 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat BlockSequencer: sequencerPk, BlockDescriptor: bd, } - // check if bd for next block exists + // check if bd for next block exists in same state info if i+1 < len(bds.GetBD()) { rollappState.NextBlockSequencer = sequencerPk rollappState.NextBlockDescriptor = bds.GetBD()[i+1] @@ -77,6 +80,11 @@ func (hook rollappHook) AfterUpdateState(ctx sdk.Context, rollappId string, stat continue } + // The BD for (h+1) is missing, cannot verify if the nextvalhash matches + if err == types.ErrNextBlockDescriptorMissing { + return err + } + // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index ef0c8b87d..8d62789c3 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -4,7 +4,9 @@ import ( "testing" "time" + sdk "github.com/cosmos/cosmos-sdk/types" keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" + lightClientKeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" "github.com/stretchr/testify/require" ) @@ -17,162 +19,169 @@ type testInput struct { } func TestAfterUpdateState(t *testing.T) { - keeper, ctx := keepertest.LightClientKeeper(t) - keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client-but-no-state", "canon-client-id-no-state") - keeper.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - testCases := []struct { - name string - input testInput + name string + prepare func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput + expectErr bool }{ { name: "canonical client does not exist for rollapp", - input: testInput{ - rollappId: "rollapp-no-canon-client", - stateInfo: &rollapptypes.StateInfo{}, - isFirstStateUpdate: true, + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { + return testInput{ + rollappId: "rollapp-no-canon-client", + stateInfo: &rollapptypes.StateInfo{}, + isFirstStateUpdate: true, + } }, + expectErr: false, }, { name: "canonical client exists but the BDs are empty", - input: testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{}, - isFirstStateUpdate: true, + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + return testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{}, + isFirstStateUpdate: true, + } }, + expectErr: false, }, { - name: "canonical client exists but consensus state is not found", - input: testInput{ - rollappId: "rollapp-has-canon-client-but-no-state", - stateInfo: &rollapptypes.StateInfo{ - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), + name: "canonical client exists but consensus state is not found for given height", + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client-but-no-state", "canon-client-id-no-state") + return testInput{ + rollappId: "rollapp-has-canon-client-but-no-state", + stateInfo: &rollapptypes.StateInfo{ + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, }, }, }, - }, - isFirstStateUpdate: true, + isFirstStateUpdate: true, + } }, + expectErr: false, }, { name: "BD does not include next block in state info", - input: testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{ - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), - }, - { - Height: 2, - StateRoot: []byte("test2"), - Timestamp: time.Now().Add(1).UTC(), + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + blockSignerTmPubKey, err := k.GetTmPubkeyAsBytes(ctx, keepertest.Alice) + require.NoError(t, err) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKey)) + return testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{ + Sequencer: keepertest.Alice, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("test2"), + Timestamp: time.Now().Add(1).UTC(), + }, }, }, }, - }, - isFirstStateUpdate: true, + isFirstStateUpdate: true, + } }, + expectErr: true, }, { name: "both states are not compatible - slash the sequencer who signed", - input: testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{ - Sequencer: keepertest.Alice, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), - }, - { - Height: 2, - StateRoot: []byte("this is not compatible"), - Timestamp: time.Now().Add(1).UTC(), - }, - { - Height: 3, - StateRoot: []byte("test3"), - Timestamp: time.Now().Add(2).UTC(), - }, - }, - }, - }, - isFirstStateUpdate: true, - }, - }, - { - name: "timestamp is missing and its first state update", - input: testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{ - Sequencer: keepertest.Alice, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), - }, - { - Height: 2, - StateRoot: []byte("test2"), - Timestamp: time.Now().Add(1).UTC(), - }, - { - Height: 3, - StateRoot: []byte("test3"), - Timestamp: time.Now().Add(2).UTC(), + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + blockSignerTmPubKey, err := k.GetTmPubkeyAsBytes(ctx, keepertest.Alice) + require.NoError(t, err) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKey)) + return testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{ + Sequencer: keepertest.Alice, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("this is not compatible"), + Timestamp: time.Now().Add(1).UTC(), + }, + { + Height: 3, + StateRoot: []byte("test3"), + Timestamp: time.Now().Add(2).UTC(), + }, }, }, }, - }, - isFirstStateUpdate: true, + isFirstStateUpdate: true, + } }, + expectErr: false, }, { name: "state is compatible", - input: testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{ - Sequencer: keepertest.Alice, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), - }, - { - Height: 2, - StateRoot: []byte("test2"), - Timestamp: time.Now().Add(1).UTC(), - }, - { - Height: 3, - StateRoot: []byte("test3"), - Timestamp: time.Now().Add(2).UTC(), + prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { + k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + blockSignerTmPubKey, err := k.GetTmPubkeyAsBytes(ctx, keepertest.Alice) + require.NoError(t, err) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKey)) + return testInput{ + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{ + Sequencer: keepertest.Alice, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("test2"), + Timestamp: time.Now().UTC(), + }, + { + Height: 3, + StateRoot: []byte("test3"), + Timestamp: time.Now().Add(1).UTC(), + }, }, }, }, - }, + } }, + expectErr: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := keeper.RollappHooks().AfterUpdateState(ctx, tc.input.rollappId, tc.input.stateInfo, tc.input.isFirstStateUpdate, tc.input.previousStateHasTimestamp) - require.NoError(t, err) + keeper, ctx := keepertest.LightClientKeeper(t) + input := tc.prepare(ctx, *keeper) + err := keeper.RollappHooks().AfterUpdateState(ctx, input.rollappId, input.stateInfo, input.isFirstStateUpdate, input.previousStateHasTimestamp) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } }) } } diff --git a/x/lightclient/post/ibc_msg_create_client_test.go b/x/lightclient/post/ibc_msg_create_client_test.go index 2bce4683c..acac4def5 100644 --- a/x/lightclient/post/ibc_msg_create_client_test.go +++ b/x/lightclient/post/ibc_msg_create_client_test.go @@ -5,8 +5,11 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" @@ -55,7 +58,10 @@ func TestHandleMsgCreateClient(t *testing.T) { success: true, } }, - assert: func(ctx sdk.Context, k keeper.Keeper) {}, + assert: func(ctx sdk.Context, k keeper.Keeper) { + _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-client-registration-in-progress") + require.False(t, found) + }, }, { name: "Canonical client registration in progress - tx was failure", @@ -112,7 +118,13 @@ func TestHandleMsgCreateClient(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) - ibcclientKeeper := NewMockIBCClientKeeper() + testClientStates := map[string]exported.ClientState{ + "non-tm-client-id": &ibcsolomachine.ClientState{}, + "canon-client-id": &ibctm.ClientState{ + ChainId: "rollapp-has-canon-client", + }, + } + ibcclientKeeper := NewMockIBCClientKeeper(testClientStates) ibcMsgDecorator := post.NewIBCMessagesDecorator(*keeper, ibcclientKeeper) input := tc.prepare(ctx, *keeper) diff --git a/x/lightclient/post/ibc_msgs_test.go b/x/lightclient/post/ibc_msgs_test.go index c20b2330f..d00dd8e13 100644 --- a/x/lightclient/post/ibc_msgs_test.go +++ b/x/lightclient/post/ibc_msgs_test.go @@ -3,15 +3,16 @@ package post_test import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" - - ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ) -type MockIBCCLientKeeper struct{} +type MockIBCCLientKeeper struct { + clientStates map[string]exported.ClientState +} -func NewMockIBCClientKeeper() *MockIBCCLientKeeper { - return &MockIBCCLientKeeper{} +func NewMockIBCClientKeeper(cs map[string]exported.ClientState) *MockIBCCLientKeeper { + return &MockIBCCLientKeeper{ + clientStates: cs, + } } func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { @@ -19,17 +20,8 @@ func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID } func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { - switch clientID { - case "non-tm-client-id": - clientState := ibcsolomachine.ClientState{} - return &clientState, true - case "canon-client-id": - clientState := ibctm.ClientState{ - ChainId: "rollapp-has-canon-client", - } - return &clientState, true - } - return nil, false + cs, ok := m.clientStates[clientID] + return cs, ok } func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { diff --git a/x/lightclient/types/errors.go b/x/lightclient/types/errors.go index b0710a0b2..37135e44a 100644 --- a/x/lightclient/types/errors.go +++ b/x/lightclient/types/errors.go @@ -5,5 +5,6 @@ import ( ) var ( - ErrTimestampNotFound = sdkerrors.Register(ModuleName, 2, "block descriptors do not contain block timestamp") + ErrTimestampNotFound = sdkerrors.Register(ModuleName, 2, "block descriptors do not contain block timestamp") + ErrNextBlockDescriptorMissing = sdkerrors.Register(ModuleName, 3, "next block descriptor is missing") ) diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 81edfbc3e..a4a47ebb7 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -14,16 +14,19 @@ import ( ) func CheckCompatibility(ibcState IBCState, raState RollappState) error { - // Check if block descriptor state root matches IBC header app hash + // Check if block descriptor state root matches IBC block header app hash if !bytes.Equal(ibcState.Root, raState.BlockDescriptor.StateRoot) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") } // in case of msgcreateclient, validator info is not available. it is only sent in msgupdateclient as header info - // Check if the validator set hash matches the sequencer + // Check if the validator pubkey matches the sequencer pubkey if len(ibcState.Validator) > 0 && !bytes.Equal(ibcState.Validator, raState.BlockSequencer) { - return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator set hash does not match the sequencer") + return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator does not match the sequencer") } - // Check if the nextValidatorHash matches the sequencer for h+1 + if len(raState.NextBlockSequencer) == 0 { + return ErrNextBlockDescriptorMissing + } + // Check if the nextValidatorHash matches for the sequencer for h+1 block descriptor nextValHashFromStateInfo, err := getValHashForSequencer(raState.NextBlockSequencer) if err != nil { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, err.Error()) @@ -42,8 +45,9 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { return nil } +// getValHashForSequencer creates a dummy tendermint validatorset to +// calculate the nextValHash for the sequencer and returns it func getValHashForSequencer(sequencerTmPubKeyBz []byte) ([]byte, error) { - // Creating a dummy tendermint validatorset to calculate the nextValHash var tmpk tmprotocrypto.PublicKey err := tmpk.Unmarshal(sequencerTmPubKeyBz) if err != nil { @@ -62,16 +66,25 @@ func getValHashForSequencer(sequencerTmPubKeyBz []byte) ([]byte, error) { } type IBCState struct { - Root []byte - Height uint64 - Validator []byte + // Root is the app root shared by the IBC consensus state + Root []byte + // Height is the block height of the IBC consensus state the root is at + Height uint64 + // Validator is the tendermint pubkey of signer of the block header + Validator []byte + // NextValidatorsHash is the hash of the next validator set for the next block NextValidatorsHash []byte - Timestamp time.Time + // Timestamp is the block timestamp of the header + Timestamp time.Time } type RollappState struct { - BlockSequencer []byte - BlockDescriptor rollapptypes.BlockDescriptor - NextBlockSequencer []byte + // BlockSequencer is the tendermint pubkey of the sequencer who submitted the block descriptor for the required height + BlockSequencer []byte + // BlockDescriptor is the block descriptor for the required height + BlockDescriptor rollapptypes.BlockDescriptor + // NextBlockSequencer is the tendermint pubkey of the sequencer who submitted the block descriptor for the next height (h+1) + NextBlockSequencer []byte + // NextBlockDescriptor is the block descriptor for the next height (h+1) NextBlockDescriptor rollapptypes.BlockDescriptor } diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 7dd9c5cfa..40664d80a 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -24,12 +24,21 @@ func TestCheckCompatibility(t *testing.T) { name: "roots are not equal", input: input{ ibcState: types.IBCState{ - Root: []byte("root"), + Root: []byte("root"), + Timestamp: timestamp, + NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, + Validator: []byte("sequencer"), }, raState: types.RollappState{ BlockSequencer: []byte("sequencer"), BlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("not root"), + StateRoot: []byte("not same root"), + Timestamp: timestamp, + }, + NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, + NextBlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root2"), + Timestamp: timestamp, }, }, }, @@ -39,9 +48,10 @@ func TestCheckCompatibility(t *testing.T) { name: "validator who signed the block header is not the sequencer who submitted the block", input: input{ ibcState: types.IBCState{ - Root: []byte("root"), - Timestamp: timestamp, - Validator: []byte("validator"), + Root: []byte("root"), + Timestamp: timestamp, + NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, + Validator: []byte("validator"), }, raState: types.RollappState{ BlockSequencer: []byte("sequencer"), @@ -49,9 +59,14 @@ func TestCheckCompatibility(t *testing.T) { StateRoot: []byte("root"), Timestamp: timestamp, }, + NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, + NextBlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root2"), + Timestamp: timestamp, + }, }, }, - err: "validator set hash does not match the sequencer", + err: "validator does not match the sequencer", }, { name: "nextValidatorHash does not match the sequencer who submitted the next block descriptor", From 93c1235b76f3e5de5d22b230375dd1108131585d Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:35:56 +0530 Subject: [PATCH 30/87] addressing updates from main --- go.mod | 2 +- testutil/keeper/lightclient.go | 4 ++-- x/dymns/keeper/hooks.go | 4 ++++ x/lightclient/keeper/hook_listener.go | 2 +- x/lightclient/types/expected_keepers.go | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 70d3ab513..da45c87ad 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/cosmos/gogoproto v1.4.10 github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.1.3 github.com/cosmos/ibc-go/v7 v7.5.1 + github.com/cosmos/ics23/go v0.10.0 github.com/decred/dcrd/dcrec/edwards v1.0.0 github.com/dustin/go-humanize v1.0.1 github.com/dymensionxyz/gerr-cosmos v1.0.0 @@ -84,7 +85,6 @@ require ( github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.20.1 // indirect - github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect github.com/cosmos/rosetta-sdk-go v0.10.0 // indirect github.com/creachadair/taskgroup v0.4.2 // indirect diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 6c320ea21..34167fb5c 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -25,7 +25,7 @@ import ( ) const ( - Alice = "cosmos1c4k24jzduc365kywrsvf5ujz4ya6mwymy8vq4q" + Alice = "dym1wg8p6j0pxpnsvhkwfu54ql62cnrumf0v634mft" ) func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { @@ -103,7 +103,7 @@ func NewMockSequencerKeeper() *MockSequencerKeeper { return &MockSequencerKeeper{} } -func (m *MockSequencerKeeper) SlashAndJailFraud(ctx sdk.Context, seqAddr string) error { +func (m *MockSequencerKeeper) JailSequencerOnFraud(ctx sdk.Context, seqAddr string) error { return nil } diff --git a/x/dymns/keeper/hooks.go b/x/dymns/keeper/hooks.go index c94ee63e1..450174d08 100644 --- a/x/dymns/keeper/hooks.go +++ b/x/dymns/keeper/hooks.go @@ -286,6 +286,10 @@ func (h rollappHooks) BeforeUpdateState(_ sdk.Context, _ string, _ string, _ boo return nil } +func (h rollappHooks) AfterUpdateState(_ sdk.Context, _ string, _ *rollapptypes.StateInfo, _ bool, _ bool) error { + return nil +} + func (h rollappHooks) AfterStateFinalized(_ sdk.Context, _ string, _ *rollapptypes.StateInfo) error { return nil } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index baa846a0e..a126ff4ec 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -92,7 +92,7 @@ func (hook rollappHook) AfterUpdateState( if err != nil { return err } - err = hook.k.sequencerKeeper.SlashAndJailFraud(ctx, sequencerAddr) + err = hook.k.sequencerKeeper.JailSequencerOnFraud(ctx, sequencerAddr) if err != nil { return err } diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index f036f128d..6d86c3986 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -10,7 +10,7 @@ import ( ) type SequencerKeeperExpected interface { - SlashAndJailFraud(ctx sdk.Context, seqAddr string) error + JailSequencerOnFraud(ctx sdk.Context, seqAddr string) error } type RollappKeeperExpected interface { From 52659df402d3d19d8fbddadaee017c9c066af7fe Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:44:30 +0530 Subject: [PATCH 31/87] =?UTF-8?q?linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/post/post.go | 2 +- app/upgrades/v4/upgrade.go | 2 +- x/lightclient/keeper/hook_listener.go | 5 +++-- x/lightclient/post/ibc_msg_create_client.go | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/post/post.go b/app/post/post.go index 3e307d7a2..e4b8819ed 100644 --- a/app/post/post.go +++ b/app/post/post.go @@ -21,7 +21,7 @@ func NewPostHandler(options HandlerOptions) (sdk.PostHandler, error) { return nil, err } postDecorators := []sdk.PostDecorator{ - lightclientpost.NewIBCMessagesDecorator(*options.LightClientKeeper, *&options.IBCKeeper.ClientKeeper), + lightclientpost.NewIBCMessagesDecorator(*options.LightClientKeeper, options.IBCKeeper.ClientKeeper), } return sdk.ChainPostDecorators(postDecorators...), nil diff --git a/app/upgrades/v4/upgrade.go b/app/upgrades/v4/upgrade.go index 700c559db..13c85bad2 100644 --- a/app/upgrades/v4/upgrade.go +++ b/app/upgrades/v4/upgrade.go @@ -150,7 +150,7 @@ func migrateRollappLightClients(ctx sdk.Context, rollappkeeper *rollappkeeper.Ke // get the client ID the channel belongs to _, connection, err := ibcChannelKeeper.GetChannelConnection(ctx, ibctransfertypes.PortID, rollapp.ChannelId) if err != nil { - // if could not find a connection, skip the cannonical client assignment + // if could not find a connection, skip the canonical client assignment return } clientID := connection.GetClientID() diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index a126ff4ec..50f1e989e 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -1,6 +1,7 @@ package keeper import ( + "errors" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -76,12 +77,12 @@ func (hook rollappHook) AfterUpdateState( err = types.CheckCompatibility(ibcState, rollappState) if err != nil { // Only require timestamp on BD if first ever update, or the previous update had BD - if err == types.ErrTimestampNotFound && !isFirstStateUpdate && !previousStateHasTimestamp { + if errors.Is(err, types.ErrTimestampNotFound) && !isFirstStateUpdate && !previousStateHasTimestamp { continue } // The BD for (h+1) is missing, cannot verify if the nextvalhash matches - if err == types.ErrNextBlockDescriptorMissing { + if errors.Is(err, types.ErrNextBlockDescriptorMissing) { return err } diff --git a/x/lightclient/post/ibc_msg_create_client.go b/x/lightclient/post/ibc_msg_create_client.go index 631458dc0..b573bc94f 100644 --- a/x/lightclient/post/ibc_msg_create_client.go +++ b/x/lightclient/post/ibc_msg_create_client.go @@ -31,6 +31,6 @@ func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibccli } } - // Always clear the registration after the tx as the transient store is shared amoung all txs in the block + // Always clear the registration after the tx as the transient store is shared among all txs in the block i.lightClientKeeper.ClearCanonicalLightClientRegistration(ctx, rollappID) } From 346b146072f27e62f9287d76aca64c31d78317df Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:59:51 +0530 Subject: [PATCH 32/87] =?UTF-8?q?more=20linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ante/ibc_msg_channel_open_ack.go | 1 - .../ante/ibc_msg_create_client_test.go | 9 +++---- .../ante/ibc_msg_submit_misbehaviour_test.go | 1 - x/lightclient/keeper/keeper.go | 1 - .../post/ibc_msg_create_client_test.go | 9 +++---- x/lightclient/types/params.go | 26 +++++++++---------- 6 files changed, 18 insertions(+), 29 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack.go b/x/lightclient/ante/ibc_msg_channel_open_ack.go index 17b7c100a..dbf74a21b 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack.go @@ -26,7 +26,6 @@ func (i IBCMessagesDecorator) HandleMsgChannelOpenAck(ctx sdk.Context, msg *ibcc if rollapp.ChannelId != "" { // canonical channel already exists, return error return errorsmod.Wrap(ibcchanneltypes.ErrChannelExists, "cannot create a new channel when a canonical channel already exists for the rollapp") - } // Set this channel as the canonical channel for the rollapp rollapp.ChannelId = msg.ChannelId diff --git a/x/lightclient/ante/ibc_msg_create_client_test.go b/x/lightclient/ante/ibc_msg_create_client_test.go index 5cc88d781..867d08a84 100644 --- a/x/lightclient/ante/ibc_msg_create_client_test.go +++ b/x/lightclient/ante/ibc_msg_create_client_test.go @@ -21,11 +21,9 @@ import ( rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) -var ( - testClientState = ibctm.NewClientState("chain-id", - ibctm.DefaultTrustLevel, time.Hour*24*7*2, time.Hour*24*7*2, time.Second*10, - ibcclienttypes.MustParseHeight("1-1"), commitmenttypes.GetSDKSpecs(), []string{}, - ) +var testClientState = ibctm.NewClientState("chain-id", + ibctm.DefaultTrustLevel, time.Hour*24*7*2, time.Hour*24*7*2, time.Second*10, + ibcclienttypes.MustParseHeight("1-1"), commitmenttypes.GetSDKSpecs(), []string{}, ) func TestHandleMsgCreateClient(t *testing.T) { @@ -377,5 +375,4 @@ func TestHandleMsgCreateClient(t *testing.T) { tc.assert(ctx, *keeper) }) } - } diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go index 66cb97296..5ea75fc23 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour_test.go @@ -66,5 +66,4 @@ func TestHandleMsgSubmitMisbehaviour(t *testing.T) { } }) } - } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 432ab496f..dfa280a90 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -28,7 +28,6 @@ func NewKeeper( sequencerKeeper types.SequencerKeeperExpected, accountKeeper types.AccountKeeperExpected, ) *Keeper { - k := &Keeper{ cdc: cdc, storeKey: storeKey, diff --git a/x/lightclient/post/ibc_msg_create_client_test.go b/x/lightclient/post/ibc_msg_create_client_test.go index acac4def5..eb7dcc3ab 100644 --- a/x/lightclient/post/ibc_msg_create_client_test.go +++ b/x/lightclient/post/ibc_msg_create_client_test.go @@ -17,11 +17,9 @@ import ( "github.com/stretchr/testify/require" ) -var ( - testClientState = ibctm.NewClientState("chain-id", - ibctm.DefaultTrustLevel, time.Hour*24*7*2, time.Hour*24*7*2, time.Second*10, - ibcclienttypes.MustParseHeight("1-1"), commitmenttypes.GetSDKSpecs(), []string{}, - ) +var testClientState = ibctm.NewClientState("chain-id", + ibctm.DefaultTrustLevel, time.Hour*24*7*2, time.Hour*24*7*2, time.Second*10, + ibcclienttypes.MustParseHeight("1-1"), commitmenttypes.GetSDKSpecs(), []string{}, ) type testInput struct { @@ -132,5 +130,4 @@ func TestHandleMsgCreateClient(t *testing.T) { tc.assert(ctx, *keeper) }) } - } diff --git a/x/lightclient/types/params.go b/x/lightclient/types/params.go index 287ed9e2d..cdee758ff 100644 --- a/x/lightclient/types/params.go +++ b/x/lightclient/types/params.go @@ -29,20 +29,18 @@ type CanonicalClientParams struct { AllowUpdateAfterMisbehaviour bool } -var ( - ExpectedCanonicalClientParams = CanonicalClientParams{ - TrustLevel: ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}), - TrustingPeriod: time.Hour * 24 * 7 * 2, - UnbondingPeriod: time.Hour * 24 * 7 * 3, - MaxClockDrift: time.Minute * 10, - ProofSpecs: []*ics23.ProofSpec{ // the proofspecs for a SDK chain - ics23.IavlSpec, - ics23.TendermintSpec, - }, - AllowUpdateAfterExpiry: false, - AllowUpdateAfterMisbehaviour: false, - } -) +var ExpectedCanonicalClientParams = CanonicalClientParams{ + TrustLevel: ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}), + TrustingPeriod: time.Hour * 24 * 7 * 2, + UnbondingPeriod: time.Hour * 24 * 7 * 3, + MaxClockDrift: time.Minute * 10, + ProofSpecs: []*ics23.ProofSpec{ // the proofspecs for a SDK chain + ics23.IavlSpec, + ics23.TendermintSpec, + }, + AllowUpdateAfterExpiry: false, + AllowUpdateAfterMisbehaviour: false, +} func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { if clientState.TrustLevel != ExpectedCanonicalClientParams.TrustLevel { From 3f7d4cc72ecfe8d0fd57a1b9d0bcd9e53bf56625 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 22 Aug 2024 12:25:31 +0530 Subject: [PATCH 33/87] =?UTF-8?q?more=20linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- testutil/keeper/lightclient.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 34167fb5c..6cba56151 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -96,8 +96,7 @@ func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) ( return nil, false } -type MockSequencerKeeper struct { -} +type MockSequencerKeeper struct{} func NewMockSequencerKeeper() *MockSequencerKeeper { return &MockSequencerKeeper{} From afb5347ff416eff1f198dad7e339c5c5954689f3 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:24:26 +0530 Subject: [PATCH 34/87] fixing potential out of bounds errors --- x/rollapp/keeper/msg_server_update_state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/rollapp/keeper/msg_server_update_state.go b/x/rollapp/keeper/msg_server_update_state.go index ac7dbae31..9c90fc9d0 100644 --- a/x/rollapp/keeper/msg_server_update_state.go +++ b/x/rollapp/keeper/msg_server_update_state.go @@ -44,7 +44,7 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) } // check if previous state last block desc has timestamp - lastBD := stateInfo.GetBDs().BD[stateInfo.NumBlocks-1] + lastBD := stateInfo.BDs.BD[len(stateInfo.BDs.BD)-1] previousStateHasTimestamp = !lastBD.Timestamp.IsZero() // check to see if received height is the one we expected From 662be5c8bd0033e8e1852113d885a4211c60d117 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 23 Aug 2024 09:24:18 +0530 Subject: [PATCH 35/87] setting canonical client if conditions match in afterstateupdatehook --- x/lightclient/keeper/client.go | 73 +++++++++++++++++++++++++ x/lightclient/keeper/hook_listener.go | 4 ++ x/lightclient/types/expected_keepers.go | 2 + 3 files changed, 79 insertions(+) create mode 100644 x/lightclient/keeper/client.go diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go new file mode 100644 index 000000000..47db9cca2 --- /dev/null +++ b/x/lightclient/keeper/client.go @@ -0,0 +1,73 @@ +package keeper + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + 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/dymension/v3/x/lightclient/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" +) + +func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) string { + clients := k.ibcClientKeeper.GetAllGenesisClients(ctx) + for _, client := range clients { + clientState, err := ibcclienttypes.UnpackClientState(client.ClientState) + if err != nil { + continue + } + if clientState.ClientType() == exported.Tendermint { + // Cast client state to tendermint client state - we need this to get the chain id and state height + tmClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + continue + } + // Check if the client is for the given rollapp + if tmClientState.ChainId == rollappId { + // Check if the client params match the expected params + if types.IsCanonicalClientParamsValid(tmClientState) { + sequencerPk, err := k.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) + if err != nil { + continue + } + + for i, bd := range stateInfo.GetBDs().BD { + height := ibcclienttypes.NewHeight(1, bd.GetHeight()) + consensusState, found := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, height) + if !found { + continue + } + // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash + tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) + if !ok { + continue + } + ibcState := types.IBCState{ + Root: tmConsensusState.GetRoot().GetHash(), + Height: height.GetRevisionHeight(), + NextValidatorsHash: tmConsensusState.NextValidatorsHash, + Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), + } + rollappState := types.RollappState{ + BlockSequencer: sequencerPk, + BlockDescriptor: bd, + } + // Check if BD for next block exists in same stateinfo + if i+1 < len(stateInfo.GetBDs().BD) { + rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[i+1] + rollappState.NextBlockSequencer = sequencerPk + } + err := types.CheckCompatibility(ibcState, rollappState) + if err != nil { + continue + } + return client.GetClientId() + } + } + } + } + } + return "" +} diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 50f1e989e..906177fea 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -34,6 +34,10 @@ func (hook rollappHook) AfterUpdateState( ) error { canonicalClient, found := hook.k.GetCanonicalClient(ctx, rollappId) if !found { + canonicalClient = hook.k.GetProspectiveCanonicalClient(ctx, rollappId, stateInfo) + if canonicalClient != "" { + hook.k.SetCanonicalClient(ctx, rollappId, canonicalClient) + } return nil } bds := stateInfo.GetBDs() diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 6d86c3986..f9beb32bf 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -3,6 +3,7 @@ package types import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" @@ -24,6 +25,7 @@ type IBCClientKeeperExpected interface { GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) GenerateClientIdentifier(ctx sdk.Context, clientType string) string GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) + GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates } type IBCChannelKeeperExpected interface { From 92f6ebe4b9f2f63ab710031aa055c6cde0a0b713 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 23 Aug 2024 10:12:00 +0530 Subject: [PATCH 36/87] adding tests for setting canonical client automatically --- testutil/keeper/lightclient.go | 41 ++++++++++++++++++---- x/lightclient/ante/ibc_msgs_test.go | 5 +++ x/lightclient/keeper/client.go | 7 ++-- x/lightclient/keeper/hook_listener_test.go | 33 +++++++++++++++++ x/lightclient/post/ibc_msgs_test.go | 5 +++ x/lightclient/types/state.go | 6 ++-- 6 files changed, 84 insertions(+), 13 deletions(-) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 6cba56151..db34a939c 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -12,6 +12,7 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" @@ -20,7 +21,9 @@ import ( cometbftdb "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/libs/log" + "github.com/cometbft/cometbft/libs/math" cometbftproto "github.com/cometbft/cometbft/proto/tendermint/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/stretchr/testify/require" ) @@ -42,22 +45,40 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { registry := codectypes.NewInterfaceRegistry() cdc := codec.NewProtoCodec(registry) - + sequencerPubKey := ed25519.GenPrivKey().PubKey() + tmPk, err := cryptocodec.ToTmProtoPublicKey(sequencerPubKey) + require.NoError(t, err) + tmPkBytes, err := tmPk.Marshal() + require.NoError(t, err) + nextValHash, err := types.GetValHashForSequencer(tmPkBytes) + require.NoError(t, err) + testPubKeys := map[string]cryptotypes.PubKey{ + Alice: sequencerPubKey, + } testConsensusStates := map[string]map[uint64]exported.ConsensusState{ "canon-client-id": { 2: &ibctm.ConsensusState{ Timestamp: time.Now().UTC(), Root: commitmenttypes.NewMerkleRoot([]byte("test2")), - NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, + NextValidatorsHash: nextValHash, }, }, } - sequencerPubKey := ed25519.GenPrivKey().PubKey() - testPubKeys := map[string]cryptotypes.PubKey{ - Alice: sequencerPubKey, + cs := ibctm.NewClientState("rollapp-wants-canon-client", + ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}), + time.Hour*24*7*2, time.Hour*24*7*3, time.Minute*10, + ibcclienttypes.MustParseHeight("1-2"), commitmenttypes.GetSDKSpecs(), []string{}, + ) + packedCS, err := ibcclienttypes.PackClientState(cs) + require.NoError(t, err) + testGenesisClients := ibcclienttypes.IdentifiedClientStates{ + { + ClientId: "canon-client-id", + ClientState: packedCS, + }, } - mockIBCKeeper := NewMockIBCClientKeeper(testConsensusStates) + mockIBCKeeper := NewMockIBCClientKeeper(testConsensusStates, testGenesisClients) mockSequencerKeeper := NewMockSequencerKeeper() mockAccountKeeper := NewMockAccountKeeper(testPubKeys) k := keeper.NewKeeper( @@ -75,11 +96,13 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { type MockIBCCLientKeeper struct { clientConsensusState map[string]map[uint64]exported.ConsensusState + genesisClients ibcclienttypes.IdentifiedClientStates } -func NewMockIBCClientKeeper(clientCS map[string]map[uint64]exported.ConsensusState) *MockIBCCLientKeeper { +func NewMockIBCClientKeeper(clientCS map[string]map[uint64]exported.ConsensusState, genesisClients ibcclienttypes.IdentifiedClientStates) *MockIBCCLientKeeper { return &MockIBCCLientKeeper{ clientConsensusState: clientCS, + genesisClients: genesisClients, } } @@ -96,6 +119,10 @@ func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) ( return nil, false } +func (m *MockIBCCLientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates { + return m.genesisClients +} + type MockSequencerKeeper struct{} func NewMockSequencerKeeper() *MockSequencerKeeper { diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index f10c52eca..8d58421f1 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -4,6 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" @@ -74,6 +75,10 @@ func (m *MockIBCClientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientTy return "new-canon-client-1" } +func (m *MockIBCClientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates { + return nil +} + type MockIBCChannelKeeper struct { channelConnections map[string]ibcconnectiontypes.ConnectionEnd } diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go index 47db9cca2..82ba6ed27 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/client.go @@ -11,7 +11,7 @@ import ( rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) -func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) string { +func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) (clientID string) { clients := k.ibcClientKeeper.GetAllGenesisClients(ctx) for _, client := range clients { clientState, err := ibcclienttypes.UnpackClientState(client.ClientState) @@ -63,11 +63,12 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, if err != nil { continue } - return client.GetClientId() + clientID = client.GetClientId() + return } } } } } - return "" + return } diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index 8d62789c3..46a85638a 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -185,3 +185,36 @@ func TestAfterUpdateState(t *testing.T) { }) } } + +func TestAfterUpdateState_SetCanonicalClient(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + rollappId := "rollapp-wants-canon-client" + stateInfo := &rollapptypes.StateInfo{ + Sequencer: keepertest.Alice, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ + { + Height: 1, + StateRoot: []byte("test"), + Timestamp: time.Now().UTC(), + }, + { + Height: 2, + StateRoot: []byte("test2"), + Timestamp: time.Now().UTC(), + }, + { + Height: 3, + StateRoot: []byte("test3"), + Timestamp: time.Now().Add(1).UTC(), + }, + }, + }, + } + err := keeper.RollappHooks().AfterUpdateState(ctx, rollappId, stateInfo, false, false) + require.NoError(t, err) + + clientID, found := keeper.GetCanonicalClient(ctx, rollappId) + require.True(t, found) + require.Equal(t, "canon-client-id", clientID) +} diff --git a/x/lightclient/post/ibc_msgs_test.go b/x/lightclient/post/ibc_msgs_test.go index d00dd8e13..1593829a6 100644 --- a/x/lightclient/post/ibc_msgs_test.go +++ b/x/lightclient/post/ibc_msgs_test.go @@ -2,6 +2,7 @@ package post_test import ( sdk "github.com/cosmos/cosmos-sdk/types" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" ) @@ -27,3 +28,7 @@ func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) ( func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { return "" } + +func (m *MockIBCCLientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates { + return nil +} diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index a4a47ebb7..4ac740752 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -27,7 +27,7 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { return ErrNextBlockDescriptorMissing } // Check if the nextValidatorHash matches for the sequencer for h+1 block descriptor - nextValHashFromStateInfo, err := getValHashForSequencer(raState.NextBlockSequencer) + nextValHashFromStateInfo, err := GetValHashForSequencer(raState.NextBlockSequencer) if err != nil { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, err.Error()) } @@ -45,9 +45,9 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { return nil } -// getValHashForSequencer creates a dummy tendermint validatorset to +// GetValHashForSequencer creates a dummy tendermint validatorset to // calculate the nextValHash for the sequencer and returns it -func getValHashForSequencer(sequencerTmPubKeyBz []byte) ([]byte, error) { +func GetValHashForSequencer(sequencerTmPubKeyBz []byte) ([]byte, error) { var tmpk tmprotocrypto.PublicKey err := tmpk.Unmarshal(sequencerTmPubKeyBz) if err != nil { From 686ee8b342169f5d1389fdca8ba7295860a2e0e4 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 23 Aug 2024 11:38:23 +0530 Subject: [PATCH 37/87] fixing test --- testutil/keeper/lightclient.go | 2 +- .../ante/ibc_msg_create_client_test.go | 12 +++++----- .../ante/ibc_msg_update_client_test.go | 10 ++++---- x/lightclient/keeper/hook_listener_test.go | 24 +++++++++---------- x/lightclient/types/state_test.go | 2 +- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index db34a939c..f33c3a47e 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -58,7 +58,7 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { testConsensusStates := map[string]map[uint64]exported.ConsensusState{ "canon-client-id": { 2: &ibctm.ConsensusState{ - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), Root: commitmenttypes.NewMerkleRoot([]byte("test2")), NextValidatorsHash: nextValHash, }, diff --git a/x/lightclient/ante/ibc_msg_create_client_test.go b/x/lightclient/ante/ibc_msg_create_client_test.go index 867d08a84..e4c96609c 100644 --- a/x/lightclient/ante/ibc_msg_create_client_test.go +++ b/x/lightclient/ante/ibc_msg_create_client_test.go @@ -156,7 +156,7 @@ func TestHandleMsgCreateClient(t *testing.T) { { Height: 1, StateRoot: []byte{}, - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, }, }, @@ -177,7 +177,7 @@ func TestHandleMsgCreateClient(t *testing.T) { clientState, err := ibcclienttypes.PackClientState(testClientState) require.NoError(t, err) testConsensusState := ibctm.NewConsensusState( - time.Now().UTC(), + time.Unix(1724392989, 0), commitmenttypes.NewMerkleRoot([]byte{}), ctx.BlockHeader().ValidatorsHash, ) @@ -207,12 +207,12 @@ func TestHandleMsgCreateClient(t *testing.T) { { Height: 1, StateRoot: []byte{}, - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, { Height: 2, StateRoot: []byte{}, - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, }, }, @@ -229,7 +229,7 @@ func TestHandleMsgCreateClient(t *testing.T) { { name: "State compatible but client params not conforming to expected params - continue without setting as canonical client", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - blocktimestamp := time.Now().UTC() + blocktimestamp := time.Unix(1724392989, 0) testClientState.ChainId = "rollapp-wants-canon-client" clientState, err := ibcclienttypes.PackClientState(testClientState) require.NoError(t, err) @@ -293,7 +293,7 @@ func TestHandleMsgCreateClient(t *testing.T) { { name: "State compatible + expected client params - Candidate canonical client set", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - blocktimestamp := time.Now().UTC() + blocktimestamp := time.Unix(1724392989, 0) testClientState.ChainId = "rollapp-wants-canon-client" testClientState.TrustLevel = ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}) testClientState.TrustingPeriod = time.Hour * 24 * 7 * 2 diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index 1f212246e..42b4db5d0 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -115,7 +115,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { { Height: 3, StateRoot: []byte{}, - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, }, }, @@ -143,7 +143,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { Header: &cmtproto.Header{ AppHash: []byte("appHash"), ProposerAddress: []byte("sequencerAddr"), - Time: time.Now().UTC(), + Time: time.Unix(1724392989, 0), NextValidatorsHash: []byte("nextValHash"), }, Commit: &cmtproto.Commit{}, @@ -181,12 +181,12 @@ func TestHandleMsgUpdateClient(t *testing.T) { { Height: 1, StateRoot: []byte{}, - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, { Height: 2, StateRoot: []byte{}, - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, }, }, @@ -205,7 +205,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { sequencer := keepertest.Alice proposerAddr, err := k.GetTmPubkeyAsBytes(ctx, sequencer) require.NoError(t, err) - blocktimestamp := time.Now().UTC() + blocktimestamp := time.Unix(1724392989, 0) k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") var ( valSet *cmtproto.ValidatorSet diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index 46a85638a..de1acc0f9 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -59,7 +59,7 @@ func TestAfterUpdateState(t *testing.T) { { Height: 1, StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, }, }, @@ -85,12 +85,12 @@ func TestAfterUpdateState(t *testing.T) { { Height: 1, StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, { Height: 2, StateRoot: []byte("test2"), - Timestamp: time.Now().Add(1).UTC(), + Timestamp: time.Unix(1724392989, 0).Add(1), }, }, }, @@ -116,17 +116,17 @@ func TestAfterUpdateState(t *testing.T) { { Height: 1, StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, { Height: 2, StateRoot: []byte("this is not compatible"), - Timestamp: time.Now().Add(1).UTC(), + Timestamp: time.Unix(1724392989, 0).Add(1), }, { Height: 3, StateRoot: []byte("test3"), - Timestamp: time.Now().Add(2).UTC(), + Timestamp: time.Unix(1724392989, 0).Add(2), }, }, }, @@ -152,17 +152,17 @@ func TestAfterUpdateState(t *testing.T) { { Height: 1, StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, { Height: 2, StateRoot: []byte("test2"), - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, { Height: 3, StateRoot: []byte("test3"), - Timestamp: time.Now().Add(1).UTC(), + Timestamp: time.Unix(1724392989, 0).Add(1), }, }, }, @@ -196,17 +196,17 @@ func TestAfterUpdateState_SetCanonicalClient(t *testing.T) { { Height: 1, StateRoot: []byte("test"), - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, { Height: 2, StateRoot: []byte("test2"), - Timestamp: time.Now().UTC(), + Timestamp: time.Unix(1724392989, 0), }, { Height: 3, StateRoot: []byte("test3"), - Timestamp: time.Now().Add(1).UTC(), + Timestamp: time.Unix(1724392989, 0).UTC(), }, }, }, diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 40664d80a..8499f1297 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -14,7 +14,7 @@ func TestCheckCompatibility(t *testing.T) { ibcState types.IBCState raState types.RollappState } - timestamp := time.Now().UTC() + timestamp := time.Unix(1724392989, 0) testCases := []struct { name string input input From 198fd424ea51459475b2414fc5d64ce28dd108c0 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:41:13 +0530 Subject: [PATCH 38/87] removing the ante & post for msg create client to set canonical client as this happens automatically now --- app/app.go | 9 +- app/keepers/keys.go | 2 +- app/post/post.go | 38 -- testutil/keeper/lightclient.go | 2 - x/lightclient/ante/ibc_msg_create_client.go | 106 ----- .../ante/ibc_msg_create_client_test.go | 378 ------------------ x/lightclient/ante/ibc_msgs.go | 2 - x/lightclient/keeper/keeper.go | 19 - x/lightclient/post/ibc_msg_create_client.go | 36 -- .../post/ibc_msg_create_client_test.go | 133 ------ x/lightclient/post/ibc_msgs.go | 35 -- x/lightclient/post/ibc_msgs_test.go | 34 -- x/lightclient/types/keys.go | 12 - 13 files changed, 5 insertions(+), 801 deletions(-) delete mode 100644 app/post/post.go delete mode 100644 x/lightclient/ante/ibc_msg_create_client.go delete mode 100644 x/lightclient/ante/ibc_msg_create_client_test.go delete mode 100644 x/lightclient/post/ibc_msg_create_client.go delete mode 100644 x/lightclient/post/ibc_msg_create_client_test.go delete mode 100644 x/lightclient/post/ibc_msgs.go delete mode 100644 x/lightclient/post/ibc_msgs_test.go diff --git a/app/app.go b/app/app.go index 90beda3d5..ac6638793 100644 --- a/app/app.go +++ b/app/app.go @@ -45,6 +45,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/auth/posthandler" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/cosmos/cosmos-sdk/x/crisis" @@ -57,7 +58,6 @@ import ( "github.com/dymensionxyz/dymension/v3/app/ante" appparams "github.com/dymensionxyz/dymension/v3/app/params" - "github.com/dymensionxyz/dymension/v3/app/post" packetforwardmiddleware "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward" packetforwardkeeper "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7/packetforward/keeper" @@ -222,10 +222,9 @@ func New( if err != nil { panic(err) } - postHandler, err := post.NewPostHandler(post.HandlerOptions{ - IBCKeeper: app.IBCKeeper, - LightClientKeeper: &app.LightClientKeeper, - }) + postHandler, err := posthandler.NewPostHandler( + posthandler.HandlerOptions{}, + ) if err != nil { panic(fmt.Errorf("failed to create PostHandler: %w", err)) } diff --git a/app/keepers/keys.go b/app/keepers/keys.go index 3e6976015..c2fa25402 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -47,7 +47,7 @@ func (a *AppKeepers) GenerateKeys() { a.keys = KVStoreKeys // Define transient store keys - a.tkeys = sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey, feemarkettypes.TransientKey, lightcliendmoduletypes.TransientKey) + a.tkeys = sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey, feemarkettypes.TransientKey) // MemKeys are for information that is stored only in RAM. a.memKeys = sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) diff --git a/app/post/post.go b/app/post/post.go deleted file mode 100644 index e4b8819ed..000000000 --- a/app/post/post.go +++ /dev/null @@ -1,38 +0,0 @@ -package post - -import ( - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - errortypes "github.com/cosmos/cosmos-sdk/types/errors" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" - lightclientkeeper "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" - - lightclientpost "github.com/dymensionxyz/dymension/v3/x/lightclient/post" -) - -// HandlerOptions are the options required for constructing a default SDK PostHandler. -type HandlerOptions struct { - IBCKeeper *ibckeeper.Keeper - LightClientKeeper *lightclientkeeper.Keeper -} - -func NewPostHandler(options HandlerOptions) (sdk.PostHandler, error) { - if err := options.validate(); err != nil { - return nil, err - } - postDecorators := []sdk.PostDecorator{ - lightclientpost.NewIBCMessagesDecorator(*options.LightClientKeeper, options.IBCKeeper.ClientKeeper), - } - - return sdk.ChainPostDecorators(postDecorators...), nil -} - -func (options HandlerOptions) validate() error { - if options.IBCKeeper == nil { - return errorsmod.Wrap(errortypes.ErrLogic, "ibc keeper is required for PostHandler") - } - if options.LightClientKeeper == nil { - return errorsmod.Wrap(errortypes.ErrLogic, "light client keeper is required for PostHandler") - } - return nil -} diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index f33c3a47e..cc5dbf82e 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -34,13 +34,11 @@ const ( func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { storeKey := sdk.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.StoreKey + "_mem") - transientStoreKey := storetypes.NewTransientStoreKey(types.TransientKey) db := cometbftdb.NewMemDB() stateStore := store.NewCommitMultiStore(db) stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) - stateStore.MountStoreWithDB(transientStoreKey, storetypes.StoreTypeTransient, nil) require.NoError(t, stateStore.LoadLatestVersion()) registry := codectypes.NewInterfaceRegistry() diff --git a/x/lightclient/ante/ibc_msg_create_client.go b/x/lightclient/ante/ibc_msg_create_client.go deleted file mode 100644 index a7b163252..000000000 --- a/x/lightclient/ante/ibc_msg_create_client.go +++ /dev/null @@ -1,106 +0,0 @@ -package ante - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - 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/dymension/v3/x/lightclient/types" -) - -func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibcclienttypes.MsgCreateClient) { - clientState, err := ibcclienttypes.UnpackClientState(msg.ClientState) - if err != nil { - return - } - // Only tendermint client types can be set as canonical light client - if clientState.ClientType() == exported.Tendermint { - // Cast client state to tendermint client state - we need this to get the chain id and state height - tmClientState, ok := clientState.(*ibctm.ClientState) - if !ok { - return - } - // Check if rollapp exists for given chain id - rollappID := tmClientState.ChainId - _, found := i.rollappKeeper.GetRollapp(ctx, rollappID) - if !found { - return - } - // Check if canonical client already exists for rollapp. Only one canonical client is allowed per rollapp - _, found = i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) - if found { - return - } - // Check if there are existing block descriptors for the given height of client state - height := tmClientState.GetLatestHeight() - stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) - if err != nil { - return - } - blockDescriptor := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] - - consensusState, err := ibcclienttypes.UnpackConsensusState(msg.ConsensusState) - if err != nil { - return - } - // Cast consensus state to tendermint consensus state - we need this to get the state root and timestamp and nextValHash - tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) - if !ok { - return - } - // Convert timestamp from nanoseconds to time.Time - timestamp := time.Unix(0, int64(tmConsensusState.GetTimestamp())) - - ibcState := types.IBCState{ - Root: tmConsensusState.GetRoot().GetHash(), - Height: tmClientState.GetLatestHeight().GetRevisionHeight(), - Validator: []byte{}, // This info is available in the tendermint consensus state as no header has been shared yet - NextValidatorsHash: tmConsensusState.NextValidatorsHash, - Timestamp: timestamp, - } - sequencerPk, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) - if err != nil { - return - } - rollappState := types.RollappState{ - BlockSequencer: sequencerPk, - BlockDescriptor: blockDescriptor, - } - // Check if bd for next block exists and is part of same state info - nextHeight := height.GetRevisionHeight() + 1 - if stateInfo.StartHeight+stateInfo.NumBlocks >= nextHeight { - rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[nextHeight-stateInfo.StartHeight] - rollappState.NextBlockSequencer = sequencerPk - } else { - // nextBD doesnt exist in same stateInfo. So lookup in the next StateInfo - currentStateInfoIndex := stateInfo.GetIndex().Index - nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, currentStateInfoIndex+1) - if !found { - return // There is no BD for h+1, so we can't verify the next block valhash. So we cant mark this client as canonical - } else { - nextSequencerPk, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, nextStateInfo.Sequencer) - if err != nil { - return - } - rollappState.NextBlockSequencer = nextSequencerPk - rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] - } - } - // Check if the consensus state is compatible with the block descriptor state - err = types.CheckCompatibility(ibcState, rollappState) - if err != nil { - return // In case of incompatibility, the client will be created but not set as canonical - } - - // Ensure the light client params conform to expected values - if !types.IsCanonicalClientParamsValid(tmClientState) { - return // In case of invalid params, the client will be created but not set as canonical - } - // Generate client id and begin canonical light client registration by storing it in transient store. - // Will be confirmed after the client is created in post handler. - nextClientID := i.ibcClientKeeper.GenerateClientIdentifier(ctx, exported.Tendermint) - i.lightClientKeeper.BeginCanonicalLightClientRegistration(ctx, rollappID, nextClientID) - } -} diff --git a/x/lightclient/ante/ibc_msg_create_client_test.go b/x/lightclient/ante/ibc_msg_create_client_test.go deleted file mode 100644 index e4c96609c..000000000 --- a/x/lightclient/ante/ibc_msg_create_client_test.go +++ /dev/null @@ -1,378 +0,0 @@ -package ante_test - -import ( - "testing" - "time" - - "github.com/cometbft/cometbft/libs/math" - - abci "github.com/cometbft/cometbft/abci/types" - cmttypes "github.com/cometbft/cometbft/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" - - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" - ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" - "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" - "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" - rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" -) - -var testClientState = ibctm.NewClientState("chain-id", - ibctm.DefaultTrustLevel, time.Hour*24*7*2, time.Hour*24*7*2, time.Second*10, - ibcclienttypes.MustParseHeight("1-1"), commitmenttypes.GetSDKSpecs(), []string{}, -) - -func TestHandleMsgCreateClient(t *testing.T) { - type testInput struct { - msg *ibcclienttypes.MsgCreateClient - rollapps map[string]rollapptypes.Rollapp - stateInfos map[string]map[uint64]rollapptypes.StateInfo - } - - testCases := []struct { - name string - prepare func(ctx sdk.Context, k keeper.Keeper) testInput - assert func(ctx sdk.Context, k keeper.Keeper) - }{ - { - name: "Could not unpack light client state to tendermint state", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{}, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) {}, - }, - { - name: "Client is not a tendermint client", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - solomachineClientState := ibcsolomachine.NewClientState(0, nil) - cs, err := ibcclienttypes.PackClientState(solomachineClientState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: cs, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) {}, - }, - { - name: "Rollapp with given chainID does not exist", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - testClientState.ChainId = "not-a-rollapp" - cs, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: cs, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - _, found := k.GetCanonicalLightClientRegistration(ctx, "not-a-rollapp") - require.False(t, found) - }, - }, - { - name: "Canonical client for the rollapp already exists", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - testClientState.ChainId = "rollapp-has-canon-client" - k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - cs, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: cs, - }, - rollapps: map[string]rollapptypes.Rollapp{ - "rollapp-has-canon-client": { - RollappId: "rollapp-has-canon-client", - ChannelId: "channel-on-canon-client", - }, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - clientID, found := k.GetCanonicalClient(ctx, "rollapp-has-canon-client") - require.True(t, found) - require.Equal(t, "canon-client-id", clientID) - _, registrationPending := k.GetCanonicalLightClientRegistration(ctx, "rollapp-has-canon-client") - require.False(t, registrationPending) - }, - }, - { - name: "Could not find block desc for given height - continue without setting as canonical client", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - testClientState.ChainId = "rollapp-wants-canon-client" - cs, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: cs, - }, - rollapps: map[string]rollapptypes.Rollapp{ - "rollapp-wants-canon-client": { - RollappId: "rollapp-wants-canon-client", - }, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") - require.False(t, found) - }, - }, - { - name: "Could not find block descriptor for the next height (h+1) - continue without setting as canonical client", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - testClientState.ChainId = "rollapp-wants-canon-client" - cs, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: cs, - }, - rollapps: map[string]rollapptypes.Rollapp{ - "rollapp-wants-canon-client": { - RollappId: "rollapp-wants-canon-client", - }, - }, - stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ - "rollapp-wants-canon-client": { - 1: { - StartHeight: 1, - NumBlocks: 1, - StateInfoIndex: rollapptypes.StateInfoIndex{ - Index: 1, - }, - Sequencer: keepertest.Alice, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte{}, - Timestamp: time.Unix(1724392989, 0), - }, - }, - }, - }, - }, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") - require.False(t, found) - }, - }, - { - name: "State incompatible - continue without setting as canonical client", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - testClientState.ChainId = "rollapp-wants-canon-client" - clientState, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - testConsensusState := ibctm.NewConsensusState( - time.Unix(1724392989, 0), - commitmenttypes.NewMerkleRoot([]byte{}), - ctx.BlockHeader().ValidatorsHash, - ) - consState, err := ibcclienttypes.PackConsensusState(testConsensusState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: clientState, - ConsensusState: consState, - }, - rollapps: map[string]rollapptypes.Rollapp{ - "rollapp-wants-canon-client": { - RollappId: "rollapp-wants-canon-client", - }, - }, - stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ - "rollapp-wants-canon-client": { - 1: { - StartHeight: 1, - NumBlocks: 2, - StateInfoIndex: rollapptypes.StateInfoIndex{ - Index: 1, - }, - Sequencer: keepertest.Alice, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte{}, - Timestamp: time.Unix(1724392989, 0), - }, - { - Height: 2, - StateRoot: []byte{}, - Timestamp: time.Unix(1724392989, 0), - }, - }, - }, - }, - }, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") - require.False(t, found) - }, - }, - { - name: "State compatible but client params not conforming to expected params - continue without setting as canonical client", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - blocktimestamp := time.Unix(1724392989, 0) - testClientState.ChainId = "rollapp-wants-canon-client" - clientState, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - var nextVals cmttypes.ValidatorSet - tmPk, err := k.GetTmPubkey(ctx, keepertest.Alice) - require.NoError(t, err) - updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: tmPk}}) - require.NoError(t, err) - err = nextVals.UpdateWithChangeSet(updates) - require.NoError(t, err) - testConsensusState := ibctm.NewConsensusState( - blocktimestamp, - commitmenttypes.NewMerkleRoot([]byte("appHash")), - nextVals.Hash(), - ) - consState, err := ibcclienttypes.PackConsensusState(testConsensusState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: clientState, - ConsensusState: consState, - }, - rollapps: map[string]rollapptypes.Rollapp{ - "rollapp-wants-canon-client": { - RollappId: "rollapp-wants-canon-client", - }, - }, - stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ - "rollapp-wants-canon-client": { - 1: { - StartHeight: 1, - NumBlocks: 2, - StateInfoIndex: rollapptypes.StateInfoIndex{ - Index: 1, - }, - Sequencer: keepertest.Alice, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("appHash"), - Timestamp: blocktimestamp, - }, - { - Height: 2, - StateRoot: []byte("appHash2"), - Timestamp: blocktimestamp.Add(1), - }, - }, - }, - }, - }, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") - require.False(t, found) - }, - }, - { - name: "State compatible + expected client params - Candidate canonical client set", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - blocktimestamp := time.Unix(1724392989, 0) - testClientState.ChainId = "rollapp-wants-canon-client" - testClientState.TrustLevel = ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}) - testClientState.TrustingPeriod = time.Hour * 24 * 7 * 2 - testClientState.UnbondingPeriod = time.Hour * 24 * 7 * 3 - testClientState.MaxClockDrift = time.Minute * 10 - testClientState.AllowUpdateAfterExpiry = false - testClientState.AllowUpdateAfterMisbehaviour = false - clientState, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - var nextVals cmttypes.ValidatorSet - tmPk, err := k.GetTmPubkey(ctx, keepertest.Alice) - require.NoError(t, err) - updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: tmPk}}) - require.NoError(t, err) - err = nextVals.UpdateWithChangeSet(updates) - require.NoError(t, err) - testConsensusState := ibctm.NewConsensusState( - blocktimestamp, - commitmenttypes.NewMerkleRoot([]byte("appHash")), - nextVals.Hash(), - ) - consState, err := ibcclienttypes.PackConsensusState(testConsensusState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: clientState, - ConsensusState: consState, - }, - rollapps: map[string]rollapptypes.Rollapp{ - "rollapp-wants-canon-client": { - RollappId: "rollapp-wants-canon-client", - }, - }, - stateInfos: map[string]map[uint64]rollapptypes.StateInfo{ - "rollapp-wants-canon-client": { - 1: { - StartHeight: 1, - NumBlocks: 2, - StateInfoIndex: rollapptypes.StateInfoIndex{ - Index: 1, - }, - Sequencer: keepertest.Alice, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("appHash"), - Timestamp: blocktimestamp, - }, - { - Height: 2, - StateRoot: []byte("appHash2"), - Timestamp: blocktimestamp.Add(1), - }, - }, - }, - }, - }, - }, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - clientID, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-wants-canon-client") - require.True(t, found) - require.Equal(t, "new-canon-client-1", clientID) - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - keeper, ctx := keepertest.LightClientKeeper(t) - ibcclientKeeper := NewMockIBCClientKeeper(nil) - ibcchannelKeeper := NewMockIBCChannelKeeper(nil) - input := tc.prepare(ctx, *keeper) - rollappKeeper := NewMockRollappKeeper(input.rollapps, input.stateInfos) - ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) - - ibcMsgDecorator.HandleMsgCreateClient(ctx, input.msg) - tc.assert(ctx, *keeper) - }) - } -} diff --git a/x/lightclient/ante/ibc_msgs.go b/x/lightclient/ante/ibc_msgs.go index 5145f101c..edc8a4e2f 100644 --- a/x/lightclient/ante/ibc_msgs.go +++ b/x/lightclient/ante/ibc_msgs.go @@ -30,8 +30,6 @@ func (i IBCMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo msgs := tx.GetMsgs() for _, m := range msgs { switch msg := m.(type) { - case *ibcclienttypes.MsgCreateClient: - i.HandleMsgCreateClient(ctx, msg) case *ibcclienttypes.MsgSubmitMisbehaviour: if err := i.HandleMsgSubmitMisbehaviour(ctx, msg); err != nil { return ctx, err diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index dfa280a90..36dd65cd0 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -96,25 +96,6 @@ func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID s store.Set(types.CanonicalClientKey(clientID), []byte(rollappId)) } -func (k Keeper) BeginCanonicalLightClientRegistration(ctx sdk.Context, rollappId string, clientID string) { - store := ctx.KVStore(k.storeKey) - store.Set(types.CanonicalLightClientRegistrationKey(rollappId), []byte(clientID)) -} - -func (k Keeper) GetCanonicalLightClientRegistration(ctx sdk.Context, rollappId string) (string, bool) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.CanonicalLightClientRegistrationKey(rollappId)) - if bz == nil { - return "", false - } - return string(bz), true -} - -func (k Keeper) ClearCanonicalLightClientRegistration(ctx sdk.Context, rollappId string) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.CanonicalLightClientRegistrationKey(rollappId)) -} - func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencer string) { store := ctx.KVStore(k.storeKey) store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), []byte(sequencer)) diff --git a/x/lightclient/post/ibc_msg_create_client.go b/x/lightclient/post/ibc_msg_create_client.go deleted file mode 100644 index b573bc94f..000000000 --- a/x/lightclient/post/ibc_msg_create_client.go +++ /dev/null @@ -1,36 +0,0 @@ -package post - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" -) - -func (i IBCMessagesDecorator) HandleMsgCreateClient(ctx sdk.Context, msg *ibcclienttypes.MsgCreateClient, success bool) { - clientState, err := ibcclienttypes.UnpackClientState(msg.ClientState) - if err != nil { - return - } - // Parse client state to tendermint client state to get the chain id - tmClientState, ok := clientState.(*ibctm.ClientState) - if !ok { - return - } - rollappID := tmClientState.ChainId - // If tx failed, no need to proceed with canonical client registration - if success { - // Check if a client registration is in progress - nextClientID, registrationFound := i.lightClientKeeper.GetCanonicalLightClientRegistration(ctx, rollappID) - if registrationFound { - // Check if the client was successfully created with given clientID - _, clientFound := i.ibcClientKeeper.GetClientState(ctx, nextClientID) - if clientFound { - // Set the client as the canonical client for the rollapp - i.lightClientKeeper.SetCanonicalClient(ctx, rollappID, nextClientID) - } - - } - } - // Always clear the registration after the tx as the transient store is shared among all txs in the block - i.lightClientKeeper.ClearCanonicalLightClientRegistration(ctx, rollappID) -} diff --git a/x/lightclient/post/ibc_msg_create_client_test.go b/x/lightclient/post/ibc_msg_create_client_test.go deleted file mode 100644 index eb7dcc3ab..000000000 --- a/x/lightclient/post/ibc_msg_create_client_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package post_test - -import ( - "testing" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ibc-go/v7/modules/core/exported" - - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" - ibcsolomachine "github.com/cosmos/ibc-go/v7/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" - keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" - "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" - "github.com/dymensionxyz/dymension/v3/x/lightclient/post" - "github.com/stretchr/testify/require" -) - -var testClientState = ibctm.NewClientState("chain-id", - ibctm.DefaultTrustLevel, time.Hour*24*7*2, time.Hour*24*7*2, time.Second*10, - ibcclienttypes.MustParseHeight("1-1"), commitmenttypes.GetSDKSpecs(), []string{}, -) - -type testInput struct { - msg *ibcclienttypes.MsgCreateClient - success bool -} - -func TestHandleMsgCreateClient(t *testing.T) { - testCases := []struct { - name string - prepare func(ctx sdk.Context, k keeper.Keeper) testInput - assert func(ctx sdk.Context, k keeper.Keeper) - }{ - { - name: "Could not unpack light client state to tendermint state", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{}, - success: true, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) {}, - }, - { - name: "Canonical client registration not in progress", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - testClientState.ChainId = "not-a-rollapp" - cs, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: cs, - }, - success: true, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-client-registration-in-progress") - require.False(t, found) - }, - }, - { - name: "Canonical client registration in progress - tx was failure", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - testClientState.ChainId = "rollapp-client-registration-in-progress" - cs, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - expectedClientID := "new-client-1" - k.BeginCanonicalLightClientRegistration( - ctx, - "rollapp-client-registration-in-progress", - expectedClientID, - ) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: cs, - }, - success: false, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - _, found := k.GetCanonicalLightClientRegistration(ctx, "rollapp-client-registration-in-progress") - require.False(t, found) - }, - }, - { - name: "Canonical client registration in progress - client was found with name", - prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { - testClientState.ChainId = "rollapp-client-registration-in-progress" - cs, err := ibcclienttypes.PackClientState(testClientState) - require.NoError(t, err) - expectedClientID := "canon-client-id" - k.BeginCanonicalLightClientRegistration( - ctx, - "rollapp-client-registration-in-progress", - expectedClientID, - ) - return testInput{ - msg: &ibcclienttypes.MsgCreateClient{ - ClientState: cs, - }, - success: true, - } - }, - assert: func(ctx sdk.Context, k keeper.Keeper) { - clientID, found := k.GetCanonicalClient(ctx, "rollapp-client-registration-in-progress") - require.True(t, found) - require.Equal(t, "canon-client-id", clientID) - _, found = k.GetCanonicalLightClientRegistration(ctx, "rollapp-client-registration-in-progress") - require.False(t, found) - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - keeper, ctx := keepertest.LightClientKeeper(t) - testClientStates := map[string]exported.ClientState{ - "non-tm-client-id": &ibcsolomachine.ClientState{}, - "canon-client-id": &ibctm.ClientState{ - ChainId: "rollapp-has-canon-client", - }, - } - ibcclientKeeper := NewMockIBCClientKeeper(testClientStates) - ibcMsgDecorator := post.NewIBCMessagesDecorator(*keeper, ibcclientKeeper) - - input := tc.prepare(ctx, *keeper) - ibcMsgDecorator.HandleMsgCreateClient(ctx, input.msg, input.success) - tc.assert(ctx, *keeper) - }) - } -} diff --git a/x/lightclient/post/ibc_msgs.go b/x/lightclient/post/ibc_msgs.go deleted file mode 100644 index 2ab0f6886..000000000 --- a/x/lightclient/post/ibc_msgs.go +++ /dev/null @@ -1,35 +0,0 @@ -package post - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - "github.com/dymensionxyz/dymension/v3/x/lightclient/keeper" - "github.com/dymensionxyz/dymension/v3/x/lightclient/types" -) - -var _ sdk.PostDecorator = IBCMessagesDecorator{} - -type IBCMessagesDecorator struct { - ibcClientKeeper types.IBCClientKeeperExpected - lightClientKeeper keeper.Keeper -} - -func NewIBCMessagesDecorator(k keeper.Keeper, ibcK types.IBCClientKeeperExpected) IBCMessagesDecorator { - return IBCMessagesDecorator{ - ibcClientKeeper: ibcK, - lightClientKeeper: k, - } -} - -func (i IBCMessagesDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, success bool, next sdk.PostHandler) (newCtx sdk.Context, err error) { - msgs := tx.GetMsgs() - for _, m := range msgs { - switch msg := m.(type) { - case *ibcclienttypes.MsgCreateClient: - i.HandleMsgCreateClient(ctx, msg, success) - default: - continue - } - } - return next(ctx, tx, simulate, success) -} diff --git a/x/lightclient/post/ibc_msgs_test.go b/x/lightclient/post/ibc_msgs_test.go deleted file mode 100644 index 1593829a6..000000000 --- a/x/lightclient/post/ibc_msgs_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package post_test - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v7/modules/core/exported" -) - -type MockIBCCLientKeeper struct { - clientStates map[string]exported.ClientState -} - -func NewMockIBCClientKeeper(cs map[string]exported.ClientState) *MockIBCCLientKeeper { - return &MockIBCCLientKeeper{ - clientStates: cs, - } -} - -func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { - return nil, false -} - -func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { - cs, ok := m.clientStates[clientID] - return cs, ok -} - -func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { - return "" -} - -func (m *MockIBCCLientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates { - return nil -} diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go index 3ca68e029..d680d1e42 100644 --- a/x/lightclient/types/keys.go +++ b/x/lightclient/types/keys.go @@ -10,9 +10,6 @@ const ( // StoreKey defines the primary module store key StoreKey = ModuleName - - // TransientKey defines the module's transient store key - TransientKey = "t_lightclient" ) // KV Store @@ -22,19 +19,10 @@ var ( canonicalClientKey = []byte{0x04} ) -// Transient Store -var ( - lightClientRegistrationKey = []byte{0x02} -) - func RollappClientKey(rollappId string) []byte { return append(rollappClientKey, []byte(rollappId)...) } -func CanonicalLightClientRegistrationKey(rollappId string) []byte { - return append(lightClientRegistrationKey, []byte(rollappId)...) -} - func ConsensusStateSignerKeyByClientID(clientID string, height uint64) []byte { prefix := append([]byte(clientID), sdk.Uint64ToBigEndian(height)...) return append(consensusStateSignerKey, prefix...) From 009df2dc573f50d1d77301fe4b4e466cca8509f3 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:56:42 +0530 Subject: [PATCH 39/87] adding some code comments --- x/lightclient/ante/ibc_msg_update_client.go | 2 +- x/lightclient/ante/ibc_msg_update_client_test.go | 2 +- x/lightclient/keeper/client.go | 5 +++-- x/lightclient/keeper/hook_listener.go | 11 ++++------- x/lightclient/keeper/keeper.go | 3 +++ x/lightclient/module.go | 6 +++--- x/lightclient/types/keys.go | 1 - x/lightclient/types/params.go | 1 + x/lightclient/types/state.go | 2 +- 9 files changed, 17 insertions(+), 16 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index fdbb0b881..9cc50f6da 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -35,7 +35,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli return nil } // Check if there are existing block descriptors for the given height of client state - height := header.TrustedHeight + height := clientState.GetLatestHeight() stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) if err != nil { // No BDs found for given height. diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index 42b4db5d0..86465ae3c 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -132,7 +132,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { }, }, { - name: "State is incompatible", + name: "State is incompatible - do not accept", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") var ( diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go index 82ba6ed27..229311733 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/client.go @@ -11,6 +11,8 @@ import ( rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) +// GetProspectiveCanonicalClient returns the client id of the first IBC client which can be set as the canonical client for the given rollapp. +// Returns empty string if no such client is found. func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) (clientID string) { clients := k.ibcClientKeeper.GetAllGenesisClients(ctx) for _, client := range clients { @@ -26,13 +28,12 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, } // Check if the client is for the given rollapp if tmClientState.ChainId == rollappId { - // Check if the client params match the expected params + // Check if the client params match the expected params for a canonical client if types.IsCanonicalClientParamsValid(tmClientState) { sequencerPk, err := k.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) if err != nil { continue } - for i, bd := range stateInfo.GetBDs().BD { height := ibcclienttypes.NewHeight(1, bd.GetHeight()) consensusState, found := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, height) diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 906177fea..c26f3866a 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -25,6 +25,9 @@ func (k Keeper) RollappHooks() rollapptypes.RollappHooks { return rollappHook{k: k} } +// AfterUpdateState is called after a state update is made to a rollapp. +// This hook checks if the rollapp has a canonical IBC light client and if the Consensus state is compatible with the state update +// and punishes the sequencer if it is not func (hook rollappHook) AfterUpdateState( ctx sdk.Context, rollappId string, @@ -54,16 +57,12 @@ func (hook rollappHook) AfterUpdateState( if !ok { return nil } - - // Convert timestamp from nanoseconds to time.Time - timestamp := time.Unix(0, int64(tmConsensusState.GetTimestamp())) - ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), Height: bd.GetHeight(), Validator: []byte(tmHeaderSigner), NextValidatorsHash: tmConsensusState.NextValidatorsHash, - Timestamp: timestamp, + Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), } sequencerPk, err := hook.k.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) if err != nil { @@ -84,12 +83,10 @@ func (hook rollappHook) AfterUpdateState( if errors.Is(err, types.ErrTimestampNotFound) && !isFirstStateUpdate && !previousStateHasTimestamp { continue } - // The BD for (h+1) is missing, cannot verify if the nextvalhash matches if errors.Is(err, types.ErrNextBlockDescriptorMissing) { return err } - // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 36dd65cd0..d0eaf4728 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -38,6 +38,7 @@ func NewKeeper( return k } +// GetTmPubkeyAsBytes returns the tendermint public key as bytes for the given sequencer address in bech32 format func (k Keeper) GetTmPubkeyAsBytes(ctx sdk.Context, sequencerAddr string) ([]byte, error) { tmPk, err := k.GetTmPubkey(ctx, sequencerAddr) if err != nil { @@ -47,6 +48,7 @@ func (k Keeper) GetTmPubkeyAsBytes(ctx sdk.Context, sequencerAddr string) ([]byt return tmPkBytes, err } +// GetTmPubkey returns the tendermint public key for the given sequencer address in bech32 format func (k Keeper) GetTmPubkey(ctx sdk.Context, sequencerAddr string) (tmprotocrypto.PublicKey, error) { acc, err := sdk.AccAddressFromBech32(sequencerAddr) if err != nil { @@ -63,6 +65,7 @@ func (k Keeper) GetTmPubkey(ctx sdk.Context, sequencerAddr string) (tmprotocrypt return tmPk, nil } +// getAddress converts a tendermint public key to a bech32 address func (k Keeper) getAddress(tmPubkeyBz []byte) (string, error) { var tmpk tmprotocrypto.PublicKey err := tmpk.Unmarshal(tmPubkeyBz) diff --git a/x/lightclient/module.go b/x/lightclient/module.go index 4b4f59a94..3c3d9ecc9 100644 --- a/x/lightclient/module.go +++ b/x/lightclient/module.go @@ -60,7 +60,7 @@ func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncod return nil } -// RegisterRESTRoutes registers the capability module's REST service handlers. +// RegisterRESTRoutes registers the module's REST service handlers. func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { } @@ -68,12 +68,12 @@ func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Rout func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { } -// GetTxCmd returns the capability module's root tx command. +// GetTxCmd returns the module's root tx command. func (a AppModuleBasic) GetTxCmd() *cobra.Command { return nil } -// GetQueryCmd returns the capability module's root query command. +// GetQueryCmd returns the module's root query command. func (AppModuleBasic) GetQueryCmd() *cobra.Command { return nil } diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go index d680d1e42..0a034ae80 100644 --- a/x/lightclient/types/keys.go +++ b/x/lightclient/types/keys.go @@ -12,7 +12,6 @@ const ( StoreKey = ModuleName ) -// KV Store var ( rollappClientKey = []byte{0x01} consensusStateSignerKey = []byte{0x03} diff --git a/x/lightclient/types/params.go b/x/lightclient/types/params.go index cdee758ff..465fee9e9 100644 --- a/x/lightclient/types/params.go +++ b/x/lightclient/types/params.go @@ -42,6 +42,7 @@ var ExpectedCanonicalClientParams = CanonicalClientParams{ AllowUpdateAfterMisbehaviour: false, } +// IsCanonicalClientParamsValid checks if the given IBC tendermint client state has the expected canonical client parameters func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { if clientState.TrustLevel != ExpectedCanonicalClientParams.TrustLevel { return false diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 4ac740752..e605a63ab 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -13,12 +13,12 @@ import ( rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) +// CheckCompatibility checks if the IBC state and Rollapp state are compatible func CheckCompatibility(ibcState IBCState, raState RollappState) error { // Check if block descriptor state root matches IBC block header app hash if !bytes.Equal(ibcState.Root, raState.BlockDescriptor.StateRoot) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") } - // in case of msgcreateclient, validator info is not available. it is only sent in msgupdateclient as header info // Check if the validator pubkey matches the sequencer pubkey if len(ibcState.Validator) > 0 && !bytes.Equal(ibcState.Validator, raState.BlockSequencer) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator does not match the sequencer") From 8052844e2ffb881767ce13294196b52ab85d351e Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Sat, 24 Aug 2024 10:39:00 +0530 Subject: [PATCH 40/87] reverting the wrong code pushed --- x/lightclient/ante/ibc_msg_update_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 9cc50f6da..fdbb0b881 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -35,7 +35,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli return nil } // Check if there are existing block descriptors for the given height of client state - height := clientState.GetLatestHeight() + height := header.TrustedHeight stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) if err != nil { // No BDs found for given height. From 38a6ee8b208acd10d50ba71c850c9936236bff0d Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:20:11 +0530 Subject: [PATCH 41/87] using sequencer keeper for getting the pubkey instead of the account keeper --- app/keepers/keepers.go | 1 - testutil/keeper/lightclient.go | 52 ++++++++----------- x/lightclient/ante/ibc_msg_update_client.go | 8 +-- .../ante/ibc_msg_update_client_test.go | 18 ++----- x/lightclient/keeper/client.go | 2 +- x/lightclient/keeper/hook_listener.go | 6 +-- x/lightclient/keeper/hook_listener_test.go | 12 ++--- x/lightclient/keeper/keeper.go | 45 ++++++---------- x/lightclient/types/expected_keepers.go | 9 ++-- x/sequencer/types/sequencer.go | 21 ++++++++ 10 files changed, 79 insertions(+), 95 deletions(-) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index c644cfb19..8f50557f8 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -368,7 +368,6 @@ func (a *AppKeepers) InitKeepers( a.keys[lightclientmoduletypes.StoreKey], a.IBCKeeper.ClientKeeper, a.SequencerKeeper, - a.AccountKeeper, ) a.RollappKeeper.SetSequencerKeeper(a.SequencerKeeper) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index cc5dbf82e..9c4b96bfb 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -7,23 +7,21 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/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/dymension/v3/x/lightclient/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" + sequencertypes "github.com/dymensionxyz/dymension/v3/x/sequencer/types" cometbftdb "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/libs/log" "github.com/cometbft/cometbft/libs/math" cometbftproto "github.com/cometbft/cometbft/proto/tendermint/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/stretchr/testify/require" ) @@ -44,14 +42,17 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { registry := codectypes.NewInterfaceRegistry() cdc := codec.NewProtoCodec(registry) sequencerPubKey := ed25519.GenPrivKey().PubKey() - tmPk, err := cryptocodec.ToTmProtoPublicKey(sequencerPubKey) + tmPk, err := codectypes.NewAnyWithValue(sequencerPubKey) require.NoError(t, err) - tmPkBytes, err := tmPk.Marshal() - require.NoError(t, err) - nextValHash, err := types.GetValHashForSequencer(tmPkBytes) + + testSequencer := sequencertypes.Sequencer{ + Address: Alice, + DymintPubKey: tmPk, + } + nextValHash, err := testSequencer.GetDymintPubKeyHash() require.NoError(t, err) - testPubKeys := map[string]cryptotypes.PubKey{ - Alice: sequencerPubKey, + testSequencers := map[string]sequencertypes.Sequencer{ + Alice: testSequencer, } testConsensusStates := map[string]map[uint64]exported.ConsensusState{ "canon-client-id": { @@ -77,14 +78,12 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { } mockIBCKeeper := NewMockIBCClientKeeper(testConsensusStates, testGenesisClients) - mockSequencerKeeper := NewMockSequencerKeeper() - mockAccountKeeper := NewMockAccountKeeper(testPubKeys) + mockSequencerKeeper := NewMockSequencerKeeper(testSequencers) k := keeper.NewKeeper( cdc, storeKey, mockIBCKeeper, mockSequencerKeeper, - mockAccountKeeper, ) ctx := sdk.NewContext(stateStore, cometbftproto.Header{}, false, log.NewNopLogger()) @@ -121,30 +120,21 @@ func (m *MockIBCCLientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttyp return m.genesisClients } -type MockSequencerKeeper struct{} +type MockSequencerKeeper struct { + sequencers map[string]sequencertypes.Sequencer +} -func NewMockSequencerKeeper() *MockSequencerKeeper { - return &MockSequencerKeeper{} +func NewMockSequencerKeeper(sequencers map[string]sequencertypes.Sequencer) *MockSequencerKeeper { + return &MockSequencerKeeper{ + sequencers: sequencers, + } } func (m *MockSequencerKeeper) JailSequencerOnFraud(ctx sdk.Context, seqAddr string) error { return nil } -type MockAccountKeeper struct { - pubkeys map[string]cryptotypes.PubKey -} - -func NewMockAccountKeeper(pubkeys map[string]cryptotypes.PubKey) *MockAccountKeeper { - return &MockAccountKeeper{ - pubkeys: pubkeys, - } -} - -func (m *MockAccountKeeper) GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (cryptotypes.PubKey, error) { - pubkey, ok := m.pubkeys[addr.String()] - if !ok { - return nil, sdkerrors.ErrUnknownAddress - } - return pubkey, nil +func (m *MockSequencerKeeper) GetSequencer(ctx sdk.Context, seqAddr string) (sequencertypes.Sequencer, bool) { + seq, ok := m.sequencers[seqAddr] + return seq, ok } diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index fdbb0b881..70ff5ebe3 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -42,7 +42,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // Will accept the update optimistically // But also save the blockProposer address with the height for future verification blockProposer := header.Header.ProposerAddress - i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), string(blockProposer)) + i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) return nil } bd := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] @@ -54,7 +54,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli NextValidatorsHash: header.Header.NextValidatorsHash, Timestamp: header.Header.Time, } - sequencerPubKey, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) + sequencerPubKey, err := i.lightClientKeeper.GetSequencerPubKey(ctx, stateInfo.Sequencer) if err != nil { return err } @@ -70,7 +70,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // next BD does not exist in this state info, check the next state info nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) if found { - nextSequencerPk, err := i.lightClientKeeper.GetTmPubkeyAsBytes(ctx, nextStateInfo.Sequencer) + nextSequencerPk, err := i.lightClientKeeper.GetSeqeuncerHash(ctx, nextStateInfo.Sequencer) if err != nil { return err } @@ -81,7 +81,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // Will accept the update optimistically // But also save the blockProposer address with the height for future verification blockProposer := header.Header.ProposerAddress - i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), string(blockProposer)) + i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) return nil } } diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index 86465ae3c..fef18137f 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -4,11 +4,7 @@ import ( "testing" "time" - abci "github.com/cometbft/cometbft/abci/types" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -128,7 +124,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { require.NoError(t, err) signer, found := k.GetConsensusStateSigner(ctx, "canon-client-id", 1) require.True(t, found) - require.Equal(t, "sequencerAddr", signer) + require.Equal(t, []byte("sequencerAddr"), signer) }, }, { @@ -203,28 +199,22 @@ func TestHandleMsgUpdateClient(t *testing.T) { name: "Ensure state is compatible - happy path", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { sequencer := keepertest.Alice - proposerAddr, err := k.GetTmPubkeyAsBytes(ctx, sequencer) + proposerAddr, err := k.GetSequencerPubKey(ctx, sequencer) require.NoError(t, err) blocktimestamp := time.Unix(1724392989, 0) k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") var ( valSet *cmtproto.ValidatorSet trustedVals *cmtproto.ValidatorSet - nextVals cmttypes.ValidatorSet ) - var tmpk tmprotocrypto.PublicKey - err = tmpk.Unmarshal(proposerAddr) - require.NoError(t, err) - updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: tmpk}}) - require.NoError(t, err) - err = nextVals.UpdateWithChangeSet(updates) + nextValsHash, err := k.GetSeqeuncerHash(ctx, sequencer) require.NoError(t, err) signedHeader := &cmtproto.SignedHeader{ Header: &cmtproto.Header{ AppHash: []byte("appHash"), ProposerAddress: proposerAddr, Time: blocktimestamp, - NextValidatorsHash: nextVals.Hash(), + NextValidatorsHash: nextValsHash, }, Commit: &cmtproto.Commit{}, } diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go index 229311733..1086bdd3f 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/client.go @@ -30,7 +30,7 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, if tmClientState.ChainId == rollappId { // Check if the client params match the expected params for a canonical client if types.IsCanonicalClientParamsValid(tmClientState) { - sequencerPk, err := k.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) + sequencerPk, err := k.GetSequencerPubKey(ctx, stateInfo.Sequencer) if err != nil { continue } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index c26f3866a..77c945f7e 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -60,11 +60,11 @@ func (hook rollappHook) AfterUpdateState( ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), Height: bd.GetHeight(), - Validator: []byte(tmHeaderSigner), + Validator: tmHeaderSigner, NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), } - sequencerPk, err := hook.k.GetTmPubkeyAsBytes(ctx, stateInfo.Sequencer) + sequencerPk, err := hook.k.GetSeqeuncerHash(ctx, stateInfo.Sequencer) if err != nil { return err } @@ -90,7 +90,7 @@ func (hook rollappHook) AfterUpdateState( // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header - sequencerAddr, err := hook.k.getAddress([]byte(tmHeaderSigner)) + sequencerAddr, err := hook.k.getAddress(tmHeaderSigner) if err != nil { return err } diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index de1acc0f9..7be614029 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -73,9 +73,9 @@ func TestAfterUpdateState(t *testing.T) { name: "BD does not include next block in state info", prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - blockSignerTmPubKey, err := k.GetTmPubkeyAsBytes(ctx, keepertest.Alice) + blockSignerTmPubKey, err := k.GetSeqeuncerHash(ctx, keepertest.Alice) require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKey)) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKey) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ @@ -104,9 +104,9 @@ 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 { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - blockSignerTmPubKey, err := k.GetTmPubkeyAsBytes(ctx, keepertest.Alice) + blockSignerTmPubKey, err := k.GetSequencerPubKey(ctx, keepertest.Alice) require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKey)) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKey) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ @@ -140,9 +140,9 @@ func TestAfterUpdateState(t *testing.T) { name: "state is compatible", prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - blockSignerTmPubKey, err := k.GetTmPubkeyAsBytes(ctx, keepertest.Alice) + blockSignerTmPubKey, err := k.GetSequencerPubKey(ctx, keepertest.Alice) require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKey)) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKey) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index d0eaf4728..141ba1ea7 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -18,7 +18,6 @@ type Keeper struct { storeKey storetypes.StoreKey ibcClientKeeper types.IBCClientKeeperExpected sequencerKeeper types.SequencerKeeperExpected - accountKeepr types.AccountKeeperExpected } func NewKeeper( @@ -26,43 +25,31 @@ func NewKeeper( storeKey storetypes.StoreKey, ibcKeeper types.IBCClientKeeperExpected, sequencerKeeper types.SequencerKeeperExpected, - accountKeeper types.AccountKeeperExpected, ) *Keeper { k := &Keeper{ cdc: cdc, storeKey: storeKey, ibcClientKeeper: ibcKeeper, sequencerKeeper: sequencerKeeper, - accountKeepr: accountKeeper, } return k } -// GetTmPubkeyAsBytes returns the tendermint public key as bytes for the given sequencer address in bech32 format -func (k Keeper) GetTmPubkeyAsBytes(ctx sdk.Context, sequencerAddr string) ([]byte, error) { - tmPk, err := k.GetTmPubkey(ctx, sequencerAddr) - if err != nil { - return nil, err +// GetSeqeuncerHash returns the seqeuncer's tendermint public key hash +func (k Keeper) GetSeqeuncerHash(ctx sdk.Context, sequencerAddr string) ([]byte, error) { + seq, found := k.sequencerKeeper.GetSequencer(ctx, sequencerAddr) + if !found { + return nil, fmt.Errorf("sequencer not found") } - tmPkBytes, err := tmPk.Marshal() - return tmPkBytes, err + return seq.GetDymintPubKeyHash() } -// GetTmPubkey returns the tendermint public key for the given sequencer address in bech32 format -func (k Keeper) GetTmPubkey(ctx sdk.Context, sequencerAddr string) (tmprotocrypto.PublicKey, error) { - acc, err := sdk.AccAddressFromBech32(sequencerAddr) - if err != nil { - return tmprotocrypto.PublicKey{}, err - } - pk, err := k.accountKeepr.GetPubKey(ctx, acc) - if err != nil { - return tmprotocrypto.PublicKey{}, err +func (k Keeper) GetSequencerPubKey(ctx sdk.Context, sequencerAddr string) ([]byte, error) { + seq, found := k.sequencerKeeper.GetSequencer(ctx, sequencerAddr) + if !found { + return nil, fmt.Errorf("sequencer not found") } - tmPk, err := cryptocodec.ToTmProtoPublicKey(pk) - if err != nil { - return tmprotocrypto.PublicKey{}, err - } - return tmPk, nil + return seq.GetDymintPubKeyBytes() } // getAddress converts a tendermint public key to a bech32 address @@ -99,18 +86,18 @@ func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID s store.Set(types.CanonicalClientKey(clientID), []byte(rollappId)) } -func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencer string) { +func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencer []byte) { store := ctx.KVStore(k.storeKey) - store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), []byte(sequencer)) + store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), sequencer) } -func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64) (string, bool) { +func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64) ([]byte, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.ConsensusStateSignerKeyByClientID(clientID, height)) if bz == nil { - return "", false + return []byte{}, false } - return string(bz), true + return bz, true } func (k Keeper) GetRollappForClientID(ctx sdk.Context, clientID string) (string, bool) { diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index f9beb32bf..8851ab43f 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -1,17 +1,18 @@ package types import ( - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + sequencertypes "github.com/dymensionxyz/dymension/v3/x/sequencer/types" ) type SequencerKeeperExpected interface { - JailSequencerOnFraud(ctx sdk.Context, seqAddr string) error + JailSequencerOnFraud(ctx sdk.Context, sequencerAddress string) error + GetSequencer(ctx sdk.Context, sequencerAddress string) (val sequencertypes.Sequencer, found bool) } type RollappKeeperExpected interface { @@ -32,7 +33,3 @@ type IBCChannelKeeperExpected interface { GetChannel(ctx sdk.Context, portID, channelID string) (channel ibcchanneltypes.Channel, found bool) GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) } - -type AccountKeeperExpected interface { - GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (cryptotypes.PubKey, error) -} diff --git a/x/sequencer/types/sequencer.go b/x/sequencer/types/sequencer.go index e150c32d2..7fed5ea5d 100644 --- a/x/sequencer/types/sequencer.go +++ b/x/sequencer/types/sequencer.go @@ -61,3 +61,24 @@ func (seq Sequencer) GetDymintPubKeyHash() ([]byte, error) { tmValidatorSet := cometbfttypes.NewValidatorSet([]*cometbfttypes.Validator{tmValidator}) return tmValidatorSet.Hash(), nil } + +// GetDymintPubKeyBytes returns the bytes of the sequencer's dymint pubkey +func (seq Sequencer) GetDymintPubKeyBytes() ([]byte, error) { + interfaceRegistry := cdctypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(interfaceRegistry) + protoCodec := codec.NewProtoCodec(interfaceRegistry) + + var pubKey cryptotypes.PubKey + err := protoCodec.UnpackAny(seq.DymintPubKey, &pubKey) + if err != nil { + return nil, err + } + + // convert the pubkey to tmPubKey + tmPubKey, err := cryptocodec.ToTmProtoPublicKey(pubKey) + if err != nil { + return nil, err + } + + return tmPubKey.Marshal() +} From 88c068785ffa9fbadf5c2dbc3678614d9eb88221 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:04:22 +0530 Subject: [PATCH 42/87] addressing some PR review comments --- .../dymension/rollapp/block_descriptor.proto | 2 +- testutil/keeper/lightclient.go | 4 --- .../ante/ibc_msg_channel_open_ack.go | 27 ++++++++++--------- .../ante/ibc_msg_submit_misbehaviour.go | 7 +++-- x/lightclient/ante/ibc_msg_update_client.go | 2 +- x/lightclient/ante/ibc_msgs_test.go | 9 ------- x/lightclient/types/errors.go | 7 ++--- x/lightclient/types/expected_keepers.go | 3 --- x/lightclient/types/params.go | 25 ++++++----------- x/rollapp/types/block_descriptor.pb.go | 2 +- 10 files changed, 32 insertions(+), 56 deletions(-) diff --git a/proto/dymensionxyz/dymension/rollapp/block_descriptor.proto b/proto/dymensionxyz/dymension/rollapp/block_descriptor.proto index 1a558ea31..c7b26fb23 100644 --- a/proto/dymensionxyz/dymension/rollapp/block_descriptor.proto +++ b/proto/dymensionxyz/dymension/rollapp/block_descriptor.proto @@ -13,7 +13,7 @@ message BlockDescriptor { uint64 height = 1; // stateRoot is a 32 byte array of the hash of the block (state root of the block) bytes stateRoot = 2; - // timestamp is the time of the block finalization + // timestamp is the time from the block header google.protobuf.Timestamp timestamp = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; } diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 9c4b96bfb..3d2bf670b 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -108,10 +108,6 @@ func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID return cs, ok } -func (m *MockIBCCLientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { - return "" -} - func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { return nil, false } diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack.go b/x/lightclient/ante/ibc_msg_channel_open_ack.go index dbf74a21b..3e27c7687 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack.go @@ -17,19 +17,20 @@ func (i IBCMessagesDecorator) HandleMsgChannelOpenAck(ctx sdk.Context, msg *ibcc return err } rollappID, found := i.lightClientKeeper.GetRollappForClientID(ctx, connection.GetClientID()) - if found { - // Check if canon channel already exists for rollapp, if yes, return err - rollapp, found := i.rollappKeeper.GetRollapp(ctx, rollappID) - if !found { - return nil - } - if rollapp.ChannelId != "" { - // canonical channel already exists, return error - return errorsmod.Wrap(ibcchanneltypes.ErrChannelExists, "cannot create a new channel when a canonical channel already exists for the rollapp") - } - // Set this channel as the canonical channel for the rollapp - rollapp.ChannelId = msg.ChannelId - i.rollappKeeper.SetRollapp(ctx, rollapp) + if !found { + return nil + } + // Check if canon channel already exists for rollapp, if yes, return err + rollapp, found := i.rollappKeeper.GetRollapp(ctx, rollappID) + if !found { + return nil } + if rollapp.ChannelId != "" { + return errorsmod.Wrap(ibcchanneltypes.ErrChannelExists, "cannot create a new channel when a canonical channel already exists for the rollapp") + } + // Set this channel as the canonical channel for the rollapp + rollapp.ChannelId = msg.ChannelId + i.rollappKeeper.SetRollapp(ctx, rollapp) + return nil } diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go index 99e77bc96..0ecf1acbe 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go @@ -20,9 +20,8 @@ func (i IBCMessagesDecorator) HandleMsgSubmitMisbehaviour(ctx sdk.Context, msg * // Check if the client is the canonical client for a rollapp rollappID := tendmermintClientState.ChainId canonicalClient, found := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) - if !found || canonicalClient != msg.ClientId { - // The client is not a rollapp's canonical client. Continue with default behaviour. - return nil + if canonicalClient == msg.ClientId { + return errorsmod.Wrap(ibcclienttypes.ErrInvalidClient, "cannot submit misbehavour for a canonical client") } - return errorsmod.Wrap(ibcclienttypes.ErrInvalidClient, "cannot submit misbehavour for a canonical client") + return nil } diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 70ff5ebe3..232bd1be0 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -86,7 +86,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli } } // Ensure that the ibc header is compatible with the existing rollapp state - // If its not, we error and prevent the MsgUpdateClient from being processed + // If it's not, we error and prevent the MsgUpdateClient from being processed err = types.CheckCompatibility(ibcState, rollappState) if err != nil { return err diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index 8d58421f1..adf93b20c 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -6,7 +6,6 @@ import ( ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibcconnectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" - ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) @@ -71,10 +70,6 @@ func (m *MockIBCClientKeeper) GetClientState(ctx sdk.Context, clientID string) ( return val, found } -func (m *MockIBCClientKeeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { - return "new-canon-client-1" -} - func (m *MockIBCClientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates { return nil } @@ -89,10 +84,6 @@ func NewMockIBCChannelKeeper(connections map[string]ibcconnectiontypes.Connectio } } -func (m *MockIBCChannelKeeper) GetChannel(ctx sdk.Context, portID, channelID string) (channel ibcchanneltypes.Channel, found bool) { - return ibcchanneltypes.Channel{}, false -} - func (m *MockIBCChannelKeeper) GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) { if portID == "transfer" { return "", m.channelConnections[channelID], nil diff --git a/x/lightclient/types/errors.go b/x/lightclient/types/errors.go index 37135e44a..9e7d9a6dd 100644 --- a/x/lightclient/types/errors.go +++ b/x/lightclient/types/errors.go @@ -1,10 +1,11 @@ package types import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + errorsmod "cosmossdk.io/errors" + "github.com/dymensionxyz/gerr-cosmos/gerrc" ) var ( - ErrTimestampNotFound = sdkerrors.Register(ModuleName, 2, "block descriptors do not contain block timestamp") - ErrNextBlockDescriptorMissing = sdkerrors.Register(ModuleName, 3, "next block descriptor is missing") + ErrTimestampNotFound = errorsmod.Wrap(gerrc.ErrNotFound, "block descriptors do not contain block timestamp") + ErrNextBlockDescriptorMissing = errorsmod.Wrap(gerrc.ErrNotFound, "next block descriptor is missing") ) diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 8851ab43f..1cfaaeccf 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -5,7 +5,6 @@ import ( ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" - ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" sequencertypes "github.com/dymensionxyz/dymension/v3/x/sequencer/types" ) @@ -24,12 +23,10 @@ type RollappKeeperExpected interface { type IBCClientKeeperExpected interface { GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) - GenerateClientIdentifier(ctx sdk.Context, clientType string) string GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates } type IBCChannelKeeperExpected interface { - GetChannel(ctx sdk.Context, portID, channelID string) (channel ibcchanneltypes.Channel, found bool) GetChannelConnection(ctx sdk.Context, portID, channelID string) (string, exported.ConnectionI, error) } diff --git a/x/lightclient/types/params.go b/x/lightclient/types/params.go index 465fee9e9..aa5cfd7ba 100644 --- a/x/lightclient/types/params.go +++ b/x/lightclient/types/params.go @@ -8,38 +8,29 @@ import ( ics23 "github.com/cosmos/ics23/go" ) -type CanonicalClientParams struct { +var ExpectedCanonicalClientParams = ibctm.ClientState{ // Trust level is the fraction of the trusted validator set // that must sign over a new untrusted header before it is accepted. // For a rollapp should be 1/1. - TrustLevel ibctm.Fraction + TrustLevel: ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}), // TrustingPeriod is the duration of the period since the // LatestTimestamp during which the submitted headers are valid for update. - TrustingPeriod time.Duration + TrustingPeriod: time.Hour * 24 * 7 * 2, // Unbonding period is the duration of the sequencer unbonding period. - UnbondingPeriod time.Duration + UnbondingPeriod: time.Hour * 24 * 7 * 3, // MaxClockDrift defines how much new (untrusted) header's Time - // can drift into the future relative to our local clock. - MaxClockDrift time.Duration + // can drift into the future relative to our local clock. + MaxClockDrift: time.Minute * 10, // ProofSpecs defines the ICS-23 standard proof specifications used by // the light client. It is used configure a proof for either existence // or non-existence of a key value pair - ProofSpecs []*ics23.ProofSpec - AllowUpdateAfterExpiry bool - AllowUpdateAfterMisbehaviour bool -} - -var ExpectedCanonicalClientParams = CanonicalClientParams{ - TrustLevel: ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 1}), - TrustingPeriod: time.Hour * 24 * 7 * 2, - UnbondingPeriod: time.Hour * 24 * 7 * 3, - MaxClockDrift: time.Minute * 10, ProofSpecs: []*ics23.ProofSpec{ // the proofspecs for a SDK chain ics23.IavlSpec, ics23.TendermintSpec, }, AllowUpdateAfterExpiry: false, AllowUpdateAfterMisbehaviour: false, + UpgradePath: []string{}, } // IsCanonicalClientParamsValid checks if the given IBC tendermint client state has the expected canonical client parameters @@ -53,7 +44,7 @@ func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { if clientState.UnbondingPeriod > ExpectedCanonicalClientParams.UnbondingPeriod { return false } - if clientState.MaxClockDrift > ExpectedCanonicalClientParams.MaxClockDrift { + if clientState.MaxClockDrift < ExpectedCanonicalClientParams.MaxClockDrift { return false } if clientState.AllowUpdateAfterExpiry != ExpectedCanonicalClientParams.AllowUpdateAfterExpiry { diff --git a/x/rollapp/types/block_descriptor.pb.go b/x/rollapp/types/block_descriptor.pb.go index c0803dd20..746dc5b83 100644 --- a/x/rollapp/types/block_descriptor.pb.go +++ b/x/rollapp/types/block_descriptor.pb.go @@ -33,7 +33,7 @@ type BlockDescriptor struct { Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` // stateRoot is a 32 byte array of the hash of the block (state root of the block) StateRoot []byte `protobuf:"bytes,2,opt,name=stateRoot,proto3" json:"stateRoot,omitempty"` - // timestamp is the time of the block finalization + // timestamp is the time from the block header Timestamp time.Time `protobuf:"bytes,3,opt,name=timestamp,proto3,stdtime" json:"timestamp"` } From a03ba98aeba6dba104bbc2ddbb8d586af368b13a Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:27:16 +0530 Subject: [PATCH 43/87] typo fix --- x/lightclient/types/params.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/lightclient/types/params.go b/x/lightclient/types/params.go index aa5cfd7ba..f125f219b 100644 --- a/x/lightclient/types/params.go +++ b/x/lightclient/types/params.go @@ -44,7 +44,7 @@ func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { if clientState.UnbondingPeriod > ExpectedCanonicalClientParams.UnbondingPeriod { return false } - if clientState.MaxClockDrift < ExpectedCanonicalClientParams.MaxClockDrift { + if clientState.MaxClockDrift > ExpectedCanonicalClientParams.MaxClockDrift { return false } if clientState.AllowUpdateAfterExpiry != ExpectedCanonicalClientParams.AllowUpdateAfterExpiry { From 1e177f5ccd6285ba2f4845bcbeb2a01377eb7489 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:20:15 +0530 Subject: [PATCH 44/87] =?UTF-8?q?linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- x/lightclient/ante/ibc_msg_submit_misbehaviour.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go index 0ecf1acbe..e0a991ff8 100644 --- a/x/lightclient/ante/ibc_msg_submit_misbehaviour.go +++ b/x/lightclient/ante/ibc_msg_submit_misbehaviour.go @@ -19,7 +19,7 @@ func (i IBCMessagesDecorator) HandleMsgSubmitMisbehaviour(ctx sdk.Context, msg * } // Check if the client is the canonical client for a rollapp rollappID := tendmermintClientState.ChainId - canonicalClient, found := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) + canonicalClient, _ := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) if canonicalClient == msg.ClientId { return errorsmod.Wrap(ibcclienttypes.ErrInvalidClient, "cannot submit misbehavour for a canonical client") } From b12d33ea2104db92295bc72aa0f03e24813f821a Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:21:40 +0530 Subject: [PATCH 45/87] using binary search when looking up FindStateInfoByHeight --- ...rpc_query_get_state_info_by_height_test.go | 6 +- x/rollapp/keeper/grpc_query_state_info.go | 114 ++---------------- x/rollapp/types/state_info.go | 11 ++ 3 files changed, 28 insertions(+), 103 deletions(-) diff --git a/x/rollapp/keeper/grpc_query_get_state_info_by_height_test.go b/x/rollapp/keeper/grpc_query_get_state_info_by_height_test.go index 14059aac3..fa3dc0e27 100644 --- a/x/rollapp/keeper/grpc_query_get_state_info_by_height_test.go +++ b/x/rollapp/keeper/grpc_query_get_state_info_by_height_test.go @@ -88,9 +88,10 @@ func TestStateInfoByHeightMissingStateInfo(t *testing.T) { Height: 100, } _, err := k.StateInfo(wctx, request) + errIndex := 1 + (85-1)/2 // Using binary search, the middle index is lookedup first and is missing. require.EqualError(t, err, errorsmod.Wrapf(types.ErrNotFound, "StateInfo wasn't found for rollappId=%s, index=%d", - rollappId, 85).Error()) + rollappId, errIndex).Error()) } func TestStateInfoByHeightMissingStateInfo1(t *testing.T) { @@ -115,9 +116,10 @@ func TestStateInfoByHeightMissingStateInfo1(t *testing.T) { NumBlocks: 1, }) _, err := k.StateInfo(wctx, request) + errIndex := 1 + (60-1)/2 // Using binary search, the middle index is lookedup first and is missing. require.EqualError(t, err, errorsmod.Wrapf(types.ErrNotFound, "StateInfo wasn't found for rollappId=%s, index=%d", - rollappId, 1).Error()) + rollappId, errIndex).Error()) } func TestStateInfoByHeightErr(t *testing.T) { diff --git a/x/rollapp/keeper/grpc_query_state_info.go b/x/rollapp/keeper/grpc_query_state_info.go index 1cbf1f248..a6bdb00eb 100644 --- a/x/rollapp/keeper/grpc_query_state_info.go +++ b/x/rollapp/keeper/grpc_query_state_info.go @@ -6,6 +6,7 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -73,111 +74,22 @@ func (k Keeper) FindStateInfoByHeight(ctx sdk.Context, rollappId string, height rollappId) } // initial interval to search in - startInfoIndex := uint64(1) // see TODO bellow + startInfoIndex := uint64(1) endInfoIndex := stateInfoIndex.Index - - // get state info - LatestStateInfo, found := k.GetStateInfo(ctx, rollappId, endInfoIndex) - if !found { - return nil, errorsmod.Wrapf(types.ErrNotFound, - "StateInfo wasn't found for rollappId=%s, index=%d", - rollappId, endInfoIndex) - } - - // check that height exists - if height >= LatestStateInfo.StartHeight+LatestStateInfo.NumBlocks { - return nil, errorsmod.Wrapf(types.ErrStateNotExists, - "rollappId=%s, height=%d", - rollappId, height) - } - - // check if the height belongs to this batch - if height >= LatestStateInfo.StartHeight { - return &LatestStateInfo, nil - } - - maxNumberOfSteps := endInfoIndex - startInfoIndex + 1 - stepNum := uint64(0) - for ; stepNum < maxNumberOfSteps; stepNum += 1 { - // we know that endInfoIndex > startInfoIndex - // otherwise the height should have been found - if endInfoIndex <= startInfoIndex { - return nil, errorsmod.Wrapf(types.ErrLogic, - "endInfoIndex should be != than startInfoIndex rollappId=%s, startInfoIndex=%d, endInfoIndex=%d", - rollappId, startInfoIndex, endInfoIndex) - } - // 1. get state info - startStateInfo, found := k.GetStateInfo(ctx, rollappId, startInfoIndex) - if !found { - // TODO: - // if stateInfo is missing it won't be logic error if history deletion be implemented - // for that we will have to check the oldest we have - return nil, errorsmod.Wrapf(types.ErrNotFound, - "StateInfo wasn't found for rollappId=%s, index=%d", - rollappId, startInfoIndex) + for startInfoIndex <= endInfoIndex { + midIndex := startInfoIndex + (endInfoIndex-startInfoIndex)/2 + state, ok := k.GetStateInfo(ctx, rollappId, midIndex) + if !ok { + return nil, errorsmod.Wrapf(gerrc.ErrNotFound, "StateInfo wasn't found for rollappId=%s, index=%d", rollappId, midIndex) } - endStateInfo, found := k.GetStateInfo(ctx, rollappId, endInfoIndex) - if !found { - return nil, errorsmod.Wrapf(types.ErrNotFound, - "StateInfo wasn't found for rollappId=%s, index=%d", - rollappId, endInfoIndex) - } - startHeight := startStateInfo.StartHeight - endHeight := endStateInfo.StartHeight + endStateInfo.NumBlocks - 1 - - // 2. check startStateInfo - if height >= startStateInfo.StartHeight && - (startStateInfo.StartHeight+startStateInfo.NumBlocks) > height { - return &startStateInfo, nil + if state.ContainsHeight(height) { + return &state, nil } - - // 3. check endStateInfo - if height >= endStateInfo.StartHeight && - (endStateInfo.StartHeight+endStateInfo.NumBlocks) > height { - return &endStateInfo, nil - } - - // 4. calculate the average blocks per batch - avgBlocksPerBatch := (endHeight - startHeight + 1) / (endInfoIndex - startInfoIndex + 1) - if avgBlocksPerBatch == 0 { - return nil, errorsmod.Wrapf(types.ErrLogic, - "avgBlocksPerBatch is zero!!! rollappId=%s, endHeight=%d, startHeight=%d, endInfoIndex=%d, startInfoIndex=%d", - rollappId, endHeight, startHeight, endInfoIndex, startInfoIndex) - } - - // 5. load the candidate block batch - infoIndexStep := (height - startHeight) / avgBlocksPerBatch - if infoIndexStep == 0 { - infoIndexStep = 1 - } - candidateInfoIndex := startInfoIndex + infoIndexStep - if candidateInfoIndex > endInfoIndex { - // skip to the last, probably the steps to big - candidateInfoIndex = endInfoIndex - } - if candidateInfoIndex == endInfoIndex { - candidateInfoIndex = endInfoIndex - 1 - } - candidateStateInfo, found := k.GetStateInfo(ctx, rollappId, candidateInfoIndex) - if !found { - return nil, errorsmod.Wrapf(types.ErrNotFound, - "StateInfo wasn't found for rollappId=%s, index=%d", - rollappId, candidateInfoIndex) - } - - // 6. check the candidate - if candidateStateInfo.StartHeight > height { - endInfoIndex = candidateInfoIndex - 1 + if height < state.GetStartHeight() { + endInfoIndex = midIndex - 1 } else { - if candidateStateInfo.StartHeight+candidateStateInfo.NumBlocks-1 < height { - startInfoIndex = candidateInfoIndex + 1 - } else { - return &candidateStateInfo, nil - } + startInfoIndex = midIndex + 1 } } - - return nil, errorsmod.Wrapf(types.ErrLogic, - "More searching steps than indexes! rollappId=%s, stepNum=%d, maxNumberOfSteps=%d", - rollappId, stepNum, maxNumberOfSteps) + return nil, errorsmod.Wrapf(types.ErrStateNotExists, "StateInfo wasn't found for rollappId=%s, height=%d", rollappId, height) } diff --git a/x/rollapp/types/state_info.go b/x/rollapp/types/state_info.go index c8e160700..9bd65b635 100644 --- a/x/rollapp/types/state_info.go +++ b/x/rollapp/types/state_info.go @@ -35,6 +35,17 @@ func (s *StateInfo) GetLatestHeight() uint64 { return s.StartHeight + s.NumBlocks - 1 } +func (s *StateInfo) ContainsHeight(height uint64) bool { + return s.StartHeight <= height && height <= s.GetLatestHeight() +} + +func (s *StateInfo) GetBlockDescriptor(height uint64) (BlockDescriptor, bool) { + if !s.ContainsHeight(height) { + return BlockDescriptor{}, false + } + return s.BDs.BD[height-s.StartHeight], true +} + func (s *StateInfo) GetEvents() []sdk.Attribute { eventAttributes := []sdk.Attribute{ sdk.NewAttribute(AttributeKeyRollappId, s.StateInfoIndex.RollappId), From f0f6809c7b114b82631f39925377e5a22b9a9738 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:55:22 +0530 Subject: [PATCH 46/87] refactor state_test to move valid inputs to top and modify the error cases as needed --- x/lightclient/types/state.go | 7 +- x/lightclient/types/state_test.go | 181 +++++++++++------------------- 2 files changed, 70 insertions(+), 118 deletions(-) diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index e605a63ab..bab52f0c1 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -20,7 +20,8 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") } // Check if the validator pubkey matches the sequencer pubkey - if len(ibcState.Validator) > 0 && !bytes.Equal(ibcState.Validator, raState.BlockSequencer) { + // The ValidatorsHash is only available when the block header is submitted (i.e only during MsgUpdateClient) + if len(ibcState.ValidatorsHash) > 0 && !bytes.Equal(ibcState.ValidatorsHash, raState.BlockSequencer) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator does not match the sequencer") } if len(raState.NextBlockSequencer) == 0 { @@ -70,8 +71,8 @@ type IBCState struct { Root []byte // Height is the block height of the IBC consensus state the root is at Height uint64 - // Validator is the tendermint pubkey of signer of the block header - Validator []byte + // ValidatorsHash is the tendermint pubkey of signer of the block header + ValidatorsHash []byte // NextValidatorsHash is the hash of the next validator set for the next block NextValidatorsHash []byte // Timestamp is the block timestamp of the header diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 8499f1297..4cd88b560 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -9,163 +9,114 @@ import ( "github.com/stretchr/testify/require" ) +var ( + timestamp = time.Unix(1724392989, 0) + validIBCState = types.IBCState{ + Root: []byte("root"), + Timestamp: timestamp, + NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, + ValidatorsHash: []byte("sequencer"), + } + validRollappState = types.RollappState{ + BlockSequencer: []byte("sequencer"), + BlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root"), + Timestamp: timestamp, + }, + NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, + NextBlockDescriptor: rollapptypes.BlockDescriptor{ + StateRoot: []byte("root2"), + Timestamp: timestamp, + }, + } +) + func TestCheckCompatibility(t *testing.T) { type input struct { ibcState types.IBCState raState types.RollappState } - timestamp := time.Unix(1724392989, 0) testCases := []struct { name string - input input + input func() input err string }{ { name: "roots are not equal", - input: input{ - ibcState: types.IBCState{ - Root: []byte("root"), - Timestamp: timestamp, - NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, - Validator: []byte("sequencer"), - }, - raState: types.RollappState{ - BlockSequencer: []byte("sequencer"), - BlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("not same root"), - Timestamp: timestamp, - }, - NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, - NextBlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root2"), - Timestamp: timestamp, - }, - }, + input: func() input { + invalidRootRaState := validRollappState + invalidRootRaState.BlockDescriptor.StateRoot = []byte("not same root") + return input{ + ibcState: validIBCState, + raState: invalidRootRaState, + } }, err: "block descriptor state root does not match tendermint header app hash", }, { name: "validator who signed the block header is not the sequencer who submitted the block", - input: input{ - ibcState: types.IBCState{ - Root: []byte("root"), - Timestamp: timestamp, - NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, - Validator: []byte("validator"), - }, - raState: types.RollappState{ - BlockSequencer: []byte("sequencer"), - BlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root"), - Timestamp: timestamp, - }, - NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, - NextBlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root2"), - Timestamp: timestamp, - }, - }, + input: func() input { + invalidValidatorHashRAState := validRollappState + invalidValidatorHashRAState.BlockSequencer = []byte("notsamesequencer") + return input{ + ibcState: validIBCState, + raState: invalidValidatorHashRAState, + } }, err: "validator does not match the sequencer", }, { name: "nextValidatorHash does not match the sequencer who submitted the next block descriptor", - input: input{ - ibcState: types.IBCState{ - Root: []byte("root"), - Timestamp: timestamp, - NextValidatorsHash: []byte("next validator hash"), - Validator: []byte("sequencer"), - }, - raState: types.RollappState{ - BlockSequencer: []byte("sequencer"), - BlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root"), - Timestamp: timestamp, - }, - NextBlockSequencer: []byte{10, 32, 6, 234, 170, 31, 60, 164, 237, 129, 237, 38, 152, 233, 81, 240, 243, 121, 79, 108, 152, 75, 27, 247, 76, 48, 15, 132, 61, 27, 161, 82, 197, 249}, - NextBlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root2"), - Timestamp: timestamp, - }, - }, + input: func() input { + invalidNextValidatorHashIBCState := validIBCState + invalidNextValidatorHashIBCState.NextValidatorsHash = []byte("wrong next validator hash") + return input{ + ibcState: invalidNextValidatorHashIBCState, + raState: validRollappState, + } }, err: "next validator hash does not match the sequencer for h+1", }, { name: "timestamps is empty", - input: input{ - ibcState: types.IBCState{ - Root: []byte("root"), - Timestamp: timestamp, - NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, - Validator: []byte("sequencer"), - }, - raState: types.RollappState{ - BlockSequencer: []byte("sequencer"), - BlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root"), - }, - NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, - NextBlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root2"), - }, - }, + input: func() input { + emptyTimestampRAState := validRollappState + emptyTimestampRAState.BlockDescriptor.Timestamp = time.Time{} + emptyTimestampRAState.NextBlockDescriptor.Timestamp = time.Time{} + return input{ + ibcState: validIBCState, + raState: emptyTimestampRAState, + } }, err: "block descriptors do not contain block timestamp", }, { name: "timestamps are not equal", - input: input{ - ibcState: types.IBCState{ - Root: []byte("root"), - Timestamp: timestamp, - NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, - Validator: []byte("sequencer"), - }, - raState: types.RollappState{ - BlockSequencer: []byte("sequencer"), - BlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root"), - Timestamp: timestamp.Add(1), - }, - NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, - NextBlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root2"), - Timestamp: timestamp, - }, - }, + input: func() input { + invalidTimestampRAState := validRollappState + invalidTimestampRAState.BlockDescriptor.Timestamp = timestamp.Add(1) + return input{ + ibcState: validIBCState, + raState: invalidTimestampRAState, + } }, err: "block descriptor timestamp does not match tendermint header timestamp", }, { name: "all fields are compatible", - input: input{ - ibcState: types.IBCState{ - Root: []byte("root"), - Timestamp: timestamp, - NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, - Validator: []byte("sequencer"), - }, - raState: types.RollappState{ - BlockSequencer: []byte("sequencer"), - BlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root"), - Timestamp: timestamp, - }, - NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, - NextBlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root2"), - Timestamp: timestamp, - }, - }, + input: func() input { + return input{ + ibcState: validIBCState, + raState: validRollappState, + } }, err: "", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := types.CheckCompatibility(tc.input.ibcState, tc.input.raState) + input := tc.input() + err := types.CheckCompatibility(input.ibcState, input.raState) if tc.err == "" { require.NoError(t, err) } else { From 589a7ff6561c9d2ab4d81af7e9883bd06710ad8f Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:07:32 +0530 Subject: [PATCH 47/87] refactor state_test to check Error.Is instead of error string match --- x/lightclient/types/state.go | 3 ++- x/lightclient/types/state_test.go | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index bab52f0c1..38a22f388 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "errors" "time" errorsmod "cosmossdk.io/errors" @@ -30,7 +31,7 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { // Check if the nextValidatorHash matches for the sequencer for h+1 block descriptor nextValHashFromStateInfo, err := GetValHashForSequencer(raState.NextBlockSequencer) if err != nil { - return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, err.Error()) + return errors.Join(ibcclienttypes.ErrInvalidConsensus, err) } if !bytes.Equal(ibcState.NextValidatorsHash, nextValHashFromStateInfo) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1") diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 4cd88b560..6b6d190a7 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + errorsmod "cosmossdk.io/errors" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" "github.com/stretchr/testify/require" @@ -39,7 +41,7 @@ func TestCheckCompatibility(t *testing.T) { testCases := []struct { name string input func() input - err string + err error }{ { name: "roots are not equal", @@ -51,7 +53,7 @@ func TestCheckCompatibility(t *testing.T) { raState: invalidRootRaState, } }, - err: "block descriptor state root does not match tendermint header app hash", + err: errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash"), }, { name: "validator who signed the block header is not the sequencer who submitted the block", @@ -63,7 +65,7 @@ func TestCheckCompatibility(t *testing.T) { raState: invalidValidatorHashRAState, } }, - err: "validator does not match the sequencer", + err: errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator does not match the sequencer"), }, { name: "nextValidatorHash does not match the sequencer who submitted the next block descriptor", @@ -75,7 +77,7 @@ func TestCheckCompatibility(t *testing.T) { raState: validRollappState, } }, - err: "next validator hash does not match the sequencer for h+1", + err: errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1"), }, { name: "timestamps is empty", @@ -88,7 +90,7 @@ func TestCheckCompatibility(t *testing.T) { raState: emptyTimestampRAState, } }, - err: "block descriptors do not contain block timestamp", + err: types.ErrTimestampNotFound, }, { name: "timestamps are not equal", @@ -100,7 +102,7 @@ func TestCheckCompatibility(t *testing.T) { raState: invalidTimestampRAState, } }, - err: "block descriptor timestamp does not match tendermint header timestamp", + err: errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp"), }, { name: "all fields are compatible", @@ -110,17 +112,17 @@ func TestCheckCompatibility(t *testing.T) { raState: validRollappState, } }, - err: "", + err: nil, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { input := tc.input() err := types.CheckCompatibility(input.ibcState, input.raState) - if tc.err == "" { - require.NoError(t, err) + if err != nil { + require.ErrorIs(t, err, tc.err) } else { - require.ErrorContains(t, err, tc.err) + require.NoError(t, err) } }) } From 61527b81403a9e9d2b36b1e93da3f9836848255f Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:29:06 +0530 Subject: [PATCH 48/87] using validlatorhash instead of proposeaddress in checking state compatibility --- x/lightclient/ante/ibc_msg_update_client.go | 8 ++++---- x/lightclient/ante/ibc_msg_update_client_test.go | 1 + x/lightclient/keeper/client.go | 6 +++--- x/lightclient/keeper/hook_listener.go | 2 +- x/lightclient/keeper/hook_listener_test.go | 4 +++- x/lightclient/types/state.go | 6 +++++- x/lightclient/types/state_test.go | 4 ++-- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 232bd1be0..e6fb47b46 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -45,12 +45,12 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) return nil } - bd := stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()] + bd, _ := stateInfo.GetBlockDescriptor(height.GetRevisionHeight()) ibcState := types.IBCState{ Root: header.Header.GetAppHash(), Height: height.GetRevisionHeight(), - Validator: header.Header.ProposerAddress, + ValidatorsHash: header.Header.ValidatorsHash, NextValidatorsHash: header.Header.NextValidatorsHash, Timestamp: header.Header.Time, } @@ -63,9 +63,9 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli BlockDescriptor: bd, } // Check that BD for next block exists in same stateinfo - if height.GetRevisionHeight()-stateInfo.GetStartHeight()+1 < uint64(len(stateInfo.GetBDs().BD)) { + if stateInfo.ContainsHeight(height.GetRevisionHeight() + 1) { rollappState.NextBlockSequencer = sequencerPubKey - rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[height.GetRevisionHeight()-stateInfo.GetStartHeight()+1] + rollappState.NextBlockDescriptor, _ = stateInfo.GetBlockDescriptor(height.GetRevisionHeight() + 1) } else { // next BD does not exist in this state info, check the next state info nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index fef18137f..91390b5a8 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -214,6 +214,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { AppHash: []byte("appHash"), ProposerAddress: proposerAddr, Time: blocktimestamp, + ValidatorsHash: nextValsHash, NextValidatorsHash: nextValsHash, }, Commit: &cmtproto.Commit{}, diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go index 1086bdd3f..5623e6628 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/client.go @@ -34,7 +34,7 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, if err != nil { continue } - for i, bd := range stateInfo.GetBDs().BD { + for _, bd := range stateInfo.GetBDs().BD { height := ibcclienttypes.NewHeight(1, bd.GetHeight()) consensusState, found := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, height) if !found { @@ -56,8 +56,8 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, BlockDescriptor: bd, } // Check if BD for next block exists in same stateinfo - if i+1 < len(stateInfo.GetBDs().BD) { - rollappState.NextBlockDescriptor = stateInfo.GetBDs().BD[i+1] + if stateInfo.ContainsHeight(bd.GetHeight() + 1) { + rollappState.NextBlockDescriptor, _ = stateInfo.GetBlockDescriptor(bd.GetHeight() + 1) rollappState.NextBlockSequencer = sequencerPk } err := types.CheckCompatibility(ibcState, rollappState) diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 77c945f7e..5572d6438 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -60,7 +60,7 @@ func (hook rollappHook) AfterUpdateState( ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), Height: bd.GetHeight(), - Validator: tmHeaderSigner, + ValidatorsHash: tmHeaderSigner, NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), } diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index 7be614029..07c924c6a 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -190,7 +190,9 @@ func TestAfterUpdateState_SetCanonicalClient(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) rollappId := "rollapp-wants-canon-client" stateInfo := &rollapptypes.StateInfo{ - Sequencer: keepertest.Alice, + Sequencer: keepertest.Alice, + StartHeight: 1, + NumBlocks: 3, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 38a22f388..d14b15ef6 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -21,8 +21,12 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") } // Check if the validator pubkey matches the sequencer pubkey + valHashFromStateInfo, err := GetValHashForSequencer(raState.BlockSequencer) + if err != nil { + return errors.Join(ibcclienttypes.ErrInvalidConsensus, err) + } // The ValidatorsHash is only available when the block header is submitted (i.e only during MsgUpdateClient) - if len(ibcState.ValidatorsHash) > 0 && !bytes.Equal(ibcState.ValidatorsHash, raState.BlockSequencer) { + if len(ibcState.ValidatorsHash) > 0 && !bytes.Equal(ibcState.ValidatorsHash, valHashFromStateInfo) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator does not match the sequencer") } if len(raState.NextBlockSequencer) == 0 { diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 6b6d190a7..5b5d42395 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -17,10 +17,10 @@ var ( Root: []byte("root"), Timestamp: timestamp, NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, - ValidatorsHash: []byte("sequencer"), + ValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, } validRollappState = types.RollappState{ - BlockSequencer: []byte("sequencer"), + BlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), Timestamp: timestamp, From 255d25142253343fdef550ae7c3c67f503370322 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:56:07 +0530 Subject: [PATCH 49/87] addressing more pr review comments --- .../ante/ibc_msg_channel_open_ack.go | 5 +- .../ante/ibc_msg_channel_open_ack_test.go | 3 +- x/lightclient/ante/ibc_msg_update_client.go | 143 +++++++++--------- x/lightclient/ante/ibc_msgs.go | 7 +- x/lightclient/keeper/client.go | 1 - x/lightclient/keeper/hook_listener.go | 20 ++- x/lightclient/keeper/keeper.go | 17 --- x/lightclient/types/state.go | 2 - x/rollapp/keeper/msg_server_update_state.go | 2 +- x/rollapp/types/state_info.go | 5 + 10 files changed, 103 insertions(+), 102 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack.go b/x/lightclient/ante/ibc_msg_channel_open_ack.go index 3e27c7687..ebef53993 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" ) func (i IBCMessagesDecorator) HandleMsgChannelOpenAck(ctx sdk.Context, msg *ibcchanneltypes.MsgChannelOpenAck) error { @@ -23,10 +24,10 @@ func (i IBCMessagesDecorator) HandleMsgChannelOpenAck(ctx sdk.Context, msg *ibcc // Check if canon channel already exists for rollapp, if yes, return err rollapp, found := i.rollappKeeper.GetRollapp(ctx, rollappID) if !found { - return nil + return errorsmod.Wrap(gerrc.ErrInternal, "rollapp not found") } if rollapp.ChannelId != "" { - return errorsmod.Wrap(ibcchanneltypes.ErrChannelExists, "cannot create a new channel when a canonical channel already exists for the rollapp") + return errorsmod.Wrap(gerrc.ErrFailedPrecondition, "canonical channel already exists for the rollapp") } // Set this channel as the canonical channel for the rollapp rollapp.ChannelId = msg.ChannelId diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go index 9a243bc04..4e84a9516 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go @@ -8,6 +8,7 @@ import ( keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/dymensionxyz/gerr-cosmos/gerrc" "github.com/stretchr/testify/require" ) @@ -70,7 +71,7 @@ func TestHandleMsgChannelOpenAck(t *testing.T) { PortId: "transfer", ChannelId: "new-channel-on-canon-client", }, - err: ibcchanneltypes.ErrChannelExists, + err: gerrc.ErrFailedPrecondition, canonClientSet: false, }, { diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index e6fb47b46..febb9e752 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -3,7 +3,6 @@ package ante import ( sdk "github.com/cosmos/cosmos-sdk/types" 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/dymension/v3/x/lightclient/types" ) @@ -13,84 +12,82 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli if !found { return nil } - // Only continue if the client is a tendermint client as rollapp only supports tendermint clients as canonical clients - if clientState.ClientType() == exported.Tendermint { - // Cast client state to tendermint client state - we need this to get the chain id(rollapp id) - tmClientState, ok := clientState.(*ibctm.ClientState) - if !ok { - return nil - } - // Check if the client is the canonical client for the rollapp - rollappID := tmClientState.ChainId - canonicalClient, found := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) - if !found || canonicalClient != msg.ClientId { - return nil // The client is not a rollapp's canonical client. Continue with default behaviour. - } - clientMessage, err := ibcclienttypes.UnpackClientMessage(msg.ClientMessage) - if err != nil { - return nil - } - header, ok := clientMessage.(*ibctm.Header) - if !ok { - return nil - } - // Check if there are existing block descriptors for the given height of client state - height := header.TrustedHeight - stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) - if err != nil { - // No BDs found for given height. + // Cast client state to tendermint client state - we need this to get the chain id(rollapp id) + tmClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + return nil + } + // Check if the client is the canonical client for the rollapp + rollappID := tmClientState.ChainId + canonicalClient, _ := i.lightClientKeeper.GetCanonicalClient(ctx, rollappID) + if canonicalClient != msg.ClientId { + return nil // The client is not a rollapp's canonical client. Continue with default behaviour. + } + clientMessage, err := ibcclienttypes.UnpackClientMessage(msg.ClientMessage) + if err != nil { + return nil + } + header, ok := clientMessage.(*ibctm.Header) + if !ok { + return nil + } + // Check if there are existing block descriptors for the given height of client state + height := header.TrustedHeight + stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) + if err != nil { + // No BDs found for given height. + // Will accept the update optimistically + // But also save the blockProposer address with the height for future verification + blockProposer := header.Header.ProposerAddress + i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) + return nil + } + bd, _ := stateInfo.GetBlockDescriptor(height.GetRevisionHeight()) + + ibcState := types.IBCState{ + Root: header.Header.GetAppHash(), + ValidatorsHash: header.Header.ValidatorsHash, + NextValidatorsHash: header.Header.NextValidatorsHash, + Timestamp: header.Header.Time, + } + sequencerPubKey, err := i.lightClientKeeper.GetSequencerPubKey(ctx, stateInfo.Sequencer) + if err != nil { + return err + } + rollappState := types.RollappState{ + BlockSequencer: sequencerPubKey, + BlockDescriptor: bd, + } + // Check that BD for next block exists in same stateinfo + if stateInfo.ContainsHeight(height.GetRevisionHeight() + 1) { + rollappState.NextBlockSequencer = sequencerPubKey + rollappState.NextBlockDescriptor, _ = stateInfo.GetBlockDescriptor(height.GetRevisionHeight() + 1) + } else { + // next BD does not exist in this state info, check the next state info + nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) + if found { + nextSequencerPk, err := i.lightClientKeeper.GetSeqeuncerHash(ctx, nextStateInfo.Sequencer) + if err != nil { + return err + } + rollappState.NextBlockSequencer = nextSequencerPk + rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] + } else { + // if next state info does not exist, then we can't verify the next block valhash. // Will accept the update optimistically // But also save the blockProposer address with the height for future verification + // When the corresponding state info is submitted by the sequencer, will perform the verification blockProposer := header.Header.ProposerAddress i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) return nil } - bd, _ := stateInfo.GetBlockDescriptor(height.GetRevisionHeight()) - - ibcState := types.IBCState{ - Root: header.Header.GetAppHash(), - Height: height.GetRevisionHeight(), - ValidatorsHash: header.Header.ValidatorsHash, - NextValidatorsHash: header.Header.NextValidatorsHash, - Timestamp: header.Header.Time, - } - sequencerPubKey, err := i.lightClientKeeper.GetSequencerPubKey(ctx, stateInfo.Sequencer) - if err != nil { - return err - } - rollappState := types.RollappState{ - BlockSequencer: sequencerPubKey, - BlockDescriptor: bd, - } - // Check that BD for next block exists in same stateinfo - if stateInfo.ContainsHeight(height.GetRevisionHeight() + 1) { - rollappState.NextBlockSequencer = sequencerPubKey - rollappState.NextBlockDescriptor, _ = stateInfo.GetBlockDescriptor(height.GetRevisionHeight() + 1) - } else { - // next BD does not exist in this state info, check the next state info - nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) - if found { - nextSequencerPk, err := i.lightClientKeeper.GetSeqeuncerHash(ctx, nextStateInfo.Sequencer) - if err != nil { - return err - } - rollappState.NextBlockSequencer = nextSequencerPk - rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] - } else { - // if next state info does not exist, then we can't verify the next block valhash. - // Will accept the update optimistically - // But also save the blockProposer address with the height for future verification - blockProposer := header.Header.ProposerAddress - i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) - return nil - } - } - // Ensure that the ibc header is compatible with the existing rollapp state - // If it's not, we error and prevent the MsgUpdateClient from being processed - err = types.CheckCompatibility(ibcState, rollappState) - if err != nil { - return err - } } + // Ensure that the ibc header is compatible with the existing rollapp state + // If it's not, we error and prevent the MsgUpdateClient from being processed + err = types.CheckCompatibility(ibcState, rollappState) + if err != nil { + return err + } + return nil } diff --git a/x/lightclient/ante/ibc_msgs.go b/x/lightclient/ante/ibc_msgs.go index edc8a4e2f..5148fd8db 100644 --- a/x/lightclient/ante/ibc_msgs.go +++ b/x/lightclient/ante/ibc_msgs.go @@ -1,6 +1,7 @@ package ante import ( + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" @@ -32,15 +33,15 @@ func (i IBCMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo switch msg := m.(type) { case *ibcclienttypes.MsgSubmitMisbehaviour: if err := i.HandleMsgSubmitMisbehaviour(ctx, msg); err != nil { - return ctx, err + return ctx, errorsmod.Wrap(err, "failed to handle MsgSubmitMisbehaviour") } case *ibcclienttypes.MsgUpdateClient: if err := i.HandleMsgUpdateClient(ctx, msg); err != nil { - return ctx, err + return ctx, errorsmod.Wrap(err, "failed to handle MsgUpdateClient") } case *ibcchanneltypes.MsgChannelOpenAck: if err := i.HandleMsgChannelOpenAck(ctx, msg); err != nil { - return ctx, err + return ctx, errorsmod.Wrap(err, "failed to handle MsgChannelOpenAck") } default: continue diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go index 5623e6628..ecb0b4a24 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/client.go @@ -47,7 +47,6 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, } ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), - Height: height.GetRevisionHeight(), NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 5572d6438..bed0b4793 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -4,6 +4,8 @@ import ( "errors" "time" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" @@ -59,7 +61,6 @@ func (hook rollappHook) AfterUpdateState( } ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), - Height: bd.GetHeight(), ValidatorsHash: tmHeaderSigner, NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), @@ -90,7 +91,7 @@ func (hook rollappHook) AfterUpdateState( // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header - sequencerAddr, err := hook.k.getAddress(tmHeaderSigner) + sequencerAddr, err := getAddress(tmHeaderSigner) if err != nil { return err } @@ -102,3 +103,18 @@ func (hook rollappHook) AfterUpdateState( } return nil } + +// getAddress converts a tendermint public key to a bech32 address +func getAddress(tmPubkeyBz []byte) (string, error) { + var tmpk tmprotocrypto.PublicKey + err := tmpk.Unmarshal(tmPubkeyBz) + if err != nil { + return "", err + } + pubkey, err := cryptocodec.FromTmProtoPublicKey(tmpk) + if err != nil { + return "", err + } + acc := sdk.AccAddress(pubkey.Address().Bytes()) + return acc.String(), nil +} diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 141ba1ea7..b37ee3914 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -5,9 +5,7 @@ import ( "github.com/cometbft/cometbft/libs/log" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" "github.com/cosmos/cosmos-sdk/codec" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" @@ -52,21 +50,6 @@ func (k Keeper) GetSequencerPubKey(ctx sdk.Context, sequencerAddr string) ([]byt return seq.GetDymintPubKeyBytes() } -// getAddress converts a tendermint public key to a bech32 address -func (k Keeper) getAddress(tmPubkeyBz []byte) (string, error) { - var tmpk tmprotocrypto.PublicKey - err := tmpk.Unmarshal(tmPubkeyBz) - if err != nil { - return "", err - } - pubkey, err := cryptocodec.FromTmProtoPublicKey(tmpk) - if err != nil { - return "", err - } - acc := sdk.AccAddress(pubkey.Address().Bytes()) - return acc.String(), nil -} - func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index d14b15ef6..2d2f9744d 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -74,8 +74,6 @@ func GetValHashForSequencer(sequencerTmPubKeyBz []byte) ([]byte, error) { type IBCState struct { // Root is the app root shared by the IBC consensus state Root []byte - // Height is the block height of the IBC consensus state the root is at - Height uint64 // ValidatorsHash is the tendermint pubkey of signer of the block header ValidatorsHash []byte // NextValidatorsHash is the hash of the next validator set for the next block diff --git a/x/rollapp/keeper/msg_server_update_state.go b/x/rollapp/keeper/msg_server_update_state.go index 9c90fc9d0..250c7b24b 100644 --- a/x/rollapp/keeper/msg_server_update_state.go +++ b/x/rollapp/keeper/msg_server_update_state.go @@ -44,7 +44,7 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) } // check if previous state last block desc has timestamp - lastBD := stateInfo.BDs.BD[len(stateInfo.BDs.BD)-1] + lastBD := stateInfo.GetLatestBlockDescriptor() previousStateHasTimestamp = !lastBD.Timestamp.IsZero() // check to see if received height is the one we expected diff --git a/x/rollapp/types/state_info.go b/x/rollapp/types/state_info.go index 9bd65b635..3e555eff8 100644 --- a/x/rollapp/types/state_info.go +++ b/x/rollapp/types/state_info.go @@ -46,6 +46,11 @@ func (s *StateInfo) GetBlockDescriptor(height uint64) (BlockDescriptor, bool) { return s.BDs.BD[height-s.StartHeight], true } +func (s *StateInfo) GetLatestBlockDescriptor() BlockDescriptor { + //return s.BDs.BD[s.NumBlocks-1] // todo: should it be this? or the one below? using this breaks ibctesting tests + return s.BDs.BD[len(s.BDs.BD)-1] +} + func (s *StateInfo) GetEvents() []sdk.Attribute { eventAttributes := []sdk.Attribute{ sdk.NewAttribute(AttributeKeyRollappId, s.StateInfoIndex.RollappId), From d7818b06344c7dbeaf7ba35b9a83aad961fd5bbe Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:12:04 +0530 Subject: [PATCH 50/87] refactor canonical client check to early exit --- x/lightclient/keeper/client.go | 87 ++++++++++++++++------------------ x/rollapp/types/state_info.go | 2 +- 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go index ecb0b4a24..411529e62 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/client.go @@ -5,7 +5,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" 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/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" @@ -20,54 +19,52 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, if err != nil { continue } - if clientState.ClientType() == exported.Tendermint { - // Cast client state to tendermint client state - we need this to get the chain id and state height - tmClientState, ok := clientState.(*ibctm.ClientState) + // Cast client state to tendermint client state - we need this to get the chain id and state height + tmClientState, ok := clientState.(*ibctm.ClientState) + if !ok { + continue + } + if tmClientState.ChainId != rollappId { + continue + } + if !types.IsCanonicalClientParamsValid(tmClientState) { + continue + } + sequencerPk, err := k.GetSequencerPubKey(ctx, stateInfo.Sequencer) + if err != nil { + continue + } + for _, bd := range stateInfo.GetBDs().BD { + height := ibcclienttypes.NewHeight(1, bd.GetHeight()) + consensusState, found := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, height) + if !found { + continue + } + // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash + tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) if !ok { continue } - // Check if the client is for the given rollapp - if tmClientState.ChainId == rollappId { - // Check if the client params match the expected params for a canonical client - if types.IsCanonicalClientParamsValid(tmClientState) { - sequencerPk, err := k.GetSequencerPubKey(ctx, stateInfo.Sequencer) - if err != nil { - continue - } - for _, bd := range stateInfo.GetBDs().BD { - height := ibcclienttypes.NewHeight(1, bd.GetHeight()) - consensusState, found := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, height) - if !found { - continue - } - // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash - tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) - if !ok { - continue - } - ibcState := types.IBCState{ - Root: tmConsensusState.GetRoot().GetHash(), - NextValidatorsHash: tmConsensusState.NextValidatorsHash, - Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), - } - rollappState := types.RollappState{ - BlockSequencer: sequencerPk, - BlockDescriptor: bd, - } - // Check if BD for next block exists in same stateinfo - if stateInfo.ContainsHeight(bd.GetHeight() + 1) { - rollappState.NextBlockDescriptor, _ = stateInfo.GetBlockDescriptor(bd.GetHeight() + 1) - rollappState.NextBlockSequencer = sequencerPk - } - err := types.CheckCompatibility(ibcState, rollappState) - if err != nil { - continue - } - clientID = client.GetClientId() - return - } - } + ibcState := types.IBCState{ + Root: tmConsensusState.GetRoot().GetHash(), + NextValidatorsHash: tmConsensusState.NextValidatorsHash, + Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), + } + rollappState := types.RollappState{ + BlockSequencer: sequencerPk, + BlockDescriptor: bd, + } + // Check if BD for next block exists in same stateinfo + if stateInfo.ContainsHeight(bd.GetHeight() + 1) { + rollappState.NextBlockDescriptor, _ = stateInfo.GetBlockDescriptor(bd.GetHeight() + 1) + rollappState.NextBlockSequencer = sequencerPk + } + err := types.CheckCompatibility(ibcState, rollappState) + if err != nil { + continue } + clientID = client.GetClientId() + return } } return diff --git a/x/rollapp/types/state_info.go b/x/rollapp/types/state_info.go index 3e555eff8..a90bf3cce 100644 --- a/x/rollapp/types/state_info.go +++ b/x/rollapp/types/state_info.go @@ -47,7 +47,7 @@ func (s *StateInfo) GetBlockDescriptor(height uint64) (BlockDescriptor, bool) { } func (s *StateInfo) GetLatestBlockDescriptor() BlockDescriptor { - //return s.BDs.BD[s.NumBlocks-1] // todo: should it be this? or the one below? using this breaks ibctesting tests + // return s.BDs.BD[s.NumBlocks-1] // todo: should it be this? or the one below? using this breaks ibctesting tests return s.BDs.BD[len(s.BDs.BD)-1] } From 3e182923ee5307fb0ed4d70ce8edc41270e87c68 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 27 Aug 2024 19:30:11 +0530 Subject: [PATCH 51/87] using pubkey in state comparision instead of byte array --- x/lightclient/ante/ibc_msg_update_client.go | 2 +- .../ante/ibc_msg_update_client_test.go | 4 +++- x/lightclient/keeper/hook_listener.go | 2 +- x/lightclient/keeper/hook_listener_test.go | 8 +++++-- x/lightclient/keeper/keeper.go | 7 ++++--- x/lightclient/types/state.go | 15 +++++-------- x/lightclient/types/state_test.go | 21 +++++++++++++------ x/sequencer/types/sequencer.go | 13 +++++------- 8 files changed, 40 insertions(+), 32 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index febb9e752..c5579cc1f 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -66,7 +66,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // next BD does not exist in this state info, check the next state info nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) if found { - nextSequencerPk, err := i.lightClientKeeper.GetSeqeuncerHash(ctx, nextStateInfo.Sequencer) + nextSequencerPk, err := i.lightClientKeeper.GetSequencerPubKey(ctx, nextStateInfo.Sequencer) if err != nil { return err } diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index 91390b5a8..f2ced566b 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -201,6 +201,8 @@ func TestHandleMsgUpdateClient(t *testing.T) { sequencer := keepertest.Alice proposerAddr, err := k.GetSequencerPubKey(ctx, sequencer) require.NoError(t, err) + proposerAddrBytes, err := proposerAddr.Marshal() + require.NoError(t, err) blocktimestamp := time.Unix(1724392989, 0) k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") var ( @@ -212,7 +214,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { signedHeader := &cmtproto.SignedHeader{ Header: &cmtproto.Header{ AppHash: []byte("appHash"), - ProposerAddress: proposerAddr, + ProposerAddress: proposerAddrBytes, Time: blocktimestamp, ValidatorsHash: nextValsHash, NextValidatorsHash: nextValsHash, diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index bed0b4793..fbb56912e 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -65,7 +65,7 @@ func (hook rollappHook) AfterUpdateState( NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), } - sequencerPk, err := hook.k.GetSeqeuncerHash(ctx, stateInfo.Sequencer) + sequencerPk, err := hook.k.GetSequencerPubKey(ctx, stateInfo.Sequencer) if err != nil { return err } diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index 07c924c6a..e009af06a 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -106,7 +106,9 @@ func TestAfterUpdateState(t *testing.T) { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") blockSignerTmPubKey, err := k.GetSequencerPubKey(ctx, keepertest.Alice) require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKey) + blockSignerTmPubKeyBytes, err := blockSignerTmPubKey.Marshal() + require.NoError(t, err) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKeyBytes) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ @@ -142,7 +144,9 @@ func TestAfterUpdateState(t *testing.T) { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") blockSignerTmPubKey, err := k.GetSequencerPubKey(ctx, keepertest.Alice) require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKey) + blockSignerTmPubKeyBytes, err := blockSignerTmPubKey.Marshal() + require.NoError(t, err) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKeyBytes) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index b37ee3914..33515fe07 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/cometbft/cometbft/libs/log" + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -42,12 +43,12 @@ func (k Keeper) GetSeqeuncerHash(ctx sdk.Context, sequencerAddr string) ([]byte, return seq.GetDymintPubKeyHash() } -func (k Keeper) GetSequencerPubKey(ctx sdk.Context, sequencerAddr string) ([]byte, error) { +func (k Keeper) GetSequencerPubKey(ctx sdk.Context, sequencerAddr string) (tmprotocrypto.PublicKey, error) { seq, found := k.sequencerKeeper.GetSequencer(ctx, sequencerAddr) if !found { - return nil, fmt.Errorf("sequencer not found") + return tmprotocrypto.PublicKey{}, fmt.Errorf("sequencer not found") } - return seq.GetDymintPubKeyBytes() + return seq.GetCometPubKey() } func (k Keeper) Logger(ctx sdk.Context) log.Logger { diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 2d2f9744d..c851a6ac0 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -29,7 +29,7 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { if len(ibcState.ValidatorsHash) > 0 && !bytes.Equal(ibcState.ValidatorsHash, valHashFromStateInfo) { return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator does not match the sequencer") } - if len(raState.NextBlockSequencer) == 0 { + if raState.NextBlockSequencer.Size() == 0 { return ErrNextBlockDescriptorMissing } // Check if the nextValidatorHash matches for the sequencer for h+1 block descriptor @@ -53,14 +53,9 @@ func CheckCompatibility(ibcState IBCState, raState RollappState) error { // GetValHashForSequencer creates a dummy tendermint validatorset to // calculate the nextValHash for the sequencer and returns it -func GetValHashForSequencer(sequencerTmPubKeyBz []byte) ([]byte, error) { - var tmpk tmprotocrypto.PublicKey - err := tmpk.Unmarshal(sequencerTmPubKeyBz) - if err != nil { - return nil, err - } +func GetValHashForSequencer(sequencerTmPubKey tmprotocrypto.PublicKey) ([]byte, error) { var nextValSet cmttypes.ValidatorSet - updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: tmpk}}) + updates, err := cmttypes.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 1, PubKey: sequencerTmPubKey}}) if err != nil { return nil, err } @@ -84,11 +79,11 @@ type IBCState struct { type RollappState struct { // BlockSequencer is the tendermint pubkey of the sequencer who submitted the block descriptor for the required height - BlockSequencer []byte + BlockSequencer tmprotocrypto.PublicKey // BlockDescriptor is the block descriptor for the required height BlockDescriptor rollapptypes.BlockDescriptor // NextBlockSequencer is the tendermint pubkey of the sequencer who submitted the block descriptor for the next height (h+1) - NextBlockSequencer []byte + NextBlockSequencer tmprotocrypto.PublicKey // NextBlockDescriptor is the block descriptor for the next height (h+1) NextBlockDescriptor rollapptypes.BlockDescriptor } diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 5b5d42395..1ed7e23da 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -5,6 +5,8 @@ import ( "time" errorsmod "cosmossdk.io/errors" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" @@ -12,20 +14,24 @@ import ( ) var ( - timestamp = time.Unix(1724392989, 0) + sequencerPubKey = ed25519.GenPrivKey().PubKey() + tmPk, _ = cryptocodec.ToTmProtoPublicKey(sequencerPubKey) + valHash, _ = types.GetValHashForSequencer(tmPk) + timestamp = time.Unix(1724392989, 0) + validIBCState = types.IBCState{ Root: []byte("root"), Timestamp: timestamp, - NextValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, - ValidatorsHash: []byte{156, 132, 96, 43, 190, 214, 140, 148, 216, 119, 98, 162, 97, 120, 115, 32, 39, 223, 114, 56, 224, 180, 80, 228, 190, 243, 9, 248, 190, 33, 188, 23}, + NextValidatorsHash: valHash, + ValidatorsHash: valHash, } validRollappState = types.RollappState{ - BlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, + BlockSequencer: tmPk, BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), Timestamp: timestamp, }, - NextBlockSequencer: []byte{10, 32, 86, 211, 180, 178, 104, 144, 159, 216, 7, 137, 173, 225, 55, 215, 228, 176, 29, 86, 98, 130, 25, 190, 214, 24, 198, 22, 111, 37, 100, 142, 154, 87}, + NextBlockSequencer: tmPk, NextBlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root2"), Timestamp: timestamp, @@ -58,8 +64,11 @@ func TestCheckCompatibility(t *testing.T) { { name: "validator who signed the block header is not the sequencer who submitted the block", input: func() input { + newSequencer := ed25519.GenPrivKey().PubKey() + newtmPk, err := cryptocodec.ToTmProtoPublicKey(newSequencer) + require.NoError(t, err) invalidValidatorHashRAState := validRollappState - invalidValidatorHashRAState.BlockSequencer = []byte("notsamesequencer") + invalidValidatorHashRAState.BlockSequencer = newtmPk return input{ ibcState: validIBCState, raState: invalidValidatorHashRAState, diff --git a/x/sequencer/types/sequencer.go b/x/sequencer/types/sequencer.go index 7fed5ea5d..619bb8188 100644 --- a/x/sequencer/types/sequencer.go +++ b/x/sequencer/types/sequencer.go @@ -1,6 +1,7 @@ package types import ( + tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" cometbfttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -62,8 +63,8 @@ func (seq Sequencer) GetDymintPubKeyHash() ([]byte, error) { return tmValidatorSet.Hash(), nil } -// GetDymintPubKeyBytes returns the bytes of the sequencer's dymint pubkey -func (seq Sequencer) GetDymintPubKeyBytes() ([]byte, error) { +// GetCometPubKey returns the bytes of the sequencer's dymint pubkey +func (seq Sequencer) GetCometPubKey() (tmprotocrypto.PublicKey, error) { interfaceRegistry := cdctypes.NewInterfaceRegistry() cryptocodec.RegisterInterfaces(interfaceRegistry) protoCodec := codec.NewProtoCodec(interfaceRegistry) @@ -71,14 +72,10 @@ func (seq Sequencer) GetDymintPubKeyBytes() ([]byte, error) { var pubKey cryptotypes.PubKey err := protoCodec.UnpackAny(seq.DymintPubKey, &pubKey) if err != nil { - return nil, err + return tmprotocrypto.PublicKey{}, err } // convert the pubkey to tmPubKey tmPubKey, err := cryptocodec.ToTmProtoPublicKey(pubKey) - if err != nil { - return nil, err - } - - return tmPubKey.Marshal() + return tmPubKey, err } From 6e7cdb7f9d1b9a6b05a5e85ce48c280524e48b97 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:14:21 +0530 Subject: [PATCH 52/87] implementing init/export genesis --- .../dymension/lightclient/genesis.proto | 20 + x/lightclient/keeper/genesis.go | 27 + x/lightclient/keeper/genesis_test.go | 64 ++ x/lightclient/keeper/keeper.go | 33 +- x/lightclient/types/genesis.go | 26 + x/lightclient/types/genesis.pb.go | 877 ++++++++++++++++++ x/lightclient/types/genesis_test.go | 81 ++ x/lightclient/types/keys.go | 35 +- 8 files changed, 1154 insertions(+), 9 deletions(-) create mode 100644 proto/dymensionxyz/dymension/lightclient/genesis.proto create mode 100644 x/lightclient/keeper/genesis.go create mode 100644 x/lightclient/keeper/genesis_test.go create mode 100644 x/lightclient/types/genesis.go create mode 100644 x/lightclient/types/genesis.pb.go create mode 100644 x/lightclient/types/genesis_test.go diff --git a/proto/dymensionxyz/dymension/lightclient/genesis.proto b/proto/dymensionxyz/dymension/lightclient/genesis.proto new file mode 100644 index 000000000..553af7463 --- /dev/null +++ b/proto/dymensionxyz/dymension/lightclient/genesis.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package dymensionxyz.dymension.lightclient; + +option go_package = "github.com/dymensionxyz/dymension/v3/x/lightclient/types"; + +message GenesisState { + repeated CanonicalClient canonical_clients = 1; + repeated ConsensusStateSigner consensus_state_signers = 2; +} + +message CanonicalClient { + string rollapp_id = 1; + string ibc_client_id = 2; +} + +message ConsensusStateSigner { + string ibc_client_id = 1; + uint64 height = 2; + string signer = 3; +} \ No newline at end of file diff --git a/x/lightclient/keeper/genesis.go b/x/lightclient/keeper/genesis.go new file mode 100644 index 000000000..8d338c89d --- /dev/null +++ b/x/lightclient/keeper/genesis.go @@ -0,0 +1,27 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" +) + +func (k Keeper) InitGenesis(ctx sdk.Context, genesisState types.GenesisState) { + if err := genesisState.Validate(); err != nil { + panic(err) + } + for _, client := range genesisState.GetCanonicalClients() { + k.SetCanonicalClient(ctx, client.RollappId, client.IbcClientId) + } + for _, stateSigner := range genesisState.GetConsensusStateSigners() { + k.SetConsensusStateSigner(ctx, stateSigner.IbcClientId, stateSigner.Height, []byte(stateSigner.Signer)) + } +} + +func (k Keeper) ExportGenesis(ctx sdk.Context) types.GenesisState { + clients := k.GetAllCanonicalClients(ctx) + stateSigners := k.GetAllConsensusStateSigners(ctx) + return types.GenesisState{ + CanonicalClients: clients, + ConsensusStateSigners: stateSigners, + } +} diff --git a/x/lightclient/keeper/genesis_test.go b/x/lightclient/keeper/genesis_test.go new file mode 100644 index 000000000..cbafd0de2 --- /dev/null +++ b/x/lightclient/keeper/genesis_test.go @@ -0,0 +1,64 @@ +package keeper_test + +import ( + "testing" + + keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" + "github.com/stretchr/testify/require" +) + +func TestInitGenesis(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + clients := []*types.CanonicalClient{ + {RollappId: "rollapp-1", IbcClientId: "client-1"}, + {RollappId: "rollapp-2", IbcClientId: "client-2"}, + } + stateSigners := []*types.ConsensusStateSigner{ + {IbcClientId: "client-1", Height: 1, Signer: "signer-1"}, + {IbcClientId: "client-1", Height: 2, Signer: "signer-1"}, + } + + keeper.InitGenesis(ctx, types.GenesisState{ + CanonicalClients: clients, + ConsensusStateSigners: stateSigners, + }) + + ibc, found := keeper.GetCanonicalClient(ctx, "rollapp-1") + require.True(t, found) + require.Equal(t, "client-1", ibc) + ibc, found = keeper.GetCanonicalClient(ctx, "rollapp-2") + require.True(t, found) + require.Equal(t, "client-2", ibc) + + signer, found := keeper.GetConsensusStateSigner(ctx, "client-1", 1) + require.True(t, found) + require.Equal(t, "signer-1", string(signer)) + signer, found = keeper.GetConsensusStateSigner(ctx, "client-1", 2) + require.True(t, found) + require.Equal(t, "signer-1", string(signer)) +} + +func TestExportGenesis(t *testing.T) { + keeper, ctx := keepertest.LightClientKeeper(t) + + keeper.SetCanonicalClient(ctx, "rollapp-1", "client-1") + keeper.SetCanonicalClient(ctx, "rollapp-2", "client-2") + keeper.SetConsensusStateSigner(ctx, "client-1", 1, []byte("signer-1")) + keeper.SetConsensusStateSigner(ctx, "client-1", 2, []byte("signer-1")) + + genesis := keeper.ExportGenesis(ctx) + + require.Len(t, genesis.CanonicalClients, 2) + require.Equal(t, "client-1", genesis.CanonicalClients[0].IbcClientId) + require.Equal(t, "client-2", genesis.CanonicalClients[1].IbcClientId) + require.Equal(t, "rollapp-1", genesis.CanonicalClients[0].RollappId) + require.Equal(t, "rollapp-2", genesis.CanonicalClients[1].RollappId) + require.Len(t, genesis.ConsensusStateSigners, 2) + require.Equal(t, "client-1", genesis.ConsensusStateSigners[0].IbcClientId) + require.Equal(t, "client-1", genesis.ConsensusStateSigners[1].IbcClientId) + require.Equal(t, uint64(1), genesis.ConsensusStateSigners[0].Height) + require.Equal(t, uint64(2), genesis.ConsensusStateSigners[1].Height) + require.Equal(t, "signer-1", genesis.ConsensusStateSigners[0].Signer) + require.Equal(t, "signer-1", genesis.ConsensusStateSigners[1].Signer) +} diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 33515fe07..5809caa97 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -57,7 +57,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) GetCanonicalClient(ctx sdk.Context, rollappId string) (string, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.RollappClientKey(rollappId)) + bz := store.Get(types.GetRollappClientKey(rollappId)) if bz == nil { return "", false } @@ -66,10 +66,23 @@ func (k Keeper) GetCanonicalClient(ctx sdk.Context, rollappId string) (string, b func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID string) { store := ctx.KVStore(k.storeKey) - store.Set(types.RollappClientKey(rollappId), []byte(clientID)) + store.Set(types.GetRollappClientKey(rollappId), []byte(clientID)) store.Set(types.CanonicalClientKey(clientID), []byte(rollappId)) } +func (k Keeper) GetAllCanonicalClients(ctx sdk.Context) (clients []*types.CanonicalClient) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.RollappClientKey) + defer iterator.Close() // nolint: errcheck + for ; iterator.Valid(); iterator.Next() { + clients = append(clients, &types.CanonicalClient{ + RollappId: string(iterator.Key()[1:]), + IbcClientId: string(iterator.Value()), + }) + } + return +} + func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencer []byte) { store := ctx.KVStore(k.storeKey) store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), sequencer) @@ -84,6 +97,22 @@ func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height return bz, true } +func (k Keeper) GetAllConsensusStateSigners(ctx sdk.Context) (signers []*types.ConsensusStateSigner) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ConsensusStateSignerKey) + defer iterator.Close() // nolint: errcheck + for ; iterator.Valid(); iterator.Next() { + key := iterator.Key() + clientID, height := types.ParseConsensusStateSignerKey(key) + signers = append(signers, &types.ConsensusStateSigner{ + IbcClientId: clientID, + Height: height, + Signer: string(iterator.Value()), + }) + } + return +} + func (k Keeper) GetRollappForClientID(ctx sdk.Context, clientID string) (string, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.CanonicalClientKey(clientID)) diff --git a/x/lightclient/types/genesis.go b/x/lightclient/types/genesis.go new file mode 100644 index 000000000..394b4fec2 --- /dev/null +++ b/x/lightclient/types/genesis.go @@ -0,0 +1,26 @@ +package types + +import fmt "fmt" + +func (g GenesisState) Validate() error { + for _, client := range g.CanonicalClients { + if client.RollappId == "" { + return fmt.Errorf("invalid rollapp id: %v", client) + } + if client.IbcClientId == "" { + return fmt.Errorf("invalid ibc client id: %v", client) + } + } + for _, stateSigner := range g.ConsensusStateSigners { + if stateSigner.IbcClientId == "" { + return fmt.Errorf("invalid ibc client id: %v", stateSigner) + } + if stateSigner.Height == 0 { + return fmt.Errorf("invalid height: %v", stateSigner) + } + if stateSigner.Signer == "" { + return fmt.Errorf("invalid signer: %v", stateSigner) + } + } + return nil +} diff --git a/x/lightclient/types/genesis.pb.go b/x/lightclient/types/genesis.pb.go new file mode 100644 index 000000000..f8f5bc9c8 --- /dev/null +++ b/x/lightclient/types/genesis.pb.go @@ -0,0 +1,877 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dymensionxyz/dymension/lightclient/genesis.proto + +package types + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/proto" + 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 + +type GenesisState struct { + CanonicalClients []*CanonicalClient `protobuf:"bytes,1,rep,name=canonical_clients,json=canonicalClients,proto3" json:"canonical_clients,omitempty"` + ConsensusStateSigners []*ConsensusStateSigner `protobuf:"bytes,2,rep,name=consensus_state_signers,json=consensusStateSigners,proto3" json:"consensus_state_signers,omitempty"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_5520440548912168, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetCanonicalClients() []*CanonicalClient { + if m != nil { + return m.CanonicalClients + } + return nil +} + +func (m *GenesisState) GetConsensusStateSigners() []*ConsensusStateSigner { + if m != nil { + return m.ConsensusStateSigners + } + return nil +} + +type CanonicalClient struct { + RollappId string `protobuf:"bytes,1,opt,name=rollapp_id,json=rollappId,proto3" json:"rollapp_id,omitempty"` + IbcClientId string `protobuf:"bytes,2,opt,name=ibc_client_id,json=ibcClientId,proto3" json:"ibc_client_id,omitempty"` +} + +func (m *CanonicalClient) Reset() { *m = CanonicalClient{} } +func (m *CanonicalClient) String() string { return proto.CompactTextString(m) } +func (*CanonicalClient) ProtoMessage() {} +func (*CanonicalClient) Descriptor() ([]byte, []int) { + return fileDescriptor_5520440548912168, []int{1} +} +func (m *CanonicalClient) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CanonicalClient) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CanonicalClient.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 *CanonicalClient) XXX_Merge(src proto.Message) { + xxx_messageInfo_CanonicalClient.Merge(m, src) +} +func (m *CanonicalClient) XXX_Size() int { + return m.Size() +} +func (m *CanonicalClient) XXX_DiscardUnknown() { + xxx_messageInfo_CanonicalClient.DiscardUnknown(m) +} + +var xxx_messageInfo_CanonicalClient proto.InternalMessageInfo + +func (m *CanonicalClient) GetRollappId() string { + if m != nil { + return m.RollappId + } + return "" +} + +func (m *CanonicalClient) GetIbcClientId() string { + if m != nil { + return m.IbcClientId + } + return "" +} + +type ConsensusStateSigner struct { + IbcClientId string `protobuf:"bytes,1,opt,name=ibc_client_id,json=ibcClientId,proto3" json:"ibc_client_id,omitempty"` + Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *ConsensusStateSigner) Reset() { *m = ConsensusStateSigner{} } +func (m *ConsensusStateSigner) String() string { return proto.CompactTextString(m) } +func (*ConsensusStateSigner) ProtoMessage() {} +func (*ConsensusStateSigner) Descriptor() ([]byte, []int) { + return fileDescriptor_5520440548912168, []int{2} +} +func (m *ConsensusStateSigner) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusStateSigner) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusStateSigner.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 *ConsensusStateSigner) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusStateSigner.Merge(m, src) +} +func (m *ConsensusStateSigner) XXX_Size() int { + return m.Size() +} +func (m *ConsensusStateSigner) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusStateSigner.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusStateSigner proto.InternalMessageInfo + +func (m *ConsensusStateSigner) GetIbcClientId() string { + if m != nil { + return m.IbcClientId + } + return "" +} + +func (m *ConsensusStateSigner) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *ConsensusStateSigner) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "dymensionxyz.dymension.lightclient.GenesisState") + proto.RegisterType((*CanonicalClient)(nil), "dymensionxyz.dymension.lightclient.CanonicalClient") + proto.RegisterType((*ConsensusStateSigner)(nil), "dymensionxyz.dymension.lightclient.ConsensusStateSigner") +} + +func init() { + proto.RegisterFile("dymensionxyz/dymension/lightclient/genesis.proto", fileDescriptor_5520440548912168) +} + +var fileDescriptor_5520440548912168 = []byte{ + // 321 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x4f, 0xc2, 0x30, + 0x18, 0xc6, 0x29, 0x18, 0x12, 0x8a, 0x46, 0x6d, 0xfc, 0xb3, 0x8b, 0x0d, 0xd9, 0x89, 0x53, 0x67, + 0xe4, 0xc2, 0x59, 0x0e, 0x86, 0xeb, 0xf0, 0xe4, 0x65, 0x6e, 0x5d, 0x33, 0x6a, 0x46, 0xbb, 0xf0, + 0x16, 0x03, 0x7e, 0x0a, 0x3f, 0x96, 0x47, 0x8e, 0x1e, 0xcd, 0xf8, 0x22, 0x66, 0xdd, 0x20, 0x20, + 0x18, 0x3d, 0x3e, 0x4f, 0xde, 0xdf, 0xf3, 0xbe, 0x6d, 0x1e, 0x7c, 0x1b, 0x2f, 0x26, 0x42, 0x81, + 0xd4, 0x6a, 0xbe, 0x78, 0xf3, 0x36, 0xc2, 0x4b, 0x65, 0x32, 0x36, 0x3c, 0x95, 0x42, 0x19, 0x2f, + 0x11, 0x4a, 0x80, 0x04, 0x96, 0x4d, 0xb5, 0xd1, 0xc4, 0xdd, 0x26, 0xd8, 0x46, 0xb0, 0x2d, 0xc2, + 0xcd, 0x11, 0x3e, 0x7e, 0x28, 0xa9, 0x91, 0x09, 0x8d, 0x20, 0xcf, 0xf8, 0x9c, 0x87, 0x4a, 0x2b, + 0xc9, 0xc3, 0x34, 0x28, 0x87, 0xc0, 0x41, 0x9d, 0x46, 0xb7, 0x7d, 0xd7, 0x63, 0x7f, 0x07, 0xb2, + 0xc1, 0x1a, 0x1e, 0x58, 0xed, 0x9f, 0xf1, 0x5d, 0x03, 0x48, 0x86, 0xaf, 0xb9, 0x56, 0x20, 0x14, + 0xcc, 0x20, 0x80, 0x62, 0x69, 0x00, 0x32, 0x51, 0x62, 0x0a, 0x4e, 0xdd, 0xee, 0xe9, 0xff, 0x6b, + 0xcf, 0x3a, 0xc2, 0x9e, 0x3d, 0xb2, 0x01, 0xfe, 0x25, 0x3f, 0xe0, 0x82, 0xfb, 0x88, 0x4f, 0x7f, + 0x9c, 0x45, 0x6e, 0x30, 0x9e, 0xea, 0x34, 0x0d, 0xb3, 0x2c, 0x90, 0xb1, 0x83, 0x3a, 0xa8, 0xdb, + 0xf2, 0x5b, 0x95, 0x33, 0x8c, 0x89, 0x8b, 0x4f, 0x64, 0xc4, 0xab, 0xf7, 0x17, 0x13, 0x75, 0x3b, + 0xd1, 0x96, 0x11, 0x2f, 0x03, 0x86, 0xb1, 0xfb, 0x82, 0x2f, 0x0e, 0x1d, 0xb1, 0xcf, 0xa2, 0x3d, + 0x96, 0x5c, 0xe1, 0xe6, 0x58, 0x14, 0xaf, 0xb1, 0xc1, 0x47, 0x7e, 0xa5, 0x0a, 0xbf, 0xfc, 0x0b, + 0xa7, 0x61, 0xa1, 0x4a, 0xdd, 0xfb, 0x1f, 0x39, 0x45, 0xcb, 0x9c, 0xa2, 0xaf, 0x9c, 0xa2, 0xf7, + 0x15, 0xad, 0x2d, 0x57, 0xb4, 0xf6, 0xb9, 0xa2, 0xb5, 0xa7, 0x7e, 0x22, 0xcd, 0x78, 0x16, 0x31, + 0xae, 0x27, 0xde, 0x2f, 0x0d, 0x79, 0xed, 0x79, 0xf3, 0x9d, 0x9a, 0x98, 0x45, 0x26, 0x20, 0x6a, + 0xda, 0x96, 0xf4, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x0d, 0x9e, 0xd8, 0x59, 0x02, 0x00, + 0x00, +} + +func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConsensusStateSigners) > 0 { + for iNdEx := len(m.ConsensusStateSigners) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ConsensusStateSigners[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.CanonicalClients) > 0 { + for iNdEx := len(m.CanonicalClients) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CanonicalClients[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *CanonicalClient) 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 *CanonicalClient) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CanonicalClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.IbcClientId) > 0 { + i -= len(m.IbcClientId) + copy(dAtA[i:], m.IbcClientId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.IbcClientId))) + i-- + dAtA[i] = 0x12 + } + if len(m.RollappId) > 0 { + i -= len(m.RollappId) + copy(dAtA[i:], m.RollappId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.RollappId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConsensusStateSigner) 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 *ConsensusStateSigner) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusStateSigner) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.Height != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + } + if len(m.IbcClientId) > 0 { + i -= len(m.IbcClientId) + copy(dAtA[i:], m.IbcClientId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.IbcClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.CanonicalClients) > 0 { + for _, e := range m.CanonicalClients { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ConsensusStateSigners) > 0 { + for _, e := range m.ConsensusStateSigners { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *CanonicalClient) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.RollappId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.IbcClientId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} + +func (m *ConsensusStateSigner) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.IbcClientId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if m.Height != 0 { + n += 1 + sovGenesis(uint64(m.Height)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) 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 ErrIntOverflowGenesis + } + 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: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CanonicalClients", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CanonicalClients = append(m.CanonicalClients, &CanonicalClient{}) + if err := m.CanonicalClients[len(m.CanonicalClients)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusStateSigners", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConsensusStateSigners = append(m.ConsensusStateSigners, &ConsensusStateSigner{}) + if err := m.ConsensusStateSigners[len(m.ConsensusStateSigners)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CanonicalClient) 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 ErrIntOverflowGenesis + } + 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: CanonicalClient: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CanonicalClient: 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 ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + 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 IbcClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IbcClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusStateSigner) 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 ErrIntOverflowGenesis + } + 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: ConsensusStateSigner: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusStateSigner: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IbcClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IbcClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + 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 ErrIntOverflowGenesis + } + 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 ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrIntOverflowGenesis + } + 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, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/lightclient/types/genesis_test.go b/x/lightclient/types/genesis_test.go new file mode 100644 index 000000000..a40dfb4d9 --- /dev/null +++ b/x/lightclient/types/genesis_test.go @@ -0,0 +1,81 @@ +package types_test + +import ( + "testing" + + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" + "github.com/stretchr/testify/require" +) + +func TestGenesisValidate(t *testing.T) { + tests := []struct { + name string + g types.GenesisState + valid bool + }{ + { + name: "valid", + g: types.GenesisState{ + CanonicalClients: []*types.CanonicalClient{ + {RollappId: "rollapp-1", IbcClientId: "client-1"}, + {RollappId: "rollapp-2", IbcClientId: "client-2"}, + }, + ConsensusStateSigners: []*types.ConsensusStateSigner{ + {IbcClientId: "client-1", Height: 1, Signer: "signer-1"}, + {IbcClientId: "client-1", Height: 2, Signer: "signer-1"}, + }, + }, + valid: true, + }, + { + name: "invalid rollapp id", + g: types.GenesisState{ + CanonicalClients: []*types.CanonicalClient{ + {RollappId: "", IbcClientId: "client-1"}, + }, + }, + valid: false, + }, + { + name: "invalid ibc client id", + g: types.GenesisState{ + CanonicalClients: []*types.CanonicalClient{ + {RollappId: "rollapp-1", IbcClientId: ""}, + }, + }, + valid: false, + }, + { + name: "invalid height", + g: types.GenesisState{ + ConsensusStateSigners: []*types.ConsensusStateSigner{ + {IbcClientId: "client-1", Height: 0, Signer: "signer-1"}, + }, + }, + valid: false, + }, + { + name: "invalid signer", + g: types.GenesisState{ + ConsensusStateSigners: []*types.ConsensusStateSigner{ + {IbcClientId: "client-1", Height: 1, Signer: ""}, + }, + }, + valid: false, + }, + { + name: "empty", + g: types.GenesisState{}, + valid: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.valid { + require.NoError(t, tt.g.Validate()) + } else { + require.Error(t, tt.g.Validate()) + } + }) + } +} diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go index 0a034ae80..bebceec8e 100644 --- a/x/lightclient/types/keys.go +++ b/x/lightclient/types/keys.go @@ -1,6 +1,8 @@ package types import ( + "bytes" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -12,21 +14,40 @@ const ( StoreKey = ModuleName ) +const ( + keySeparator = "/" +) + var ( - rollappClientKey = []byte{0x01} - consensusStateSignerKey = []byte{0x03} + RollappClientKey = []byte{0x01} + ConsensusStateSignerKey = []byte{0x03} canonicalClientKey = []byte{0x04} ) -func RollappClientKey(rollappId string) []byte { - return append(rollappClientKey, []byte(rollappId)...) +func GetRollappClientKey(rollappId string) []byte { + key := RollappClientKey + key = append(key, []byte(rollappId)...) + return key } func ConsensusStateSignerKeyByClientID(clientID string, height uint64) []byte { - prefix := append([]byte(clientID), sdk.Uint64ToBigEndian(height)...) - return append(consensusStateSignerKey, prefix...) + key := ConsensusStateSignerKey + key = append(key, []byte(clientID)...) + key = append(key, keySeparator...) + key = append(key, sdk.Uint64ToBigEndian(height)...) + return key } func CanonicalClientKey(clientID string) []byte { - return append(canonicalClientKey, []byte(clientID)...) + key := canonicalClientKey + key = append(key, []byte(clientID)...) + return key +} + +func ParseConsensusStateSignerKey(key []byte) (clientID string, height uint64) { + key = key[len(ConsensusStateSignerKey):] + parts := bytes.Split(key, []byte(keySeparator)) + clientID = string(parts[0]) + height = sdk.BigEndianToUint64(parts[1]) + return } From 29b8a39db881481792a764d459abd2b5ff13635a Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:26:17 +0530 Subject: [PATCH 53/87] registering invariants --- app/keepers/keepers.go | 1 + testutil/keeper/lightclient.go | 1 + x/lightclient/keeper/invariants.go | 50 ++++++++++++++++++++++++++++++ x/lightclient/keeper/keeper.go | 3 ++ x/lightclient/module.go | 1 + 5 files changed, 56 insertions(+) create mode 100644 x/lightclient/keeper/invariants.go diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 8f50557f8..6452557af 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -368,6 +368,7 @@ func (a *AppKeepers) InitKeepers( a.keys[lightclientmoduletypes.StoreKey], a.IBCKeeper.ClientKeeper, a.SequencerKeeper, + a.RollappKeeper, ) a.RollappKeeper.SetSequencerKeeper(a.SequencerKeeper) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 3d2bf670b..822321794 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -84,6 +84,7 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { storeKey, mockIBCKeeper, mockSequencerKeeper, + nil, ) ctx := sdk.NewContext(stateStore, cometbftproto.Header{}, false, log.NewNopLogger()) diff --git a/x/lightclient/keeper/invariants.go b/x/lightclient/keeper/invariants.go new file mode 100644 index 000000000..94804135e --- /dev/null +++ b/x/lightclient/keeper/invariants.go @@ -0,0 +1,50 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" +) + +// RegisterInvariants registers the lightclient module invariants +func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { + ir.RegisterRoute(types.ModuleName, "canonical-client-valid", CanonicalClientsValid(k)) +} + +// CanonicalClientsValid checks that all canonical clients have a known rollapp as their chain ID +func CanonicalClientsValid(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var ( + broken bool + msg string + ) + clients := k.GetAllCanonicalClients(ctx) + for _, client := range clients { + cs, found := k.ibcClientKeeper.GetClientState(ctx, client.IbcClientId) + if !found { + broken = true + msg += "client state not found for client ID " + client.IbcClientId + "\n" + } + tmCS, ok := cs.(*ibctm.ClientState) + if !ok { + broken = true + msg += "client state is not a tendermint client state for client ID " + client.IbcClientId + "\n" + } + if tmCS.ChainId != client.RollappId { + broken = true + msg += "client state chain ID does not match rollapp ID for client " + client.IbcClientId + "\n" + } + _, found = k.rollappKeeper.GetRollapp(ctx, client.RollappId) + if !found { + broken = true + msg += "rollapp not found for given rollapp ID " + client.RollappId + "\n" + } + } + + return sdk.FormatInvariant( + types.ModuleName, "canonical-client-valid", + msg, + ), broken + } +} diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 5809caa97..2cc30efc9 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -17,6 +17,7 @@ type Keeper struct { storeKey storetypes.StoreKey ibcClientKeeper types.IBCClientKeeperExpected sequencerKeeper types.SequencerKeeperExpected + rollappKeeper types.RollappKeeperExpected } func NewKeeper( @@ -24,12 +25,14 @@ func NewKeeper( storeKey storetypes.StoreKey, ibcKeeper types.IBCClientKeeperExpected, sequencerKeeper types.SequencerKeeperExpected, + rollappKeeper types.RollappKeeperExpected, ) *Keeper { k := &Keeper{ cdc: cdc, storeKey: storeKey, ibcClientKeeper: ibcKeeper, sequencerKeeper: sequencerKeeper, + rollappKeeper: rollappKeeper, } return k } diff --git a/x/lightclient/module.go b/x/lightclient/module.go index 3c3d9ecc9..40a9f3b0c 100644 --- a/x/lightclient/module.go +++ b/x/lightclient/module.go @@ -111,6 +111,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { // RegisterInvariants registers the module's invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + keeper.RegisterInvariants(ir, am.keeper) } // InitGenesis performs the module's genesis initialization It returns From cf3e8c77bbb8736e48fbd714805d9f1d9b1f2fe5 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:38:01 +0530 Subject: [PATCH 54/87] wiring up genesis actions in module --- .../dymension/lightclient/genesis.proto | 6 +- x/lightclient/keeper/genesis_test.go | 4 +- x/lightclient/keeper/keeper.go | 8 +-- x/lightclient/module.go | 16 ++++- x/lightclient/types/genesis.go | 7 +++ x/lightclient/types/genesis.pb.go | 58 ++++++++++--------- x/lightclient/types/genesis_test.go | 17 ++++-- 7 files changed, 71 insertions(+), 45 deletions(-) diff --git a/proto/dymensionxyz/dymension/lightclient/genesis.proto b/proto/dymensionxyz/dymension/lightclient/genesis.proto index 553af7463..0c23cb12a 100644 --- a/proto/dymensionxyz/dymension/lightclient/genesis.proto +++ b/proto/dymensionxyz/dymension/lightclient/genesis.proto @@ -1,11 +1,13 @@ syntax = "proto3"; package dymensionxyz.dymension.lightclient; +import "gogoproto/gogo.proto"; + option go_package = "github.com/dymensionxyz/dymension/v3/x/lightclient/types"; message GenesisState { - repeated CanonicalClient canonical_clients = 1; - repeated ConsensusStateSigner consensus_state_signers = 2; + repeated CanonicalClient canonical_clients = 1 [ (gogoproto.nullable) = false ]; + repeated ConsensusStateSigner consensus_state_signers = 2 [ (gogoproto.nullable) = false ]; } message CanonicalClient { diff --git a/x/lightclient/keeper/genesis_test.go b/x/lightclient/keeper/genesis_test.go index cbafd0de2..f3a723d3b 100644 --- a/x/lightclient/keeper/genesis_test.go +++ b/x/lightclient/keeper/genesis_test.go @@ -10,11 +10,11 @@ import ( func TestInitGenesis(t *testing.T) { keeper, ctx := keepertest.LightClientKeeper(t) - clients := []*types.CanonicalClient{ + clients := []types.CanonicalClient{ {RollappId: "rollapp-1", IbcClientId: "client-1"}, {RollappId: "rollapp-2", IbcClientId: "client-2"}, } - stateSigners := []*types.ConsensusStateSigner{ + stateSigners := []types.ConsensusStateSigner{ {IbcClientId: "client-1", Height: 1, Signer: "signer-1"}, {IbcClientId: "client-1", Height: 2, Signer: "signer-1"}, } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 2cc30efc9..b4d34cd09 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -73,12 +73,12 @@ func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID s store.Set(types.CanonicalClientKey(clientID), []byte(rollappId)) } -func (k Keeper) GetAllCanonicalClients(ctx sdk.Context) (clients []*types.CanonicalClient) { +func (k Keeper) GetAllCanonicalClients(ctx sdk.Context) (clients []types.CanonicalClient) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, types.RollappClientKey) defer iterator.Close() // nolint: errcheck for ; iterator.Valid(); iterator.Next() { - clients = append(clients, &types.CanonicalClient{ + clients = append(clients, types.CanonicalClient{ RollappId: string(iterator.Key()[1:]), IbcClientId: string(iterator.Value()), }) @@ -100,14 +100,14 @@ func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height return bz, true } -func (k Keeper) GetAllConsensusStateSigners(ctx sdk.Context) (signers []*types.ConsensusStateSigner) { +func (k Keeper) GetAllConsensusStateSigners(ctx sdk.Context) (signers []types.ConsensusStateSigner) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, types.ConsensusStateSignerKey) defer iterator.Close() // nolint: errcheck for ; iterator.Valid(); iterator.Next() { key := iterator.Key() clientID, height := types.ParseConsensusStateSignerKey(key) - signers = append(signers, &types.ConsensusStateSigner{ + signers = append(signers, types.ConsensusStateSigner{ IbcClientId: clientID, Height: height, Signer: string(iterator.Value()), diff --git a/x/lightclient/module.go b/x/lightclient/module.go index 40a9f3b0c..31fef91fa 100644 --- a/x/lightclient/module.go +++ b/x/lightclient/module.go @@ -2,6 +2,7 @@ package sequencer import ( "encoding/json" + "fmt" abci "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/cosmos-sdk/client" @@ -52,12 +53,17 @@ func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { // DefaultGenesis returns the module's default genesis state. func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { - return nil + defaultGenesis := types.DefaultGenesisState() + return cdc.MustMarshalJSON(&defaultGenesis) } // ValidateGenesis performs genesis state validation for the module. func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { - return nil + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() } // RegisterRESTRoutes registers the module's REST service handlers. @@ -117,12 +123,16 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { // InitGenesis performs the module's genesis initialization It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { + var genState types.GenesisState + cdc.MustUnmarshalJSON(gs, &genState) + am.keeper.InitGenesis(ctx, genState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the module's exported genesis state as raw JSON bytes. func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - return nil + genState := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(&genState) } // ConsensusVersion implements ConsensusVersion. diff --git a/x/lightclient/types/genesis.go b/x/lightclient/types/genesis.go index 394b4fec2..5da1a4270 100644 --- a/x/lightclient/types/genesis.go +++ b/x/lightclient/types/genesis.go @@ -2,6 +2,13 @@ package types import fmt "fmt" +func DefaultGenesisState() GenesisState { + return GenesisState{ + CanonicalClients: []CanonicalClient{}, + ConsensusStateSigners: []ConsensusStateSigner{}, + } +} + func (g GenesisState) Validate() error { for _, client := range g.CanonicalClients { if client.RollappId == "" { diff --git a/x/lightclient/types/genesis.pb.go b/x/lightclient/types/genesis.pb.go index f8f5bc9c8..653337521 100644 --- a/x/lightclient/types/genesis.pb.go +++ b/x/lightclient/types/genesis.pb.go @@ -5,6 +5,7 @@ package types import ( fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" io "io" math "math" @@ -23,8 +24,8 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type GenesisState struct { - CanonicalClients []*CanonicalClient `protobuf:"bytes,1,rep,name=canonical_clients,json=canonicalClients,proto3" json:"canonical_clients,omitempty"` - ConsensusStateSigners []*ConsensusStateSigner `protobuf:"bytes,2,rep,name=consensus_state_signers,json=consensusStateSigners,proto3" json:"consensus_state_signers,omitempty"` + CanonicalClients []CanonicalClient `protobuf:"bytes,1,rep,name=canonical_clients,json=canonicalClients,proto3" json:"canonical_clients"` + ConsensusStateSigners []ConsensusStateSigner `protobuf:"bytes,2,rep,name=consensus_state_signers,json=consensusStateSigners,proto3" json:"consensus_state_signers"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -60,14 +61,14 @@ func (m *GenesisState) XXX_DiscardUnknown() { var xxx_messageInfo_GenesisState proto.InternalMessageInfo -func (m *GenesisState) GetCanonicalClients() []*CanonicalClient { +func (m *GenesisState) GetCanonicalClients() []CanonicalClient { if m != nil { return m.CanonicalClients } return nil } -func (m *GenesisState) GetConsensusStateSigners() []*ConsensusStateSigner { +func (m *GenesisState) GetConsensusStateSigners() []ConsensusStateSigner { if m != nil { return m.ConsensusStateSigners } @@ -197,28 +198,29 @@ func init() { } var fileDescriptor_5520440548912168 = []byte{ - // 321 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x4f, 0xc2, 0x30, - 0x18, 0xc6, 0x29, 0x18, 0x12, 0x8a, 0x46, 0x6d, 0xfc, 0xb3, 0x8b, 0x0d, 0xd9, 0x89, 0x53, 0x67, - 0xe4, 0xc2, 0x59, 0x0e, 0x86, 0xeb, 0xf0, 0xe4, 0x65, 0x6e, 0x5d, 0x33, 0x6a, 0x46, 0xbb, 0xf0, - 0x16, 0x03, 0x7e, 0x0a, 0x3f, 0x96, 0x47, 0x8e, 0x1e, 0xcd, 0xf8, 0x22, 0x66, 0xdd, 0x20, 0x20, - 0x18, 0x3d, 0x3e, 0x4f, 0xde, 0xdf, 0xf3, 0xbe, 0x6d, 0x1e, 0x7c, 0x1b, 0x2f, 0x26, 0x42, 0x81, - 0xd4, 0x6a, 0xbe, 0x78, 0xf3, 0x36, 0xc2, 0x4b, 0x65, 0x32, 0x36, 0x3c, 0x95, 0x42, 0x19, 0x2f, - 0x11, 0x4a, 0x80, 0x04, 0x96, 0x4d, 0xb5, 0xd1, 0xc4, 0xdd, 0x26, 0xd8, 0x46, 0xb0, 0x2d, 0xc2, - 0xcd, 0x11, 0x3e, 0x7e, 0x28, 0xa9, 0x91, 0x09, 0x8d, 0x20, 0xcf, 0xf8, 0x9c, 0x87, 0x4a, 0x2b, - 0xc9, 0xc3, 0x34, 0x28, 0x87, 0xc0, 0x41, 0x9d, 0x46, 0xb7, 0x7d, 0xd7, 0x63, 0x7f, 0x07, 0xb2, - 0xc1, 0x1a, 0x1e, 0x58, 0xed, 0x9f, 0xf1, 0x5d, 0x03, 0x48, 0x86, 0xaf, 0xb9, 0x56, 0x20, 0x14, - 0xcc, 0x20, 0x80, 0x62, 0x69, 0x00, 0x32, 0x51, 0x62, 0x0a, 0x4e, 0xdd, 0xee, 0xe9, 0xff, 0x6b, - 0xcf, 0x3a, 0xc2, 0x9e, 0x3d, 0xb2, 0x01, 0xfe, 0x25, 0x3f, 0xe0, 0x82, 0xfb, 0x88, 0x4f, 0x7f, - 0x9c, 0x45, 0x6e, 0x30, 0x9e, 0xea, 0x34, 0x0d, 0xb3, 0x2c, 0x90, 0xb1, 0x83, 0x3a, 0xa8, 0xdb, - 0xf2, 0x5b, 0x95, 0x33, 0x8c, 0x89, 0x8b, 0x4f, 0x64, 0xc4, 0xab, 0xf7, 0x17, 0x13, 0x75, 0x3b, - 0xd1, 0x96, 0x11, 0x2f, 0x03, 0x86, 0xb1, 0xfb, 0x82, 0x2f, 0x0e, 0x1d, 0xb1, 0xcf, 0xa2, 0x3d, - 0x96, 0x5c, 0xe1, 0xe6, 0x58, 0x14, 0xaf, 0xb1, 0xc1, 0x47, 0x7e, 0xa5, 0x0a, 0xbf, 0xfc, 0x0b, - 0xa7, 0x61, 0xa1, 0x4a, 0xdd, 0xfb, 0x1f, 0x39, 0x45, 0xcb, 0x9c, 0xa2, 0xaf, 0x9c, 0xa2, 0xf7, - 0x15, 0xad, 0x2d, 0x57, 0xb4, 0xf6, 0xb9, 0xa2, 0xb5, 0xa7, 0x7e, 0x22, 0xcd, 0x78, 0x16, 0x31, - 0xae, 0x27, 0xde, 0x2f, 0x0d, 0x79, 0xed, 0x79, 0xf3, 0x9d, 0x9a, 0x98, 0x45, 0x26, 0x20, 0x6a, - 0xda, 0x96, 0xf4, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x0d, 0x9e, 0xd8, 0x59, 0x02, 0x00, - 0x00, + // 343 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4e, 0xfa, 0x40, + 0x10, 0xc6, 0xbb, 0x40, 0x48, 0x58, 0xfe, 0xff, 0xa8, 0x0d, 0x6a, 0x63, 0x62, 0x25, 0x3d, 0x71, + 0x6a, 0x8d, 0x5c, 0x38, 0xc3, 0xc1, 0x70, 0x2d, 0x9e, 0xbc, 0x34, 0xed, 0x76, 0x5d, 0xd6, 0x94, + 0xdd, 0x86, 0x59, 0x08, 0xf8, 0x14, 0x3e, 0x16, 0x47, 0x8e, 0x9e, 0x8c, 0x81, 0xbb, 0xcf, 0x60, + 0xba, 0x2d, 0x04, 0x04, 0xa3, 0xb7, 0x7e, 0xd3, 0xf9, 0x7d, 0xdf, 0xec, 0x64, 0xf0, 0x6d, 0x3c, + 0x1f, 0x51, 0x01, 0x5c, 0x8a, 0xd9, 0xfc, 0xc5, 0xdb, 0x0a, 0x2f, 0xe1, 0x6c, 0xa8, 0x48, 0xc2, + 0xa9, 0x50, 0x1e, 0xa3, 0x82, 0x02, 0x07, 0x37, 0x1d, 0x4b, 0x25, 0x4d, 0x67, 0x97, 0x70, 0xb7, + 0xc2, 0xdd, 0x21, 0xae, 0x1a, 0x4c, 0x32, 0xa9, 0xdb, 0xbd, 0xec, 0x2b, 0x27, 0x9d, 0x4f, 0x84, + 0xff, 0xdd, 0xe7, 0x5e, 0x03, 0x15, 0x2a, 0x6a, 0x3e, 0xe1, 0x33, 0x12, 0x0a, 0x29, 0x38, 0x09, + 0x93, 0x20, 0x47, 0xc1, 0x42, 0xcd, 0x72, 0xab, 0x7e, 0xd7, 0x76, 0x7f, 0x8f, 0x71, 0x7b, 0x1b, + 0xb8, 0xa7, 0x75, 0xb7, 0xb2, 0x78, 0xbf, 0x31, 0xfc, 0x53, 0xb2, 0x5f, 0x06, 0x73, 0x8a, 0x2f, + 0x89, 0x14, 0x40, 0x05, 0x4c, 0x20, 0x80, 0x2c, 0x3a, 0x00, 0xce, 0x04, 0x1d, 0x83, 0x55, 0xd2, + 0x69, 0x9d, 0x3f, 0xa5, 0x6d, 0x2c, 0xf4, 0xf0, 0x03, 0x6d, 0x50, 0x44, 0x9e, 0x93, 0x23, 0xff, + 0xc0, 0x79, 0xc0, 0x27, 0xdf, 0x46, 0x34, 0xaf, 0x31, 0x1e, 0xcb, 0x24, 0x09, 0xd3, 0x34, 0xe0, + 0xb1, 0x85, 0x9a, 0xa8, 0x55, 0xf3, 0x6b, 0x45, 0xa5, 0x1f, 0x9b, 0x0e, 0xfe, 0xcf, 0x23, 0x52, + 0xec, 0x22, 0xeb, 0x28, 0xe9, 0x8e, 0x3a, 0x8f, 0x48, 0x6e, 0xd0, 0x8f, 0x9d, 0x67, 0xdc, 0x38, + 0x36, 0xca, 0x21, 0x8b, 0x0e, 0x58, 0xf3, 0x02, 0x57, 0x87, 0x34, 0x7b, 0x93, 0x36, 0xae, 0xf8, + 0x85, 0xca, 0xea, 0xf9, 0x46, 0xac, 0xb2, 0x86, 0x0a, 0xd5, 0xf5, 0x17, 0x2b, 0x1b, 0x2d, 0x57, + 0x36, 0xfa, 0x58, 0xd9, 0xe8, 0x75, 0x6d, 0x1b, 0xcb, 0xb5, 0x6d, 0xbc, 0xad, 0x6d, 0xe3, 0xb1, + 0xc3, 0xb8, 0x1a, 0x4e, 0x22, 0x97, 0xc8, 0x91, 0xf7, 0xc3, 0x0d, 0x4d, 0xdb, 0xde, 0x6c, 0xef, + 0x90, 0xd4, 0x3c, 0xa5, 0x10, 0x55, 0xf5, 0x35, 0xb4, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x94, + 0x4c, 0x39, 0x70, 0x7b, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -484,7 +486,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.CanonicalClients = append(m.CanonicalClients, &CanonicalClient{}) + m.CanonicalClients = append(m.CanonicalClients, CanonicalClient{}) if err := m.CanonicalClients[len(m.CanonicalClients)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -518,7 +520,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ConsensusStateSigners = append(m.ConsensusStateSigners, &ConsensusStateSigner{}) + m.ConsensusStateSigners = append(m.ConsensusStateSigners, ConsensusStateSigner{}) if err := m.ConsensusStateSigners[len(m.ConsensusStateSigners)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } diff --git a/x/lightclient/types/genesis_test.go b/x/lightclient/types/genesis_test.go index a40dfb4d9..0be957cd3 100644 --- a/x/lightclient/types/genesis_test.go +++ b/x/lightclient/types/genesis_test.go @@ -16,11 +16,11 @@ func TestGenesisValidate(t *testing.T) { { name: "valid", g: types.GenesisState{ - CanonicalClients: []*types.CanonicalClient{ + CanonicalClients: []types.CanonicalClient{ {RollappId: "rollapp-1", IbcClientId: "client-1"}, {RollappId: "rollapp-2", IbcClientId: "client-2"}, }, - ConsensusStateSigners: []*types.ConsensusStateSigner{ + ConsensusStateSigners: []types.ConsensusStateSigner{ {IbcClientId: "client-1", Height: 1, Signer: "signer-1"}, {IbcClientId: "client-1", Height: 2, Signer: "signer-1"}, }, @@ -30,7 +30,7 @@ func TestGenesisValidate(t *testing.T) { { name: "invalid rollapp id", g: types.GenesisState{ - CanonicalClients: []*types.CanonicalClient{ + CanonicalClients: []types.CanonicalClient{ {RollappId: "", IbcClientId: "client-1"}, }, }, @@ -39,7 +39,7 @@ func TestGenesisValidate(t *testing.T) { { name: "invalid ibc client id", g: types.GenesisState{ - CanonicalClients: []*types.CanonicalClient{ + CanonicalClients: []types.CanonicalClient{ {RollappId: "rollapp-1", IbcClientId: ""}, }, }, @@ -48,7 +48,7 @@ func TestGenesisValidate(t *testing.T) { { name: "invalid height", g: types.GenesisState{ - ConsensusStateSigners: []*types.ConsensusStateSigner{ + ConsensusStateSigners: []types.ConsensusStateSigner{ {IbcClientId: "client-1", Height: 0, Signer: "signer-1"}, }, }, @@ -57,7 +57,7 @@ func TestGenesisValidate(t *testing.T) { { name: "invalid signer", g: types.GenesisState{ - ConsensusStateSigners: []*types.ConsensusStateSigner{ + ConsensusStateSigners: []types.ConsensusStateSigner{ {IbcClientId: "client-1", Height: 1, Signer: ""}, }, }, @@ -68,6 +68,11 @@ func TestGenesisValidate(t *testing.T) { g: types.GenesisState{}, valid: true, }, + { + name: "default", + g: types.DefaultGenesisState(), + valid: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From af005e1c9916ada923ad45551dd044eb9af8995d Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 09:09:23 +0530 Subject: [PATCH 55/87] moving the timestamp upgrade logic to rollapp keeper --- x/dymns/keeper/hooks.go | 2 +- x/lightclient/keeper/hook_listener.go | 6 ------ x/lightclient/keeper/hook_listener_test.go | 23 +++++++-------------- x/rollapp/keeper/msg_server_update_state.go | 17 +++++++++------ x/rollapp/types/hooks.go | 12 +++++------ 5 files changed, 26 insertions(+), 34 deletions(-) diff --git a/x/dymns/keeper/hooks.go b/x/dymns/keeper/hooks.go index 450174d08..5047a2275 100644 --- a/x/dymns/keeper/hooks.go +++ b/x/dymns/keeper/hooks.go @@ -286,7 +286,7 @@ func (h rollappHooks) BeforeUpdateState(_ sdk.Context, _ string, _ string, _ boo return nil } -func (h rollappHooks) AfterUpdateState(_ sdk.Context, _ string, _ *rollapptypes.StateInfo, _ bool, _ bool) error { +func (h rollappHooks) AfterUpdateState(_ sdk.Context, _ string, _ *rollapptypes.StateInfo) error { return nil } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index fbb56912e..e8c05729c 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -34,8 +34,6 @@ func (hook rollappHook) AfterUpdateState( ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo, - isFirstStateUpdate bool, - previousStateHasTimestamp bool, ) error { canonicalClient, found := hook.k.GetCanonicalClient(ctx, rollappId) if !found { @@ -80,10 +78,6 @@ func (hook rollappHook) AfterUpdateState( } err = types.CheckCompatibility(ibcState, rollappState) if err != nil { - // Only require timestamp on BD if first ever update, or the previous update had BD - if errors.Is(err, types.ErrTimestampNotFound) && !isFirstStateUpdate && !previousStateHasTimestamp { - continue - } // The BD for (h+1) is missing, cannot verify if the nextvalhash matches if errors.Is(err, types.ErrNextBlockDescriptorMissing) { return err diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index e009af06a..388b82acb 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -12,10 +12,8 @@ import ( ) type testInput struct { - rollappId string - stateInfo *rollapptypes.StateInfo - isFirstStateUpdate bool - previousStateHasTimestamp bool + rollappId string + stateInfo *rollapptypes.StateInfo } func TestAfterUpdateState(t *testing.T) { @@ -28,9 +26,8 @@ func TestAfterUpdateState(t *testing.T) { name: "canonical client does not exist for rollapp", prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { return testInput{ - rollappId: "rollapp-no-canon-client", - stateInfo: &rollapptypes.StateInfo{}, - isFirstStateUpdate: true, + rollappId: "rollapp-no-canon-client", + stateInfo: &rollapptypes.StateInfo{}, } }, expectErr: false, @@ -40,9 +37,8 @@ func TestAfterUpdateState(t *testing.T) { prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") return testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{}, - isFirstStateUpdate: true, + rollappId: "rollapp-has-canon-client", + stateInfo: &rollapptypes.StateInfo{}, } }, expectErr: false, @@ -64,7 +60,6 @@ func TestAfterUpdateState(t *testing.T) { }, }, }, - isFirstStateUpdate: true, } }, expectErr: false, @@ -95,7 +90,6 @@ func TestAfterUpdateState(t *testing.T) { }, }, }, - isFirstStateUpdate: true, } }, expectErr: true, @@ -133,7 +127,6 @@ func TestAfterUpdateState(t *testing.T) { }, }, }, - isFirstStateUpdate: true, } }, expectErr: false, @@ -180,7 +173,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, input.isFirstStateUpdate, input.previousStateHasTimestamp) + err := keeper.RollappHooks().AfterUpdateState(ctx, input.rollappId, input.stateInfo) if tc.expectErr { require.Error(t, err) } else { @@ -217,7 +210,7 @@ func TestAfterUpdateState_SetCanonicalClient(t *testing.T) { }, }, } - err := keeper.RollappHooks().AfterUpdateState(ctx, rollappId, stateInfo, false, false) + err := keeper.RollappHooks().AfterUpdateState(ctx, rollappId, stateInfo) require.NoError(t, err) clientID, found := keeper.GetCanonicalClient(ctx, rollappId) diff --git a/x/rollapp/keeper/msg_server_update_state.go b/x/rollapp/keeper/msg_server_update_state.go index 250c7b24b..2d4ac3f78 100644 --- a/x/rollapp/keeper/msg_server_update_state.go +++ b/x/rollapp/keeper/msg_server_update_state.go @@ -2,10 +2,12 @@ package keeper import ( "context" + "errors" errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + lightclienttypes "github.com/dymensionxyz/dymension/v3/x/lightclient/types" "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) @@ -29,9 +31,9 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) // retrieve last updating index var newIndex, lastIndex uint64 - var previousStateHasTimestamp bool latestStateInfoIndex, found := k.GetLatestStateInfoIndex(ctx, msg.RollappId) - isFirstStateUpdate := !found + // If this is the very first state update, assume the rollapp is upgraded + rollappUpgradedForTimestamp := !found if found { // retrieve last updating index stateInfo, found := k.GetStateInfo(ctx, msg.RollappId, latestStateInfoIndex.Index) @@ -43,9 +45,9 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) latestStateInfoIndex.Index, msg.RollappId) } - // check if previous state last block desc has timestamp + // if previous block descriptor has timestamp, it means the rollapp is upgraded lastBD := stateInfo.GetLatestBlockDescriptor() - previousStateHasTimestamp = !lastBD.Timestamp.IsZero() + rollappUpgradedForTimestamp = !lastBD.Timestamp.IsZero() // check to see if received height is the one we expected expectedStartHeight := stateInfo.StartHeight + stateInfo.NumBlocks @@ -71,9 +73,12 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) // Write new state information to the store indexed by k.SetStateInfo(ctx, *stateInfo) - err = k.hooks.AfterUpdateState(ctx, msg.RollappId, stateInfo, isFirstStateUpdate, previousStateHasTimestamp) + err = k.hooks.AfterUpdateState(ctx, msg.RollappId, stateInfo) if err != nil { - return nil, err + // Upgraded rollapps must have timestamps in BDs + if !(errors.Is(err, lightclienttypes.ErrTimestampNotFound) && rollappUpgradedForTimestamp) { + return nil, err + } } stateInfoIndex := stateInfo.GetIndex() diff --git a/x/rollapp/types/hooks.go b/x/rollapp/types/hooks.go index 1ab31aabc..937335649 100644 --- a/x/rollapp/types/hooks.go +++ b/x/rollapp/types/hooks.go @@ -11,9 +11,9 @@ import ( // RollappHooks event hooks for rollapp object (noalias) type RollappHooks interface { - BeforeUpdateState(ctx sdk.Context, seqAddr, rollappId string, lastStateUpdateBySequencer bool) error // Must be called when a rollapp's state changes - AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo, isFirstStateUpdate bool, previousStateHasTimestamp bool) error // Must be called when a rollapp's state changes - AfterStateFinalized(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error // Must be called when a rollapp's state changes + BeforeUpdateState(ctx sdk.Context, seqAddr, rollappId string, lastStateUpdateBySequencer bool) error // Must be called when a rollapp's state changes + AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error // Must be called when a rollapp's state changes + AfterStateFinalized(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error // Must be called when a rollapp's state changes FraudSubmitted(ctx sdk.Context, rollappID string, height uint64, seqAddr string) error RollappCreated(ctx sdk.Context, rollappID, alias string, creator sdk.AccAddress) error } @@ -38,9 +38,9 @@ func (h MultiRollappHooks) BeforeUpdateState(ctx sdk.Context, seqAddr, rollappId return nil } -func (h MultiRollappHooks) AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo, isFirstStateUpdate bool, previousStateHasTimestamp bool) error { +func (h MultiRollappHooks) AfterUpdateState(ctx sdk.Context, rollappID string, stateInfo *StateInfo) error { for i := range h { - err := h[i].AfterUpdateState(ctx, rollappID, stateInfo, isFirstStateUpdate, previousStateHasTimestamp) + err := h[i].AfterUpdateState(ctx, rollappID, stateInfo) if err != nil { return err } @@ -88,7 +88,7 @@ func (StubRollappCreatedHooks) RollappCreated(sdk.Context, string, string, sdk.A } func (StubRollappCreatedHooks) BeforeUpdateState(sdk.Context, string, string, bool) error { return nil } -func (StubRollappCreatedHooks) AfterUpdateState(sdk.Context, string, *StateInfo, bool, bool) error { +func (StubRollappCreatedHooks) AfterUpdateState(sdk.Context, string, *StateInfo) error { return nil } func (StubRollappCreatedHooks) FraudSubmitted(sdk.Context, string, uint64, string) error { return nil } From faf86031890bc2c38360317bfb1e2d08c31ef835 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:32:12 +0530 Subject: [PATCH 56/87] addressing some more PR review comments --- .../ante/ibc_msg_channel_open_ack_test.go | 26 ++-- .../ante/ibc_msg_update_client_test.go | 2 +- x/lightclient/keeper/client.go | 6 +- x/lightclient/keeper/hook_listener.go | 2 +- x/lightclient/keeper/hook_listener_test.go | 2 +- x/lightclient/keeper/keeper.go | 4 +- x/lightclient/types/params.go | 24 +++- x/lightclient/types/params_test.go | 117 ++++++++++++++++++ x/sequencer/types/sequencer.go | 25 ++-- 9 files changed, 172 insertions(+), 36 deletions(-) create mode 100644 x/lightclient/types/params_test.go diff --git a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go index 4e84a9516..020d88c08 100644 --- a/x/lightclient/ante/ibc_msg_channel_open_ack_test.go +++ b/x/lightclient/ante/ibc_msg_channel_open_ack_test.go @@ -42,10 +42,10 @@ func TestHandleMsgChannelOpenAck(t *testing.T) { keeper.SetCanonicalClient(ctx, "rollapp-no-canon-channel", "canon-client-id-2") ibcMsgDecorator := ante.NewIBCMessagesDecorator(*keeper, ibcclientKeeper, ibcchannelKeeper, rollappKeeper) testCases := []struct { - name string - inputMsg ibcchanneltypes.MsgChannelOpenAck - err error - canonClientSet bool + name string + inputMsg ibcchanneltypes.MsgChannelOpenAck + err error + canonChannelSet bool }{ { name: "port id is not transfer port", @@ -53,8 +53,8 @@ func TestHandleMsgChannelOpenAck(t *testing.T) { PortId: "not-transfer-port", ChannelId: "channel-id", }, - err: nil, - canonClientSet: false, + err: nil, + canonChannelSet: false, }, { name: "channel not on a canonical client", @@ -62,8 +62,8 @@ func TestHandleMsgChannelOpenAck(t *testing.T) { PortId: "transfer", ChannelId: "non-canon-channel-id", }, - err: nil, - canonClientSet: false, + err: nil, + canonChannelSet: false, }, { name: "canonical channel already exists for rollapp", @@ -71,8 +71,8 @@ func TestHandleMsgChannelOpenAck(t *testing.T) { PortId: "transfer", ChannelId: "new-channel-on-canon-client", }, - err: gerrc.ErrFailedPrecondition, - canonClientSet: false, + err: gerrc.ErrFailedPrecondition, + canonChannelSet: false, }, { name: "canonical channel does not exist - set new channel as canonical", @@ -80,8 +80,8 @@ func TestHandleMsgChannelOpenAck(t *testing.T) { PortId: "transfer", ChannelId: "first-channel-on-canon-client", }, - err: nil, - canonClientSet: true, + err: nil, + canonChannelSet: true, }, } for _, tc := range testCases { @@ -92,7 +92,7 @@ func TestHandleMsgChannelOpenAck(t *testing.T) { } else { require.NoError(t, err) } - if tc.canonClientSet { + if tc.canonChannelSet { rollapp, found := rollappKeeper.GetRollapp(ctx, "rollapp-no-canon-channel") require.True(t, found) require.Equal(t, tc.inputMsg.ChannelId, rollapp.ChannelId) diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index f2ced566b..ae15a5c3f 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -209,7 +209,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { valSet *cmtproto.ValidatorSet trustedVals *cmtproto.ValidatorSet ) - nextValsHash, err := k.GetSeqeuncerHash(ctx, sequencer) + nextValsHash, err := k.GetSequencerHash(ctx, sequencer) require.NoError(t, err) signedHeader := &cmtproto.SignedHeader{ Header: &cmtproto.Header{ diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go index 411529e62..8a42dce16 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/client.go @@ -10,6 +10,10 @@ import ( rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) +const ( + ibcRevisionNumber = 1 +) + // GetProspectiveCanonicalClient returns the client id of the first IBC client which can be set as the canonical client for the given rollapp. // Returns empty string if no such client is found. func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) (clientID string) { @@ -35,7 +39,7 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, continue } for _, bd := range stateInfo.GetBDs().BD { - height := ibcclienttypes.NewHeight(1, bd.GetHeight()) + height := ibcclienttypes.NewHeight(ibcRevisionNumber, bd.GetHeight()) consensusState, found := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, height) if !found { continue diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index e8c05729c..e43cbba15 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -50,7 +50,7 @@ func (hook rollappHook) AfterUpdateState( if !found { continue } - height := ibcclienttypes.NewHeight(1, bd.GetHeight()) + height := ibcclienttypes.NewHeight(ibcRevisionNumber, bd.GetHeight()) consensusState, _ := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index 388b82acb..157592f38 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -68,7 +68,7 @@ func TestAfterUpdateState(t *testing.T) { name: "BD does not include next block in state info", prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - blockSignerTmPubKey, err := k.GetSeqeuncerHash(ctx, keepertest.Alice) + blockSignerTmPubKey, err := k.GetSequencerHash(ctx, keepertest.Alice) require.NoError(t, err) k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKey) return testInput{ diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index b4d34cd09..0ebc3e8ee 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -37,8 +37,8 @@ func NewKeeper( return k } -// GetSeqeuncerHash returns the seqeuncer's tendermint public key hash -func (k Keeper) GetSeqeuncerHash(ctx sdk.Context, sequencerAddr string) ([]byte, error) { +// GetSequencerHash returns the seqeuncer's tendermint public key hash +func (k Keeper) GetSequencerHash(ctx sdk.Context, sequencerAddr string) ([]byte, error) { seq, found := k.sequencerKeeper.GetSequencer(ctx, sequencerAddr) if !found { return nil, fmt.Errorf("sequencer not found") diff --git a/x/lightclient/types/params.go b/x/lightclient/types/params.go index f125f219b..aed0edab5 100644 --- a/x/lightclient/types/params.go +++ b/x/lightclient/types/params.go @@ -4,10 +4,14 @@ import ( "time" "github.com/cometbft/cometbft/libs/math" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ics23 "github.com/cosmos/ics23/go" ) +// ExpectedCanonicalClientParams defines the expected parameters for a canonical IBC Tendermint client state +// The ChainID is not included as that varies for each rollapp +// The LatestHeight is not included as there is no condition on when a client can be registered as canonical var ExpectedCanonicalClientParams = ibctm.ClientState{ // Trust level is the fraction of the trusted validator set // that must sign over a new untrusted header before it is accepted. @@ -21,6 +25,9 @@ var ExpectedCanonicalClientParams = ibctm.ClientState{ // MaxClockDrift defines how much new (untrusted) header's Time // can drift into the future relative to our local clock. MaxClockDrift: time.Minute * 10, + // Frozen Height should be zero (default) as frozen clients cannot be canonical + // as they cannot receive state updates + FrozenHeight: ibcclienttypes.ZeroHeight(), // ProofSpecs defines the ICS-23 standard proof specifications used by // the light client. It is used configure a proof for either existence // or non-existence of a key value pair @@ -30,7 +37,8 @@ var ExpectedCanonicalClientParams = ibctm.ClientState{ }, AllowUpdateAfterExpiry: false, AllowUpdateAfterMisbehaviour: false, - UpgradePath: []string{}, + // For chains using Cosmos-SDK's default x/upgrade module, the upgrade path is as follows + UpgradePath: []string{"upgrade", "upgradedIBCState"}, } // IsCanonicalClientParamsValid checks if the given IBC tendermint client state has the expected canonical client parameters @@ -38,13 +46,16 @@ func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { if clientState.TrustLevel != ExpectedCanonicalClientParams.TrustLevel { return false } - if clientState.TrustingPeriod > ExpectedCanonicalClientParams.TrustingPeriod { + if clientState.TrustingPeriod != ExpectedCanonicalClientParams.TrustingPeriod { return false } - if clientState.UnbondingPeriod > ExpectedCanonicalClientParams.UnbondingPeriod { + if clientState.UnbondingPeriod != ExpectedCanonicalClientParams.UnbondingPeriod { return false } - if clientState.MaxClockDrift > ExpectedCanonicalClientParams.MaxClockDrift { + if clientState.MaxClockDrift != ExpectedCanonicalClientParams.MaxClockDrift { + return false + } + if clientState.FrozenHeight != ExpectedCanonicalClientParams.FrozenHeight { return false } if clientState.AllowUpdateAfterExpiry != ExpectedCanonicalClientParams.AllowUpdateAfterExpiry { @@ -58,5 +69,10 @@ func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { return false } } + for i, path := range clientState.UpgradePath { + if path != ExpectedCanonicalClientParams.UpgradePath[i] { + return false + } + } return true } diff --git a/x/lightclient/types/params_test.go b/x/lightclient/types/params_test.go new file mode 100644 index 000000000..38e31344d --- /dev/null +++ b/x/lightclient/types/params_test.go @@ -0,0 +1,117 @@ +package types_test + +import ( + "testing" + + "github.com/cometbft/cometbft/libs/math" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + ics23 "github.com/cosmos/ics23/go" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" +) + +func TestIsCanonicalClientParamsValid(t *testing.T) { + testCases := []struct { + name string + clientState func() ibctm.ClientState + valid bool + }{ + { + "valid client state", + func() ibctm.ClientState { + return types.ExpectedCanonicalClientParams + }, + true, + }, + { + "invalid trust level", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.TrustLevel = ibctm.NewFractionFromTm(math.Fraction{Numerator: 1, Denominator: 2}) + return clientState + }, + false, + }, + { + "invalid trusting period", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.TrustingPeriod = clientState.TrustingPeriod + 1 + return clientState + }, + false, + }, + { + "invalid unbonding period", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.UnbondingPeriod = clientState.UnbondingPeriod + 1 + return clientState + }, + false, + }, + { + "invalid max clock drift", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.MaxClockDrift = clientState.MaxClockDrift + 1 + return clientState + }, + false, + }, + { + "invalid frozen height", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.FrozenHeight = ibcclienttypes.NewHeight(1, 1) + return clientState + }, + false, + }, + { + "invalid allow update after expiry", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.AllowUpdateAfterExpiry = true + return clientState + }, + false, + }, + { + "invalid allow update after misbehaviour", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.AllowUpdateAfterMisbehaviour = true + return clientState + }, + false, + }, + { + "invalid proof specs", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.ProofSpecs = []*ics23.ProofSpec{ics23.SmtSpec} + return clientState + }, + false, + }, + { + "invalid upgrade path", + func() ibctm.ClientState { + clientState := types.ExpectedCanonicalClientParams + clientState.UpgradePath = []string{"custom", "upgrade"} + return clientState + }, + false, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + clientState := tc.clientState() + valid := types.IsCanonicalClientParamsValid(&clientState) + if valid != tc.valid { + t.Errorf("expected valid: %v, got: %v", tc.valid, valid) + } + }) + } +} diff --git a/x/sequencer/types/sequencer.go b/x/sequencer/types/sequencer.go index 619bb8188..9e6edffbf 100644 --- a/x/sequencer/types/sequencer.go +++ b/x/sequencer/types/sequencer.go @@ -39,13 +39,7 @@ func (seq Sequencer) IsNoticePeriodInProgress() bool { // GetDymintPubKeyHash returns the hash of the sequencer // as expected to be written on the rollapp ibc client headers func (seq Sequencer) GetDymintPubKeyHash() ([]byte, error) { - // load the dymint pubkey into a cryptotypes.PubKey - interfaceRegistry := cdctypes.NewInterfaceRegistry() - cryptocodec.RegisterInterfaces(interfaceRegistry) - protoCodec := codec.NewProtoCodec(interfaceRegistry) - - var pubKey cryptotypes.PubKey - err := protoCodec.UnpackAny(seq.DymintPubKey, &pubKey) + pubKey, err := seq.getCosmosPubKey() if err != nil { return nil, err } @@ -65,12 +59,7 @@ func (seq Sequencer) GetDymintPubKeyHash() ([]byte, error) { // GetCometPubKey returns the bytes of the sequencer's dymint pubkey func (seq Sequencer) GetCometPubKey() (tmprotocrypto.PublicKey, error) { - interfaceRegistry := cdctypes.NewInterfaceRegistry() - cryptocodec.RegisterInterfaces(interfaceRegistry) - protoCodec := codec.NewProtoCodec(interfaceRegistry) - - var pubKey cryptotypes.PubKey - err := protoCodec.UnpackAny(seq.DymintPubKey, &pubKey) + pubKey, err := seq.getCosmosPubKey() if err != nil { return tmprotocrypto.PublicKey{}, err } @@ -79,3 +68,13 @@ func (seq Sequencer) GetCometPubKey() (tmprotocrypto.PublicKey, error) { tmPubKey, err := cryptocodec.ToTmProtoPublicKey(pubKey) return tmPubKey, err } + +func (seq Sequencer) getCosmosPubKey() (cryptotypes.PubKey, error) { + interfaceRegistry := cdctypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(interfaceRegistry) + protoCodec := codec.NewProtoCodec(interfaceRegistry) + + var pubKey cryptotypes.PubKey + err := protoCodec.UnpackAny(seq.DymintPubKey, &pubKey) + return pubKey, err +} From 3abe951f8cb1c6577391db5b64f61c4eb97680bc Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:33:28 +0530 Subject: [PATCH 57/87] modify the state compatibility conditions to ignore current val hash and ignore timestamp condition if missing --- x/lightclient/ante/ibc_msg_update_client.go | 4 -- .../ante/ibc_msg_update_client_test.go | 3 +- x/lightclient/keeper/client.go | 2 - x/lightclient/keeper/hook_listener.go | 8 ---- x/lightclient/types/errors.go | 5 ++- x/lightclient/types/state.go | 38 +++++-------------- x/lightclient/types/state_test.go | 34 +++-------------- 7 files changed, 19 insertions(+), 75 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index c5579cc1f..172329146 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -46,7 +46,6 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli ibcState := types.IBCState{ Root: header.Header.GetAppHash(), - ValidatorsHash: header.Header.ValidatorsHash, NextValidatorsHash: header.Header.NextValidatorsHash, Timestamp: header.Header.Time, } @@ -55,13 +54,11 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli return err } rollappState := types.RollappState{ - BlockSequencer: sequencerPubKey, BlockDescriptor: bd, } // Check that BD for next block exists in same stateinfo if stateInfo.ContainsHeight(height.GetRevisionHeight() + 1) { rollappState.NextBlockSequencer = sequencerPubKey - rollappState.NextBlockDescriptor, _ = stateInfo.GetBlockDescriptor(height.GetRevisionHeight() + 1) } else { // next BD does not exist in this state info, check the next state info nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) @@ -71,7 +68,6 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli return err } rollappState.NextBlockSequencer = nextSequencerPk - rollappState.NextBlockDescriptor = nextStateInfo.GetBDs().BD[0] } else { // if next state info does not exist, then we can't verify the next block valhash. // Will accept the update optimistically diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index ae15a5c3f..ca28fc109 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -13,6 +13,7 @@ import ( keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" "github.com/dymensionxyz/dymension/v3/x/lightclient/ante" "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/stretchr/testify/require" ) @@ -192,7 +193,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { } }, assert: func(ctx sdk.Context, k keeper.Keeper, err error) { - require.ErrorIs(t, err, ibcclienttypes.ErrInvalidConsensus) + require.ErrorIs(t, err, types.ErrStateRootsMismatch) }, }, { diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/client.go index 8a42dce16..6f88e235a 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/client.go @@ -55,12 +55,10 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), } rollappState := types.RollappState{ - BlockSequencer: sequencerPk, BlockDescriptor: bd, } // Check if BD for next block exists in same stateinfo if stateInfo.ContainsHeight(bd.GetHeight() + 1) { - rollappState.NextBlockDescriptor, _ = stateInfo.GetBlockDescriptor(bd.GetHeight() + 1) rollappState.NextBlockSequencer = sequencerPk } err := types.CheckCompatibility(ibcState, rollappState) diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index e43cbba15..490c2fc3e 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -1,7 +1,6 @@ package keeper import ( - "errors" "time" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" @@ -59,7 +58,6 @@ func (hook rollappHook) AfterUpdateState( } ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), - ValidatorsHash: tmHeaderSigner, NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), } @@ -68,20 +66,14 @@ func (hook rollappHook) AfterUpdateState( return err } rollappState := types.RollappState{ - BlockSequencer: sequencerPk, BlockDescriptor: bd, } // check if bd for next block exists in same state info if i+1 < len(bds.GetBD()) { rollappState.NextBlockSequencer = sequencerPk - rollappState.NextBlockDescriptor = bds.GetBD()[i+1] } err = types.CheckCompatibility(ibcState, rollappState) if err != nil { - // The BD for (h+1) is missing, cannot verify if the nextvalhash matches - if errors.Is(err, types.ErrNextBlockDescriptorMissing) { - return err - } // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header diff --git a/x/lightclient/types/errors.go b/x/lightclient/types/errors.go index 9e7d9a6dd..f5d6fb055 100644 --- a/x/lightclient/types/errors.go +++ b/x/lightclient/types/errors.go @@ -6,6 +6,7 @@ import ( ) var ( - ErrTimestampNotFound = errorsmod.Wrap(gerrc.ErrNotFound, "block descriptors do not contain block timestamp") - ErrNextBlockDescriptorMissing = errorsmod.Wrap(gerrc.ErrNotFound, "next block descriptor is missing") + ErrStateRootsMismatch = errorsmod.Wrap(gerrc.ErrFailedPrecondition, "block descriptor state root does not match tendermint header app hash") + ErrValidatorHashMismatch = errorsmod.Wrap(gerrc.ErrFailedPrecondition, "next validator hash does not match the sequencer for h+1") + ErrTimestampMismatch = errorsmod.Wrap(gerrc.ErrFailedPrecondition, "block descriptor timestamp does not match tendermint header timestamp") ) diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index c851a6ac0..0e514b102 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -10,43 +10,29 @@ import ( tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" cmttypes "github.com/cometbft/cometbft/types" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) // CheckCompatibility checks if the IBC state and Rollapp state are compatible +// Compatibility Criteria: +// 1. The app root shared by the IBC consensus state matches the block descriptor state root for the same height +// 2. The next validator hash shared by the IBC consensus state matches the sequencer hash for the next block descriptor +// 3. The block descriptor timestamp matches the tendermint header timestamp func CheckCompatibility(ibcState IBCState, raState RollappState) error { // Check if block descriptor state root matches IBC block header app hash if !bytes.Equal(ibcState.Root, raState.BlockDescriptor.StateRoot) { - return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash") - } - // Check if the validator pubkey matches the sequencer pubkey - valHashFromStateInfo, err := GetValHashForSequencer(raState.BlockSequencer) - if err != nil { - return errors.Join(ibcclienttypes.ErrInvalidConsensus, err) - } - // The ValidatorsHash is only available when the block header is submitted (i.e only during MsgUpdateClient) - if len(ibcState.ValidatorsHash) > 0 && !bytes.Equal(ibcState.ValidatorsHash, valHashFromStateInfo) { - return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator does not match the sequencer") - } - if raState.NextBlockSequencer.Size() == 0 { - return ErrNextBlockDescriptorMissing + return errorsmod.Wrap(ErrStateRootsMismatch, "block descriptor state root does not match tendermint header app hash") } // Check if the nextValidatorHash matches for the sequencer for h+1 block descriptor nextValHashFromStateInfo, err := GetValHashForSequencer(raState.NextBlockSequencer) if err != nil { - return errors.Join(ibcclienttypes.ErrInvalidConsensus, err) + return errors.Join(ErrValidatorHashMismatch, err) } if !bytes.Equal(ibcState.NextValidatorsHash, nextValHashFromStateInfo) { - return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1") - } - // Check if block descriptor timestamp is not present - this happens if the rollapp has not upgraded yet - if raState.BlockDescriptor.Timestamp.IsZero() { - return ErrTimestampNotFound + return errorsmod.Wrap(ErrValidatorHashMismatch, "next validator hash does not match the sequencer for h+1") } - // Check if block descriptor timestamp matches IBC header timestamp - if !ibcState.Timestamp.Equal(raState.BlockDescriptor.Timestamp) { - return errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp") + if !raState.BlockDescriptor.Timestamp.IsZero() && !ibcState.Timestamp.Equal(raState.BlockDescriptor.Timestamp) { + return errorsmod.Wrap(ErrTimestampMismatch, "block descriptor timestamp does not match tendermint header timestamp") } return nil } @@ -69,8 +55,6 @@ func GetValHashForSequencer(sequencerTmPubKey tmprotocrypto.PublicKey) ([]byte, type IBCState struct { // Root is the app root shared by the IBC consensus state Root []byte - // ValidatorsHash is the tendermint pubkey of signer of the block header - ValidatorsHash []byte // NextValidatorsHash is the hash of the next validator set for the next block NextValidatorsHash []byte // Timestamp is the block timestamp of the header @@ -78,12 +62,8 @@ type IBCState struct { } type RollappState struct { - // BlockSequencer is the tendermint pubkey of the sequencer who submitted the block descriptor for the required height - BlockSequencer tmprotocrypto.PublicKey // BlockDescriptor is the block descriptor for the required height BlockDescriptor rollapptypes.BlockDescriptor // NextBlockSequencer is the tendermint pubkey of the sequencer who submitted the block descriptor for the next height (h+1) NextBlockSequencer tmprotocrypto.PublicKey - // NextBlockDescriptor is the block descriptor for the next height (h+1) - NextBlockDescriptor rollapptypes.BlockDescriptor } diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index 1ed7e23da..ab517464d 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -4,10 +4,8 @@ import ( "testing" "time" - errorsmod "cosmossdk.io/errors" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" "github.com/stretchr/testify/require" @@ -23,19 +21,13 @@ var ( Root: []byte("root"), Timestamp: timestamp, NextValidatorsHash: valHash, - ValidatorsHash: valHash, } validRollappState = types.RollappState{ - BlockSequencer: tmPk, BlockDescriptor: rollapptypes.BlockDescriptor{ StateRoot: []byte("root"), Timestamp: timestamp, }, NextBlockSequencer: tmPk, - NextBlockDescriptor: rollapptypes.BlockDescriptor{ - StateRoot: []byte("root2"), - Timestamp: timestamp, - }, } ) @@ -59,22 +51,7 @@ func TestCheckCompatibility(t *testing.T) { raState: invalidRootRaState, } }, - err: errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor state root does not match tendermint header app hash"), - }, - { - name: "validator who signed the block header is not the sequencer who submitted the block", - input: func() input { - newSequencer := ed25519.GenPrivKey().PubKey() - newtmPk, err := cryptocodec.ToTmProtoPublicKey(newSequencer) - require.NoError(t, err) - invalidValidatorHashRAState := validRollappState - invalidValidatorHashRAState.BlockSequencer = newtmPk - return input{ - ibcState: validIBCState, - raState: invalidValidatorHashRAState, - } - }, - err: errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "validator does not match the sequencer"), + err: types.ErrStateRootsMismatch, }, { name: "nextValidatorHash does not match the sequencer who submitted the next block descriptor", @@ -86,20 +63,19 @@ func TestCheckCompatibility(t *testing.T) { raState: validRollappState, } }, - err: errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "next validator hash does not match the sequencer for h+1"), + err: types.ErrValidatorHashMismatch, }, { - name: "timestamps is empty", + name: "timestamps is empty. ignore timestamp check", input: func() input { emptyTimestampRAState := validRollappState emptyTimestampRAState.BlockDescriptor.Timestamp = time.Time{} - emptyTimestampRAState.NextBlockDescriptor.Timestamp = time.Time{} return input{ ibcState: validIBCState, raState: emptyTimestampRAState, } }, - err: types.ErrTimestampNotFound, + err: nil, }, { name: "timestamps are not equal", @@ -111,7 +87,7 @@ func TestCheckCompatibility(t *testing.T) { raState: invalidTimestampRAState, } }, - err: errorsmod.Wrap(ibcclienttypes.ErrInvalidConsensus, "block descriptor timestamp does not match tendermint header timestamp"), + err: types.ErrTimestampMismatch, }, { name: "all fields are compatible", From ad38fdf7096704f5df77b294156f6967f05f00c1 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:39:34 +0530 Subject: [PATCH 58/87] implement the timestamp toggle behaviour + tests in rollapp keeper --- x/rollapp/keeper/msg_server_update_state.go | 17 ++-- .../keeper/msg_server_update_state_test.go | 25 +++++ x/rollapp/types/block_descriptor.go | 19 ++++ x/rollapp/types/block_descriptor_test.go | 97 +++++++++++++++++++ x/rollapp/types/errors.go | 1 + 5 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 x/rollapp/types/block_descriptor.go create mode 100644 x/rollapp/types/block_descriptor_test.go diff --git a/x/rollapp/keeper/msg_server_update_state.go b/x/rollapp/keeper/msg_server_update_state.go index 2d4ac3f78..737db74a2 100644 --- a/x/rollapp/keeper/msg_server_update_state.go +++ b/x/rollapp/keeper/msg_server_update_state.go @@ -2,12 +2,10 @@ package keeper import ( "context" - "errors" errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - lightclienttypes "github.com/dymensionxyz/dymension/v3/x/lightclient/types" "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) @@ -32,8 +30,6 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) // retrieve last updating index var newIndex, lastIndex uint64 latestStateInfoIndex, found := k.GetLatestStateInfoIndex(ctx, msg.RollappId) - // If this is the very first state update, assume the rollapp is upgraded - rollappUpgradedForTimestamp := !found if found { // retrieve last updating index stateInfo, found := k.GetStateInfo(ctx, msg.RollappId, latestStateInfoIndex.Index) @@ -46,8 +42,14 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) } // if previous block descriptor has timestamp, it means the rollapp is upgraded + // therefore all new BDs need to have timestamp lastBD := stateInfo.GetLatestBlockDescriptor() - rollappUpgradedForTimestamp = !lastBD.Timestamp.IsZero() + if !lastBD.Timestamp.IsZero() { + err := msg.BDs.Validate() + if err != nil { + return nil, err + } + } // check to see if received height is the one we expected expectedStartHeight := stateInfo.StartHeight + stateInfo.NumBlocks @@ -75,10 +77,7 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) err = k.hooks.AfterUpdateState(ctx, msg.RollappId, stateInfo) if err != nil { - // Upgraded rollapps must have timestamps in BDs - if !(errors.Is(err, lightclienttypes.ErrTimestampNotFound) && rollappUpgradedForTimestamp) { - return nil, err - } + return nil, err } stateInfoIndex := stateInfo.GetIndex() diff --git a/x/rollapp/keeper/msg_server_update_state_test.go b/x/rollapp/keeper/msg_server_update_state_test.go index c2448b0db..078505d2a 100644 --- a/x/rollapp/keeper/msg_server_update_state_test.go +++ b/x/rollapp/keeper/msg_server_update_state_test.go @@ -1,6 +1,8 @@ package keeper_test import ( + "time" + abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" @@ -231,6 +233,29 @@ func (suite *RollappTestSuite) TestUpdateStateErrNotActiveSequencer() { suite.ErrorIs(err, sequencertypes.ErrNotActiveSequencer) } +func (suite *RollappTestSuite) TestUpdateStateDowngradeTimestamp() { + rollappId, proposer := suite.CreateDefaultRollappAndProposer() + // update state without timestamp + _, err := suite.PostStateUpdate(suite.Ctx, rollappId, proposer, 1, uint64(3)) + suite.NoError(err) + + // update state with timestamp - this "upgrades" the rollapp such that all new state updates must have timestamp in BD + updateState := types.MsgUpdateState{ + Creator: proposer, + RollappId: rollappId, + StartHeight: 4, + NumBlocks: 1, + DAPath: "", + BDs: types.BlockDescriptors{BD: []types.BlockDescriptor{{Height: 4, Timestamp: time.Now().UTC()}}}, + } + _, err = suite.msgServer.UpdateState(suite.Ctx, &updateState) + suite.NoError(err) + + // update state without timestamp + _, err = suite.PostStateUpdate(suite.Ctx, rollappId, proposer, 5, uint64(1)) + suite.ErrorIs(err, types.ErrInvalidBlockDescriptorTimestamp) +} + // --------------------------------------- // verifyAll receives a list of expected results and a map of rollapId->rollapp // the function verifies that the map contains all the rollapps that are in the list and only them diff --git a/x/rollapp/types/block_descriptor.go b/x/rollapp/types/block_descriptor.go new file mode 100644 index 000000000..a9c19ec65 --- /dev/null +++ b/x/rollapp/types/block_descriptor.go @@ -0,0 +1,19 @@ +package types + +import errorsmod "cosmossdk.io/errors" + +func (bds BlockDescriptors) Validate() error { + for _, bd := range bds.BD { + if err := bd.Validate(); err != nil { + return err + } + } + return nil +} + +func (bd BlockDescriptor) Validate() error { + if bd.Timestamp.IsZero() { + return errorsmod.Wrapf(ErrInvalidBlockDescriptorTimestamp, "timestamp is empty for block descriptor at height %d", bd.Height) + } + return nil +} diff --git a/x/rollapp/types/block_descriptor_test.go b/x/rollapp/types/block_descriptor_test.go new file mode 100644 index 000000000..a8184cff0 --- /dev/null +++ b/x/rollapp/types/block_descriptor_test.go @@ -0,0 +1,97 @@ +package types_test + +import ( + "testing" + time "time" + + "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/stretchr/testify/require" +) + +func TestBlockDescriptorsValidate(t *testing.T) { + testCases := []struct { + name string + bds types.BlockDescriptors + expPass bool + }{ + { + name: "valid block descriptors", + bds: types.BlockDescriptors{ + BD: []types.BlockDescriptor{ + { + Height: 1, + Timestamp: time.Now(), + }, + { + Height: 2, + Timestamp: time.Now(), + }, + }, + }, + expPass: true, + }, + { + name: "invalid block descriptor", + bds: types.BlockDescriptors{ + BD: []types.BlockDescriptor{ + { + Height: 1, + Timestamp: time.Now(), + }, + { + Height: 2, + }, + }, + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.bds.Validate() + if tc.expPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} + +func TestBlockDescriptorValidate(t *testing.T) { + testCases := []struct { + name string + bd types.BlockDescriptor + expPass bool + }{ + { + name: "valid block descriptor", + bd: types.BlockDescriptor{ + Height: 1, + Timestamp: time.Now(), + }, + expPass: true, + }, + { + name: "invalid block descriptor", + bd: types.BlockDescriptor{ + Height: 1, + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + err := tc.bd.Validate() + if tc.expPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/x/rollapp/types/errors.go b/x/rollapp/types/errors.go index ef26ee887..2805b79cc 100644 --- a/x/rollapp/types/errors.go +++ b/x/rollapp/types/errors.go @@ -40,6 +40,7 @@ var ( ErrSameOwner = errorsmod.Wrap(gerrc.ErrInvalidArgument, "same owner") ErrInvalidRequest = errorsmod.Wrap(gerrc.ErrInvalidArgument, "invalid request") ErrInvalidVMType = errorsmod.Wrap(gerrc.ErrInvalidArgument, "invalid vm type") + ErrInvalidBlockDescriptorTimestamp = errorsmod.Wrap(gerrc.ErrInvalidArgument, "invalid block descriptor timestamp") /* ------------------------------ fraud related ----------------------------- */ ErrDisputeAlreadyFinalized = errorsmod.Register(ModuleName, 2000, "disputed height already finalized") From 0b761a39b8952a5987cc388d1553ca895354c18c Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:41:17 +0530 Subject: [PATCH 59/87] adding detailed comment on state compatibility criteria --- x/lightclient/types/state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 0e514b102..8e167f82f 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -17,7 +17,7 @@ import ( // Compatibility Criteria: // 1. The app root shared by the IBC consensus state matches the block descriptor state root for the same height // 2. The next validator hash shared by the IBC consensus state matches the sequencer hash for the next block descriptor -// 3. The block descriptor timestamp matches the tendermint header timestamp +// 3. The block descriptor timestamp matches the tendermint header timestamp (only if timestamp exists for the block descriptor) func CheckCompatibility(ibcState IBCState, raState RollappState) error { // Check if block descriptor state root matches IBC block header app hash if !bytes.Equal(ibcState.Root, raState.BlockDescriptor.StateRoot) { From e4a10898445d85e7ea2fc09fc91c5d7f1e79c15b Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:43:17 +0530 Subject: [PATCH 60/87] calling rollappkeeper.HandleFraud instead of sequencerkeeper.JailSequencerOnFraud --- testutil/keeper/lightclient.go | 34 +++++++++++++++++++++---- x/lightclient/ante/ibc_msgs_test.go | 4 +++ x/lightclient/keeper/hook_listener.go | 2 +- x/lightclient/types/expected_keepers.go | 2 +- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 822321794..18a87d971 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -16,6 +16,7 @@ import ( ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "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" sequencertypes "github.com/dymensionxyz/dymension/v3/x/sequencer/types" cometbftdb "github.com/cometbft/cometbft-db" @@ -79,12 +80,13 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { mockIBCKeeper := NewMockIBCClientKeeper(testConsensusStates, testGenesisClients) mockSequencerKeeper := NewMockSequencerKeeper(testSequencers) + mockRollappKeeper := NewMockRollappKeeper() k := keeper.NewKeeper( cdc, storeKey, mockIBCKeeper, mockSequencerKeeper, - nil, + mockRollappKeeper, ) ctx := sdk.NewContext(stateStore, cometbftproto.Header{}, false, log.NewNopLogger()) @@ -127,11 +129,33 @@ func NewMockSequencerKeeper(sequencers map[string]sequencertypes.Sequencer) *Moc } } -func (m *MockSequencerKeeper) JailSequencerOnFraud(ctx sdk.Context, seqAddr string) error { - return nil -} - func (m *MockSequencerKeeper) GetSequencer(ctx sdk.Context, seqAddr string) (sequencertypes.Sequencer, bool) { seq, ok := m.sequencers[seqAddr] return seq, ok } + +type MockRollappKeeper struct { +} + +func NewMockRollappKeeper() *MockRollappKeeper { + return &MockRollappKeeper{} +} + +func (m *MockRollappKeeper) GetRollapp(ctx sdk.Context, rollappId string) (val rollapptypes.Rollapp, found bool) { + return rollapptypes.Rollapp{}, false +} + +func (m *MockRollappKeeper) FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (*rollapptypes.StateInfo, error) { + return nil, nil +} + +func (m *MockRollappKeeper) GetStateInfo(ctx sdk.Context, rollappId string, index uint64) (val rollapptypes.StateInfo, found bool) { + return rollapptypes.StateInfo{}, false +} + +func (m *MockRollappKeeper) SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rollapp) { +} + +func (m *MockRollappKeeper) HandleFraud(ctx sdk.Context, rollappID, clientId string, fraudHeight uint64, seqAddr string) error { + return nil +} diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index adf93b20c..1c5416a98 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -51,6 +51,10 @@ func (m *MockRollappKeeper) GetStateInfo(ctx sdk.Context, rollappId string, inde return val, found } +func (m *MockRollappKeeper) HandleFraud(ctx sdk.Context, rollappID, clientId string, fraudHeight uint64, seqAddr string) error { + return nil +} + type MockIBCClientKeeper struct { clientStates map[string]exported.ClientState } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 490c2fc3e..034985b82 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -81,7 +81,7 @@ func (hook rollappHook) AfterUpdateState( if err != nil { return err } - err = hook.k.sequencerKeeper.JailSequencerOnFraud(ctx, sequencerAddr) + err = hook.k.rollappKeeper.HandleFraud(ctx, rollappId, canonicalClient, bd.GetHeight(), sequencerAddr) if err != nil { return err } diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 1cfaaeccf..72bef9864 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -10,7 +10,6 @@ import ( ) type SequencerKeeperExpected interface { - JailSequencerOnFraud(ctx sdk.Context, sequencerAddress string) error GetSequencer(ctx sdk.Context, sequencerAddress string) (val sequencertypes.Sequencer, found bool) } @@ -19,6 +18,7 @@ type RollappKeeperExpected interface { FindStateInfoByHeight(ctx sdk.Context, rollappId string, height uint64) (*rollapptypes.StateInfo, error) GetStateInfo(ctx sdk.Context, rollappId string, index uint64) (val rollapptypes.StateInfo, found bool) SetRollapp(ctx sdk.Context, rollapp rollapptypes.Rollapp) + HandleFraud(ctx sdk.Context, rollappID, clientId string, fraudHeight uint64, seqAddr string) error } type IBCClientKeeperExpected interface { From 8cda6be8b0503ddeaa42ac646a0ebe7be4868203 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:53:22 +0530 Subject: [PATCH 61/87] in msgupdateclient, run FindStateInfoByHeight twice instead of micro optimization --- x/lightclient/ante/ibc_msg_update_client.go | 43 +++++++------------ .../ante/ibc_msg_update_client_test.go | 16 ++++++- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 172329146..298a60b1f 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -31,6 +31,12 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli if !ok { return nil } + ibcState := types.IBCState{ + Root: header.Header.GetAppHash(), + NextValidatorsHash: header.Header.NextValidatorsHash, + Timestamp: header.Header.Time, + } + // Check if there are existing block descriptors for the given height of client state height := header.TrustedHeight stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) @@ -44,39 +50,22 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli } bd, _ := stateInfo.GetBlockDescriptor(height.GetRevisionHeight()) - ibcState := types.IBCState{ - Root: header.Header.GetAppHash(), - NextValidatorsHash: header.Header.NextValidatorsHash, - Timestamp: header.Header.Time, + stateInfo, err = i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()+1) + if err != nil { + // No BDs found for next height. + // Will accept the update optimistically + // But also save the blockProposer address with the height for future verification + blockProposer := header.Header.ProposerAddress + i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) + return nil } sequencerPubKey, err := i.lightClientKeeper.GetSequencerPubKey(ctx, stateInfo.Sequencer) if err != nil { return err } rollappState := types.RollappState{ - BlockDescriptor: bd, - } - // Check that BD for next block exists in same stateinfo - if stateInfo.ContainsHeight(height.GetRevisionHeight() + 1) { - rollappState.NextBlockSequencer = sequencerPubKey - } else { - // next BD does not exist in this state info, check the next state info - nextStateInfo, found := i.rollappKeeper.GetStateInfo(ctx, rollappID, stateInfo.GetIndex().Index+1) - if found { - nextSequencerPk, err := i.lightClientKeeper.GetSequencerPubKey(ctx, nextStateInfo.Sequencer) - if err != nil { - return err - } - rollappState.NextBlockSequencer = nextSequencerPk - } else { - // if next state info does not exist, then we can't verify the next block valhash. - // Will accept the update optimistically - // But also save the blockProposer address with the height for future verification - // When the corresponding state info is submitted by the sequencer, will perform the verification - blockProposer := header.Header.ProposerAddress - i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) - return nil - } + BlockDescriptor: bd, + NextBlockSequencer: sequencerPubKey, } // Ensure that the ibc header is compatible with the existing rollapp state // If it's not, we error and prevent the MsgUpdateClient from being processed diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index ca28fc109..90e685218 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -172,7 +172,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { Index: 1, }, StartHeight: 1, - NumBlocks: 2, + NumBlocks: 1, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { @@ -180,9 +180,21 @@ func TestHandleMsgUpdateClient(t *testing.T) { StateRoot: []byte{}, Timestamp: time.Unix(1724392989, 0), }, + }, + }, + }, + 2: { + Sequencer: keepertest.Alice, + StateInfoIndex: rollapptypes.StateInfoIndex{ + Index: 2, + }, + StartHeight: 2, + NumBlocks: 1, + BDs: rollapptypes.BlockDescriptors{ + BD: []rollapptypes.BlockDescriptor{ { Height: 2, - StateRoot: []byte{}, + StateRoot: []byte("appHash2"), Timestamp: time.Unix(1724392989, 0), }, }, From 0c512ff9837f0d7689a258171fb30b0ce51c773b Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:57:02 +0530 Subject: [PATCH 62/87] smol cleanup --- x/lightclient/ante/ibc_msg_update_client.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 298a60b1f..bc2555c71 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -44,8 +44,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // No BDs found for given height. // Will accept the update optimistically // But also save the blockProposer address with the height for future verification - blockProposer := header.Header.ProposerAddress - i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) + i.acceptUpdateOptimistically(ctx, rollappID, msg.ClientId, header) return nil } bd, _ := stateInfo.GetBlockDescriptor(height.GetRevisionHeight()) @@ -55,8 +54,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // No BDs found for next height. // Will accept the update optimistically // But also save the blockProposer address with the height for future verification - blockProposer := header.Header.ProposerAddress - i.lightClientKeeper.SetConsensusStateSigner(ctx, msg.ClientId, height.GetRevisionHeight(), blockProposer) + i.acceptUpdateOptimistically(ctx, rollappID, msg.ClientId, header) return nil } sequencerPubKey, err := i.lightClientKeeper.GetSequencerPubKey(ctx, stateInfo.Sequencer) @@ -76,3 +74,8 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli return nil } + +func (i IBCMessagesDecorator) acceptUpdateOptimistically(ctx sdk.Context, rollappID string, clientID string, header *ibctm.Header) { + proposerAddress := header.Header.ProposerAddress + i.lightClientKeeper.SetConsensusStateSigner(ctx, clientID, header.TrustedHeight.RevisionHeight, proposerAddress) +} From 4a1c63d1080a1e3ee2b979570547cf4b7097a4cb Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:08:54 +0530 Subject: [PATCH 63/87] adding docstring GetProspectiveCanonicalClient --- .../keeper/{client.go => canonical_client.go} | 36 +++++++++++++++++-- x/lightclient/keeper/hook_listener.go | 4 +-- x/lightclient/keeper/keeper.go | 28 --------------- 3 files changed, 36 insertions(+), 32 deletions(-) rename x/lightclient/keeper/{client.go => canonical_client.go} (65%) diff --git a/x/lightclient/keeper/client.go b/x/lightclient/keeper/canonical_client.go similarity index 65% rename from x/lightclient/keeper/client.go rename to x/lightclient/keeper/canonical_client.go index 6f88e235a..762c764d7 100644 --- a/x/lightclient/keeper/client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -15,8 +15,11 @@ const ( ) // GetProspectiveCanonicalClient returns the client id of the first IBC client which can be set as the canonical client for the given rollapp. -// Returns empty string if no such client is found. -func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) (clientID string) { +// 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 block descriptors in the state info must be compatible with the client consensus state +func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) (clientID string, foundClient bool) { clients := k.ibcClientKeeper.GetAllGenesisClients(ctx) for _, client := range clients { clientState, err := ibcclienttypes.UnpackClientState(client.ClientState) @@ -66,8 +69,37 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, continue } clientID = client.GetClientId() + foundClient = true return } } return } + +func (k Keeper) GetCanonicalClient(ctx sdk.Context, rollappId string) (string, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetRollappClientKey(rollappId)) + if bz == nil { + return "", false + } + return string(bz), true +} + +func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.GetRollappClientKey(rollappId), []byte(clientID)) + store.Set(types.CanonicalClientKey(clientID), []byte(rollappId)) +} + +func (k Keeper) GetAllCanonicalClients(ctx sdk.Context) (clients []types.CanonicalClient) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.RollappClientKey) + defer iterator.Close() // nolint: errcheck + for ; iterator.Valid(); iterator.Next() { + clients = append(clients, types.CanonicalClient{ + RollappId: string(iterator.Key()[1:]), + IbcClientId: string(iterator.Value()), + }) + } + return +} diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 034985b82..2fbaa16de 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -36,8 +36,8 @@ func (hook rollappHook) AfterUpdateState( ) error { canonicalClient, found := hook.k.GetCanonicalClient(ctx, rollappId) if !found { - canonicalClient = hook.k.GetProspectiveCanonicalClient(ctx, rollappId, stateInfo) - if canonicalClient != "" { + canonicalClient, foundClient := hook.k.GetProspectiveCanonicalClient(ctx, rollappId, stateInfo) + if foundClient { hook.k.SetCanonicalClient(ctx, rollappId, canonicalClient) } return nil diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 0ebc3e8ee..e7e3a9855 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -58,34 +58,6 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } -func (k Keeper) GetCanonicalClient(ctx sdk.Context, rollappId string) (string, bool) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.GetRollappClientKey(rollappId)) - if bz == nil { - return "", false - } - return string(bz), true -} - -func (k Keeper) SetCanonicalClient(ctx sdk.Context, rollappId string, clientID string) { - store := ctx.KVStore(k.storeKey) - store.Set(types.GetRollappClientKey(rollappId), []byte(clientID)) - store.Set(types.CanonicalClientKey(clientID), []byte(rollappId)) -} - -func (k Keeper) GetAllCanonicalClients(ctx sdk.Context) (clients []types.CanonicalClient) { - store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.RollappClientKey) - defer iterator.Close() // nolint: errcheck - for ; iterator.Valid(); iterator.Next() { - clients = append(clients, types.CanonicalClient{ - RollappId: string(iterator.Key()[1:]), - IbcClientId: string(iterator.Value()), - }) - } - return -} - func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencer []byte) { store := ctx.KVStore(k.storeKey) store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), sequencer) From 47f15209c5173d3b0f478fc7f4291584b9353b92 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:35:19 +0530 Subject: [PATCH 64/87] modifying the afterstateupdate hook to compare at h-1 --- x/lightclient/keeper/hook_listener.go | 82 ++++++++++++++-------- x/lightclient/keeper/hook_listener_test.go | 52 +++----------- 2 files changed, 60 insertions(+), 74 deletions(-) diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 2fbaa16de..552f7fe1f 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -42,49 +42,69 @@ func (hook rollappHook) AfterUpdateState( } return nil } - bds := stateInfo.GetBDs() - for i, bd := range bds.GetBD() { + sequencerPk, err := hook.k.GetSequencerPubKey(ctx, stateInfo.Sequencer) + if err != nil { + return err + } + latestHeight := stateInfo.GetLatestHeight() + // We check from latestHeight-1 downwards, as the nextValHash for latestHeight will not be available until next stateupdate + for h := latestHeight - 1; h >= stateInfo.StartHeight; h-- { + bd, _ := stateInfo.GetBlockDescriptor(h) // Check if any optimistic updates were made for the given height tmHeaderSigner, found := hook.k.GetConsensusStateSigner(ctx, canonicalClient, bd.GetHeight()) if !found { continue } - height := ibcclienttypes.NewHeight(ibcRevisionNumber, bd.GetHeight()) - consensusState, _ := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) - // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash - tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) - if !ok { - return nil - } - ibcState := types.IBCState{ - Root: tmConsensusState.GetRoot().GetHash(), - NextValidatorsHash: tmConsensusState.NextValidatorsHash, - Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), + err := hook.checkStateForHeight(ctx, rollappId, bd, canonicalClient, sequencerPk, tmHeaderSigner) + if err != nil { + return err } - sequencerPk, err := hook.k.GetSequencerPubKey(ctx, stateInfo.Sequencer) + } + // Check for the last BD from the previous stateInfo as now we have the nextValhash available for that block + tmHeaderSigner, found := hook.k.GetConsensusStateSigner(ctx, canonicalClient, stateInfo.StartHeight-1) + if found { + previousStateInfo, err := hook.k.rollappKeeper.FindStateInfoByHeight(ctx, rollappId, stateInfo.StartHeight-1) if err != nil { return err } - rollappState := types.RollappState{ - BlockDescriptor: bd, + bd, _ := previousStateInfo.GetBlockDescriptor(stateInfo.StartHeight - 1) + err = hook.checkStateForHeight(ctx, rollappId, bd, canonicalClient, sequencerPk, tmHeaderSigner) + if err != nil { + return err } - // check if bd for next block exists in same state info - if i+1 < len(bds.GetBD()) { - rollappState.NextBlockSequencer = sequencerPk + } + return nil +} + +func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, bd rollapptypes.BlockDescriptor, canonicalClient string, sequencerPk tmprotocrypto.PublicKey, tmHeaderSigner []byte) error { + height := ibcclienttypes.NewHeight(ibcRevisionNumber, bd.GetHeight()) + consensusState, _ := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) + // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash + tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) + if !ok { + return nil + } + ibcState := types.IBCState{ + Root: tmConsensusState.GetRoot().GetHash(), + NextValidatorsHash: tmConsensusState.NextValidatorsHash, + Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), + } + rollappState := types.RollappState{ + BlockDescriptor: bd, + NextBlockSequencer: sequencerPk, + } + err := types.CheckCompatibility(ibcState, rollappState) + if err != nil { + // If the state is not compatible, + // Take this state update as source of truth over the IBC update + // Punish the block proposer of the IBC signed header + sequencerAddr, err := getAddress(tmHeaderSigner) + if err != nil { + return err } - err = types.CheckCompatibility(ibcState, rollappState) + err = hook.k.rollappKeeper.HandleFraud(ctx, rollappId, canonicalClient, bd.GetHeight(), sequencerAddr) if err != nil { - // If the state is not compatible, - // Take this state update as source of truth over the IBC update - // Punish the block proposer of the IBC signed header - sequencerAddr, err := getAddress(tmHeaderSigner) - if err != nil { - return err - } - err = hook.k.rollappKeeper.HandleFraud(ctx, rollappId, canonicalClient, bd.GetHeight(), sequencerAddr) - if err != nil { - return err - } + return err } } return nil diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index 157592f38..441b5a89e 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -32,17 +32,6 @@ func TestAfterUpdateState(t *testing.T) { }, expectErr: false, }, - { - name: "canonical client exists but the BDs are empty", - prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { - k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - return testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{}, - } - }, - expectErr: false, - }, { name: "canonical client exists but consensus state is not found for given height", prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { @@ -50,6 +39,9 @@ func TestAfterUpdateState(t *testing.T) { return testInput{ rollappId: "rollapp-has-canon-client-but-no-state", stateInfo: &rollapptypes.StateInfo{ + Sequencer: keepertest.Alice, + StartHeight: 1, + NumBlocks: 1, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { @@ -64,36 +56,6 @@ func TestAfterUpdateState(t *testing.T) { }, expectErr: false, }, - { - name: "BD does not include next block in state info", - prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { - k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - blockSignerTmPubKey, err := k.GetSequencerHash(ctx, keepertest.Alice) - require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKey) - return testInput{ - rollappId: "rollapp-has-canon-client", - stateInfo: &rollapptypes.StateInfo{ - Sequencer: keepertest.Alice, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("test"), - Timestamp: time.Unix(1724392989, 0), - }, - { - Height: 2, - StateRoot: []byte("test2"), - Timestamp: time.Unix(1724392989, 0).Add(1), - }, - }, - }, - }, - } - }, - expectErr: true, - }, { name: "both states are not compatible - slash the sequencer who signed", prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { @@ -106,7 +68,9 @@ func TestAfterUpdateState(t *testing.T) { return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ - Sequencer: keepertest.Alice, + Sequencer: keepertest.Alice, + StartHeight: 1, + NumBlocks: 3, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { @@ -143,7 +107,9 @@ func TestAfterUpdateState(t *testing.T) { return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ - Sequencer: keepertest.Alice, + Sequencer: keepertest.Alice, + StartHeight: 1, + NumBlocks: 3, BDs: rollapptypes.BlockDescriptors{ BD: []rollapptypes.BlockDescriptor{ { From e10cd20fff1b456ca4172ad823e672eb5349e7c6 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 14:51:48 +0530 Subject: [PATCH 65/87] adding TestFindStateInfoByHeight --- .../keeper/grpc_query_state_info_test.go | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/x/rollapp/keeper/grpc_query_state_info_test.go b/x/rollapp/keeper/grpc_query_state_info_test.go index 02f4aee9d..f964047fc 100644 --- a/x/rollapp/keeper/grpc_query_state_info_test.go +++ b/x/rollapp/keeper/grpc_query_state_info_test.go @@ -12,6 +12,7 @@ import ( keepertest "github.com/dymensionxyz/dymension/v3/testutil/keeper" "github.com/dymensionxyz/dymension/v3/testutil/nullify" "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "github.com/dymensionxyz/sdk-utils/utils/urand" ) // Prevent strconv unused error @@ -78,3 +79,103 @@ func TestStateInfoQuerySingle(t *testing.T) { }) } } + +func TestFindStateInfoByHeight(t *testing.T) { + keeper, ctx := keepertest.RollappKeeper(t) + rollappID := urand.RollappID() + keeper.SetRollapp(ctx, types.Rollapp{ + RollappId: rollappID, + }) + keeper.SetStateInfo(ctx, types.StateInfo{ + StateInfoIndex: types.StateInfoIndex{RollappId: rollappID, Index: 1}, + StartHeight: 1, + NumBlocks: 2, + }) + keeper.SetStateInfo(ctx, types.StateInfo{ + StateInfoIndex: types.StateInfoIndex{RollappId: rollappID, Index: 2}, + StartHeight: 3, + NumBlocks: 3, + }) + keeper.SetStateInfo(ctx, types.StateInfo{ + StateInfoIndex: types.StateInfoIndex{RollappId: rollappID, Index: 3}, + StartHeight: 6, + NumBlocks: 4, + }) + keeper.SetLatestStateInfoIndex(ctx, types.StateInfoIndex{ + RollappId: rollappID, + Index: 3, + }) + + type testInput struct { + rollappId string + height uint64 + } + + testCase := []struct { + name string + input testInput + stateInfoIndex uint64 + err error + }{ + { + name: "Zero height", + input: testInput{ + rollappId: "1", + height: 0, + }, + err: types.ErrInvalidHeight, + }, + { + name: "Rollapp not found", + input: testInput{ + rollappId: "unknown", + height: 1, + }, + err: types.ErrUnknownRollappID, + }, + { + name: "First height", + input: testInput{ + rollappId: rollappID, + height: 1, + }, + stateInfoIndex: 1, + }, + { + name: "Last height", + input: testInput{ + rollappId: rollappID, + height: 9, + }, + stateInfoIndex: 3, + }, + { + name: "Height in between", + input: testInput{ + rollappId: rollappID, + height: 4, + }, + stateInfoIndex: 2, + }, + { + name: "Height not found", + input: testInput{ + rollappId: rollappID, + height: 10, + }, + err: types.ErrStateNotExists, + }, + } + for _, tc := range testCase { + t.Run(tc.name, func(t *testing.T) { + response, err := keeper.FindStateInfoByHeight(ctx, tc.input.rollappId, tc.input.height) + if tc.err != nil { + require.ErrorIs(t, err, tc.err) + } else { + require.NoError(t, err) + require.Equal(t, tc.stateInfoIndex, response.StateInfoIndex.Index) + } + }) + } + +} From 9fa4d97d1b32d44812374427bb5bfaf3689774fa Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:24:22 +0530 Subject: [PATCH 66/87] update GetProspectiveCanonicalClient to fetch all existing ibc consensus states and compare with BDs --- testutil/keeper/lightclient.go | 15 +++++- x/lightclient/ante/ibc_msgs_test.go | 6 +++ x/lightclient/keeper/canonical_client.go | 58 +++++++++++++++--------- x/lightclient/types/expected_keepers.go | 3 ++ 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 18a87d971..178ca4b30 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -1,6 +1,7 @@ package keeper import ( + "context" "testing" "time" @@ -99,7 +100,10 @@ type MockIBCCLientKeeper struct { genesisClients ibcclienttypes.IdentifiedClientStates } -func NewMockIBCClientKeeper(clientCS map[string]map[uint64]exported.ConsensusState, genesisClients ibcclienttypes.IdentifiedClientStates) *MockIBCCLientKeeper { +func NewMockIBCClientKeeper( + clientCS map[string]map[uint64]exported.ConsensusState, + genesisClients ibcclienttypes.IdentifiedClientStates, +) *MockIBCCLientKeeper { return &MockIBCCLientKeeper{ clientConsensusState: clientCS, genesisClients: genesisClients, @@ -119,6 +123,15 @@ func (m *MockIBCCLientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttyp return m.genesisClients } +func (m *MockIBCCLientKeeper) ConsensusStateHeights(c context.Context, req *ibcclienttypes.QueryConsensusStateHeightsRequest) (*ibcclienttypes.QueryConsensusStateHeightsResponse, error) { + heights := []ibcclienttypes.Height{ + ibcclienttypes.NewHeight(1, 2), + } + return &ibcclienttypes.QueryConsensusStateHeightsResponse{ + ConsensusStateHeights: heights, + }, nil +} + type MockSequencerKeeper struct { sequencers map[string]sequencertypes.Sequencer } diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index 1c5416a98..bf913042b 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -1,6 +1,8 @@ package ante_test import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -78,6 +80,10 @@ func (m *MockIBCClientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttyp return nil } +func (m *MockIBCClientKeeper) ConsensusStateHeights(c context.Context, req *ibcclienttypes.QueryConsensusStateHeightsRequest) (*ibcclienttypes.QueryConsensusStateHeightsResponse, error) { + return nil, nil +} + type MockIBCChannelKeeper struct { channelConnections map[string]ibcconnectiontypes.ConnectionEnd } diff --git a/x/lightclient/keeper/canonical_client.go b/x/lightclient/keeper/canonical_client.go index 762c764d7..c1b17e8ec 100644 --- a/x/lightclient/keeper/canonical_client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -4,6 +4,7 @@ import ( "time" 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" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" @@ -18,8 +19,8 @@ const ( // 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 block descriptors in the state info must be compatible with the client consensus state -func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) (clientID string, foundClient bool) { +// 3. All the existing consensus states much match the corresponding height rollapp block descriptors +func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, stateInfo *rollapptypes.StateInfo) (clientID string, stateCompatible bool) { clients := k.ibcClientKeeper.GetAllGenesisClients(ctx) for _, client := range clients { clientState, err := ibcclienttypes.UnpackClientState(client.ClientState) @@ -41,36 +42,49 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, if err != nil { continue } - for _, bd := range stateInfo.GetBDs().BD { - height := ibcclienttypes.NewHeight(ibcRevisionNumber, bd.GetHeight()) - consensusState, found := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, height) - if !found { - continue - } - // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash - tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) - if !ok { - continue - } + res, err := k.ibcClientKeeper.ConsensusStateHeights(ctx, &ibcclienttypes.QueryConsensusStateHeightsRequest{ + ClientId: client.GetClientId(), + Pagination: &query.PageRequest{Limit: stateInfo.GetLatestHeight()}, + }) + stateCompatible = true + for _, consensusHeight := range res.ConsensusStateHeights { + consensusState, _ := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, consensusHeight) + tmConsensusState, _ := consensusState.(*ibctm.ConsensusState) ibcState := types.IBCState{ Root: tmConsensusState.GetRoot().GetHash(), NextValidatorsHash: tmConsensusState.NextValidatorsHash, Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), } - rollappState := types.RollappState{ - BlockDescriptor: bd, - } - // Check if BD for next block exists in same stateinfo - if stateInfo.ContainsHeight(bd.GetHeight() + 1) { + + var rollappState types.RollappState + bd, found := stateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) + if found { + rollappState.BlockDescriptor = bd rollappState.NextBlockSequencer = sequencerPk + } else { + // Look up the state info for the block descriptor + oldStateInfo, err := k.rollappKeeper.FindStateInfoByHeight(ctx, rollappId, consensusHeight.GetRevisionHeight()) + if err != nil { + stateCompatible = false + break + } + bd, _ = oldStateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) + oldSequencerPk, err := k.GetSequencerPubKey(ctx, oldStateInfo.Sequencer) + if err != nil { + stateCompatible = false + break + } + rollappState.BlockDescriptor = bd + rollappState.NextBlockSequencer = oldSequencerPk } err := types.CheckCompatibility(ibcState, rollappState) if err != nil { - continue + stateCompatible = false + break } - clientID = client.GetClientId() - foundClient = true - return + } + if stateCompatible { + return client.GetClientId(), true } } return diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 72bef9864..7ad28fa84 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -1,6 +1,8 @@ package types import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -25,6 +27,7 @@ type IBCClientKeeperExpected interface { GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates + ConsensusStateHeights(c context.Context, req *ibcclienttypes.QueryConsensusStateHeightsRequest) (*ibcclienttypes.QueryConsensusStateHeightsResponse, error) } type IBCChannelKeeperExpected interface { From 5424b09ffedf43bd2ce43711fb815c9a8890519f Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:25:30 +0530 Subject: [PATCH 67/87] =?UTF-8?q?linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- x/rollapp/keeper/grpc_query_state_info_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/rollapp/keeper/grpc_query_state_info_test.go b/x/rollapp/keeper/grpc_query_state_info_test.go index f964047fc..fb4a18ed6 100644 --- a/x/rollapp/keeper/grpc_query_state_info_test.go +++ b/x/rollapp/keeper/grpc_query_state_info_test.go @@ -177,5 +177,4 @@ func TestFindStateInfoByHeight(t *testing.T) { } }) } - } From efaa792aacc8e420ebc995f802ac7968967d8b21 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:42:58 +0530 Subject: [PATCH 68/87] store block proposer as bech32 address --- x/lightclient/ante/ibc_msg_update_client.go | 9 +++---- .../ante/ibc_msg_update_client_test.go | 2 +- x/lightclient/keeper/genesis.go | 2 +- x/lightclient/keeper/genesis_test.go | 4 ++-- x/lightclient/keeper/hook_listener.go | 24 ++----------------- x/lightclient/keeper/hook_listener_test.go | 4 ++-- x/lightclient/keeper/keeper.go | 12 ++++++---- 7 files changed, 20 insertions(+), 37 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index bc2555c71..30e4f953c 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -44,7 +44,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // No BDs found for given height. // Will accept the update optimistically // But also save the blockProposer address with the height for future verification - i.acceptUpdateOptimistically(ctx, rollappID, msg.ClientId, header) + i.acceptUpdateOptimistically(ctx, msg.ClientId, header) return nil } bd, _ := stateInfo.GetBlockDescriptor(height.GetRevisionHeight()) @@ -54,7 +54,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli // No BDs found for next height. // Will accept the update optimistically // But also save the blockProposer address with the height for future verification - i.acceptUpdateOptimistically(ctx, rollappID, msg.ClientId, header) + i.acceptUpdateOptimistically(ctx, msg.ClientId, header) return nil } sequencerPubKey, err := i.lightClientKeeper.GetSequencerPubKey(ctx, stateInfo.Sequencer) @@ -75,7 +75,8 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli return nil } -func (i IBCMessagesDecorator) acceptUpdateOptimistically(ctx sdk.Context, rollappID string, clientID string, header *ibctm.Header) { +func (i IBCMessagesDecorator) acceptUpdateOptimistically(ctx sdk.Context, clientID string, header *ibctm.Header) { proposerAddress := header.Header.ProposerAddress - i.lightClientKeeper.SetConsensusStateSigner(ctx, clientID, header.TrustedHeight.RevisionHeight, proposerAddress) + proposerBech32Address := sdk.AccAddress(proposerAddress).String() + i.lightClientKeeper.SetConsensusStateSigner(ctx, clientID, header.TrustedHeight.RevisionHeight, proposerBech32Address) } diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index 90e685218..217403a57 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -125,7 +125,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { require.NoError(t, err) signer, found := k.GetConsensusStateSigner(ctx, "canon-client-id", 1) require.True(t, found) - require.Equal(t, []byte("sequencerAddr"), signer) + require.Equal(t, sdk.AccAddress([]byte("sequencerAddr")).String(), signer) }, }, { diff --git a/x/lightclient/keeper/genesis.go b/x/lightclient/keeper/genesis.go index 8d338c89d..a021dbee6 100644 --- a/x/lightclient/keeper/genesis.go +++ b/x/lightclient/keeper/genesis.go @@ -13,7 +13,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genesisState types.GenesisState) { k.SetCanonicalClient(ctx, client.RollappId, client.IbcClientId) } for _, stateSigner := range genesisState.GetConsensusStateSigners() { - k.SetConsensusStateSigner(ctx, stateSigner.IbcClientId, stateSigner.Height, []byte(stateSigner.Signer)) + k.SetConsensusStateSigner(ctx, stateSigner.IbcClientId, stateSigner.Height, stateSigner.Signer) } } diff --git a/x/lightclient/keeper/genesis_test.go b/x/lightclient/keeper/genesis_test.go index f3a723d3b..3e8c5bcab 100644 --- a/x/lightclient/keeper/genesis_test.go +++ b/x/lightclient/keeper/genesis_test.go @@ -44,8 +44,8 @@ func TestExportGenesis(t *testing.T) { keeper.SetCanonicalClient(ctx, "rollapp-1", "client-1") keeper.SetCanonicalClient(ctx, "rollapp-2", "client-2") - keeper.SetConsensusStateSigner(ctx, "client-1", 1, []byte("signer-1")) - keeper.SetConsensusStateSigner(ctx, "client-1", 2, []byte("signer-1")) + keeper.SetConsensusStateSigner(ctx, "client-1", 1, "signer-1") + keeper.SetConsensusStateSigner(ctx, "client-1", 2, "signer-1") genesis := keeper.ExportGenesis(ctx) diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 552f7fe1f..d19167dbb 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -4,7 +4,6 @@ import ( "time" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" @@ -76,7 +75,7 @@ func (hook rollappHook) AfterUpdateState( return nil } -func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, bd rollapptypes.BlockDescriptor, canonicalClient string, sequencerPk tmprotocrypto.PublicKey, tmHeaderSigner []byte) error { +func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, bd rollapptypes.BlockDescriptor, canonicalClient string, sequencerPk tmprotocrypto.PublicKey, tmHeaderSignerAddress string) error { height := ibcclienttypes.NewHeight(ibcRevisionNumber, bd.GetHeight()) consensusState, _ := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash @@ -98,29 +97,10 @@ func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, b // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header - sequencerAddr, err := getAddress(tmHeaderSigner) - if err != nil { - return err - } - err = hook.k.rollappKeeper.HandleFraud(ctx, rollappId, canonicalClient, bd.GetHeight(), sequencerAddr) + err = hook.k.rollappKeeper.HandleFraud(ctx, rollappId, canonicalClient, bd.GetHeight(), tmHeaderSignerAddress) if err != nil { return err } } return nil } - -// getAddress converts a tendermint public key to a bech32 address -func getAddress(tmPubkeyBz []byte) (string, error) { - var tmpk tmprotocrypto.PublicKey - err := tmpk.Unmarshal(tmPubkeyBz) - if err != nil { - return "", err - } - pubkey, err := cryptocodec.FromTmProtoPublicKey(tmpk) - if err != nil { - return "", err - } - acc := sdk.AccAddress(pubkey.Address().Bytes()) - return acc.String(), nil -} diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index 441b5a89e..c6d5b0f36 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -64,7 +64,7 @@ func TestAfterUpdateState(t *testing.T) { require.NoError(t, err) blockSignerTmPubKeyBytes, err := blockSignerTmPubKey.Marshal() require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKeyBytes) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKeyBytes)) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ @@ -103,7 +103,7 @@ func TestAfterUpdateState(t *testing.T) { require.NoError(t, err) blockSignerTmPubKeyBytes, err := blockSignerTmPubKey.Marshal() require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, blockSignerTmPubKeyBytes) + k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKeyBytes)) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index e7e3a9855..2e4c0ecda 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -58,18 +58,20 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } -func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencer []byte) { +// SetConsenusStateSigner sets the bech32 address of the sequencer who signed the block header for the given height of the client +func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencerAddr string) { store := ctx.KVStore(k.storeKey) - store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), sequencer) + store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), []byte(sequencerAddr)) } -func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64) ([]byte, bool) { +// GetConsensusStateSigner returns the bech32 address of the sequencer who signed the block header for the given height of the client +func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64) (string, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.ConsensusStateSignerKeyByClientID(clientID, height)) if bz == nil { - return []byte{}, false + return "", false } - return bz, true + return string(bz), true } func (k Keeper) GetAllConsensusStateSigners(ctx sdk.Context) (signers []types.ConsensusStateSigner) { From a551f9ea737c8a1ba3ae324beec026583715531c Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 28 Aug 2024 18:11:53 +0530 Subject: [PATCH 69/87] =?UTF-8?q?linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- x/lightclient/keeper/canonical_client.go | 3 +++ x/lightclient/keeper/genesis_test.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/x/lightclient/keeper/canonical_client.go b/x/lightclient/keeper/canonical_client.go index c1b17e8ec..4a66064e6 100644 --- a/x/lightclient/keeper/canonical_client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -46,6 +46,9 @@ func (k Keeper) GetProspectiveCanonicalClient(ctx sdk.Context, rollappId string, ClientId: client.GetClientId(), Pagination: &query.PageRequest{Limit: stateInfo.GetLatestHeight()}, }) + if err != nil { + continue + } stateCompatible = true for _, consensusHeight := range res.ConsensusStateHeights { consensusState, _ := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, consensusHeight) diff --git a/x/lightclient/keeper/genesis_test.go b/x/lightclient/keeper/genesis_test.go index 3e8c5bcab..bf282f26d 100644 --- a/x/lightclient/keeper/genesis_test.go +++ b/x/lightclient/keeper/genesis_test.go @@ -33,10 +33,10 @@ func TestInitGenesis(t *testing.T) { signer, found := keeper.GetConsensusStateSigner(ctx, "client-1", 1) require.True(t, found) - require.Equal(t, "signer-1", string(signer)) + require.Equal(t, "signer-1", signer) signer, found = keeper.GetConsensusStateSigner(ctx, "client-1", 2) require.True(t, found) - require.Equal(t, "signer-1", string(signer)) + require.Equal(t, "signer-1", signer) } func TestExportGenesis(t *testing.T) { From 74c94fc4a908fc58799faee23e08058c86eb9946 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:22:25 +0530 Subject: [PATCH 70/87] adding docstring for proto --- proto/dymensionxyz/dymension/lightclient/genesis.proto | 3 +++ x/lightclient/types/genesis.pb.go | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/proto/dymensionxyz/dymension/lightclient/genesis.proto b/proto/dymensionxyz/dymension/lightclient/genesis.proto index 0c23cb12a..b7344c921 100644 --- a/proto/dymensionxyz/dymension/lightclient/genesis.proto +++ b/proto/dymensionxyz/dymension/lightclient/genesis.proto @@ -16,7 +16,10 @@ message CanonicalClient { } message ConsensusStateSigner { + // ibc_client_id is the canonical IBC client which has accepted a client update optimistically string ibc_client_id = 1; + // height is the client height which was updated optimistically uint64 height = 2; + // signer is the bech32 address of the block proposer which signed the tm header string signer = 3; } \ No newline at end of file diff --git a/x/lightclient/types/genesis.pb.go b/x/lightclient/types/genesis.pb.go index 653337521..e04a435c9 100644 --- a/x/lightclient/types/genesis.pb.go +++ b/x/lightclient/types/genesis.pb.go @@ -128,9 +128,12 @@ func (m *CanonicalClient) GetIbcClientId() string { } type ConsensusStateSigner struct { + // ibc_client_id is the canonical IBC client which has accepted a client update optimistically IbcClientId string `protobuf:"bytes,1,opt,name=ibc_client_id,json=ibcClientId,proto3" json:"ibc_client_id,omitempty"` - Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` - Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` + // height is the client height which was updated optimistically + Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` + // signer is the bech32 address of the block proposer which signed the tm header + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` } func (m *ConsensusStateSigner) Reset() { *m = ConsensusStateSigner{} } From d7a5b7f8d1072ab474cb30e2502fe8e1276cbde9 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:55:22 +0530 Subject: [PATCH 71/87] using `IterateClientStates` instead of `GetAllGenesisClients` --- app/keepers/keepers.go | 1 - testutil/keeper/lightclient.go | 23 ++-- x/lightclient/ante/ibc_msgs_test.go | 4 +- x/lightclient/keeper/canonical_client.go | 135 +++++++++++------------ x/lightclient/types/expected_keepers.go | 2 +- 5 files changed, 81 insertions(+), 84 deletions(-) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 6452557af..f028a6ee6 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -370,7 +370,6 @@ func (a *AppKeepers) InitKeepers( a.SequencerKeeper, a.RollappKeeper, ) - a.RollappKeeper.SetSequencerKeeper(a.SequencerKeeper) a.IncentivesKeeper = incentiveskeeper.NewKeeper( diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 178ca4b30..1a183f37e 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -70,13 +70,8 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { time.Hour*24*7*2, time.Hour*24*7*3, time.Minute*10, ibcclienttypes.MustParseHeight("1-2"), commitmenttypes.GetSDKSpecs(), []string{}, ) - packedCS, err := ibcclienttypes.PackClientState(cs) - require.NoError(t, err) - testGenesisClients := ibcclienttypes.IdentifiedClientStates{ - { - ClientId: "canon-client-id", - ClientState: packedCS, - }, + testGenesisClients := map[string]exported.ClientState{ + "canon-client-id": cs, } mockIBCKeeper := NewMockIBCClientKeeper(testConsensusStates, testGenesisClients) @@ -97,16 +92,16 @@ func LightClientKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { type MockIBCCLientKeeper struct { clientConsensusState map[string]map[uint64]exported.ConsensusState - genesisClients ibcclienttypes.IdentifiedClientStates + clientStates map[string]exported.ClientState } func NewMockIBCClientKeeper( clientCS map[string]map[uint64]exported.ConsensusState, - genesisClients ibcclienttypes.IdentifiedClientStates, + genesisClients map[string]exported.ClientState, ) *MockIBCCLientKeeper { return &MockIBCCLientKeeper{ clientConsensusState: clientCS, - genesisClients: genesisClients, + clientStates: genesisClients, } } @@ -119,8 +114,12 @@ func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) ( return nil, false } -func (m *MockIBCCLientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates { - return m.genesisClients +func (m *MockIBCCLientKeeper) IterateClientStates(ctx sdk.Context, prefix []byte, cb func(clientID string, cs exported.ClientState) bool) { + for clientID, cs := range m.clientStates { + if cb(clientID, cs) { + break + } + } } func (m *MockIBCCLientKeeper) ConsensusStateHeights(c context.Context, req *ibcclienttypes.QueryConsensusStateHeightsRequest) (*ibcclienttypes.QueryConsensusStateHeightsResponse, error) { diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index bf913042b..8059eb5d7 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -76,8 +76,8 @@ func (m *MockIBCClientKeeper) GetClientState(ctx sdk.Context, clientID string) ( return val, found } -func (m *MockIBCClientKeeper) GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates { - return nil +func (m *MockIBCClientKeeper) IterateClientStates(ctx sdk.Context, prefix []byte, cb func(clientID string, cs exported.ClientState) bool) { + } func (m *MockIBCClientKeeper) ConsensusStateHeights(c context.Context, req *ibcclienttypes.QueryConsensusStateHeightsRequest) (*ibcclienttypes.QueryConsensusStateHeightsResponse, error) { diff --git a/x/lightclient/keeper/canonical_client.go b/x/lightclient/keeper/canonical_client.go index 4a66064e6..a9570d142 100644 --- a/x/lightclient/keeper/canonical_client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -6,6 +6,7 @@ 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/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" @@ -21,75 +22,15 @@ const ( // 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, stateInfo *rollapptypes.StateInfo) (clientID string, stateCompatible bool) { - clients := k.ibcClientKeeper.GetAllGenesisClients(ctx) - for _, client := range clients { - clientState, err := ibcclienttypes.UnpackClientState(client.ClientState) - if err != nil { - continue - } - // Cast client state to tendermint client state - we need this to get the chain id and state height - tmClientState, ok := clientState.(*ibctm.ClientState) - if !ok { - continue - } - if tmClientState.ChainId != rollappId { - continue - } - if !types.IsCanonicalClientParamsValid(tmClientState) { - continue - } - sequencerPk, err := k.GetSequencerPubKey(ctx, stateInfo.Sequencer) - if err != nil { - continue - } - res, err := k.ibcClientKeeper.ConsensusStateHeights(ctx, &ibcclienttypes.QueryConsensusStateHeightsRequest{ - ClientId: client.GetClientId(), - Pagination: &query.PageRequest{Limit: stateInfo.GetLatestHeight()}, - }) - if err != nil { - continue + k.ibcClientKeeper.IterateClientStates(ctx, nil, func(client string, cs exported.ClientState) bool { + ok := k.isValidClient(ctx, client, cs, rollappId, stateInfo) + if ok { + clientID = client + stateCompatible = true + return true } - stateCompatible = true - for _, consensusHeight := range res.ConsensusStateHeights { - consensusState, _ := k.ibcClientKeeper.GetClientConsensusState(ctx, client.ClientId, consensusHeight) - tmConsensusState, _ := consensusState.(*ibctm.ConsensusState) - ibcState := types.IBCState{ - Root: tmConsensusState.GetRoot().GetHash(), - NextValidatorsHash: tmConsensusState.NextValidatorsHash, - Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), - } - - var rollappState types.RollappState - bd, found := stateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) - if found { - rollappState.BlockDescriptor = bd - rollappState.NextBlockSequencer = sequencerPk - } else { - // Look up the state info for the block descriptor - oldStateInfo, err := k.rollappKeeper.FindStateInfoByHeight(ctx, rollappId, consensusHeight.GetRevisionHeight()) - if err != nil { - stateCompatible = false - break - } - bd, _ = oldStateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) - oldSequencerPk, err := k.GetSequencerPubKey(ctx, oldStateInfo.Sequencer) - if err != nil { - stateCompatible = false - break - } - rollappState.BlockDescriptor = bd - rollappState.NextBlockSequencer = oldSequencerPk - } - err := types.CheckCompatibility(ibcState, rollappState) - if err != nil { - stateCompatible = false - break - } - } - if stateCompatible { - return client.GetClientId(), true - } - } + return false + }) return } @@ -120,3 +61,61 @@ func (k Keeper) GetAllCanonicalClients(ctx sdk.Context) (clients []types.Canonic } return } + +func (k Keeper) isValidClient(ctx sdk.Context, clientID string, cs exported.ClientState, rollappId string, stateInfo *rollapptypes.StateInfo) bool { + tmClientState, ok := cs.(*ibctm.ClientState) + if !ok { + return false + } + if tmClientState.ChainId != rollappId { + return false + } + if !types.IsCanonicalClientParamsValid(tmClientState) { + return false + } + res, err := k.ibcClientKeeper.ConsensusStateHeights(ctx, &ibcclienttypes.QueryConsensusStateHeightsRequest{ + ClientId: clientID, + Pagination: &query.PageRequest{Limit: stateInfo.GetLatestHeight()}, + }) + if err != nil { + return false + } + for _, consensusHeight := range res.ConsensusStateHeights { + consensusState, _ := k.ibcClientKeeper.GetClientConsensusState(ctx, clientID, consensusHeight) + tmConsensusState, _ := consensusState.(*ibctm.ConsensusState) + ibcState := types.IBCState{ + Root: tmConsensusState.GetRoot().GetHash(), + NextValidatorsHash: tmConsensusState.NextValidatorsHash, + Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), + } + + var rollappState types.RollappState + bd, found := stateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) + if found { + rollappState.BlockDescriptor = bd + sequencerPk, err := k.GetSequencerPubKey(ctx, stateInfo.Sequencer) + if err != nil { + return false + } + rollappState.NextBlockSequencer = sequencerPk + } else { + // Look up the state info for the block descriptor + oldStateInfo, err := k.rollappKeeper.FindStateInfoByHeight(ctx, rollappId, consensusHeight.GetRevisionHeight()) + if err != nil { + return false + } + bd, _ = oldStateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) + oldSequencer, err := k.GetSequencerPubKey(ctx, oldStateInfo.Sequencer) + if err != nil { + return false + } + rollappState.BlockDescriptor = bd + rollappState.NextBlockSequencer = oldSequencer + } + err := types.CheckCompatibility(ibcState, rollappState) + if err != nil { + return false + } + } + return true +} diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 7ad28fa84..9980b5517 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -26,7 +26,7 @@ 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) - GetAllGenesisClients(ctx sdk.Context) ibcclienttypes.IdentifiedClientStates + IterateClientStates(ctx sdk.Context, prefix []byte, cb func(clientID string, cs exported.ClientState) bool) ConsensusStateHeights(c context.Context, req *ibcclienttypes.QueryConsensusStateHeightsRequest) (*ibcclienttypes.QueryConsensusStateHeightsResponse, error) } From fb92c668c05d3e33283f7424a5936f56358167fd Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:08:30 +0530 Subject: [PATCH 72/87] SetCanonicalClient e2e tests --- ibctesting/light_client_test.go | 106 +++++++++++++++++++++++ x/lightclient/keeper/canonical_client.go | 6 +- x/lightclient/types/params.go | 61 +++++++++++-- 3 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 ibctesting/light_client_test.go diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go new file mode 100644 index 000000000..edc7543ce --- /dev/null +++ b/ibctesting/light_client_test.go @@ -0,0 +1,106 @@ +package ibctesting_test + +import ( + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" + rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" + "testing" + + ibctesting "github.com/cosmos/ibc-go/v7/testing" + "github.com/stretchr/testify/suite" +) + +type lightClientSuite struct { + utilSuite + path *ibctesting.Path +} + +func TestLightClientSuite(t *testing.T) { + suite.Run(t, new(lightClientSuite)) +} + +func (s *lightClientSuite) TestSetCanonicalClient_FailsTrustRequirements() { + s.createRollapp(false, nil) + s.registerSequencer() + // The default tm client does not match the trust requirements of a canonical client. + // So it should not be set as one. + s.path = s.newTransferPath(s.hubChain(), s.rollappChain()) + 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) + + _, found := s.hubApp().LightClientKeeper.GetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID) + s.False(found) +} + +func (s *lightClientSuite) TestSetCanonicalClient_FailsIncompatibleState() { + s.createRollapp(false, nil) + s.registerSequencer() + // create a custom tm client which matches the trust requirements of a canonical client + newTmConfig := ibctesting.TendermintConfig{ + TrustLevel: types.ExpectedCanonicalClientParams.TrustLevel, + TrustingPeriod: types.ExpectedCanonicalClientParams.TrustingPeriod, + UnbondingPeriod: types.ExpectedCanonicalClientParams.UnbondingPeriod, + MaxClockDrift: types.ExpectedCanonicalClientParams.MaxClockDrift, + } + endpointA := ibctesting.NewEndpoint(s.hubChain(), &newTmConfig, 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} + + // Creating the tm client - this will take us to the next block + s.coordinator.SetupClients(s.path) + + // Update the rollapp state - this will trigger the check for prospective canonical client + // The block descriptor root has dummy values and will not match the IBC roots for the same height + currentRollappBlockHeight := uint64(s.rollappChain().App.LastBlockHeight()) + s.updateRollappState(currentRollappBlockHeight) + + _, found := s.hubApp().LightClientKeeper.GetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID) + s.False(found) +} + +func (s *lightClientSuite) TestSetCanonicalClient_Succeeds() { + s.createRollapp(false, nil) + s.registerSequencer() + // create a custom tm client which matches the trust requirements of a canonical client + newTmConfig := ibctesting.TendermintConfig{ + TrustLevel: types.ExpectedCanonicalClientParams.TrustLevel, + TrustingPeriod: types.ExpectedCanonicalClientParams.TrustingPeriod, + UnbondingPeriod: types.ExpectedCanonicalClientParams.UnbondingPeriod, + MaxClockDrift: types.ExpectedCanonicalClientParams.MaxClockDrift, + } + endpointA := ibctesting.NewEndpoint(s.hubChain(), &newTmConfig, 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) + + canonClientID, found := s.hubApp().LightClientKeeper.GetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID) + s.True(found) + s.Equal("07-tendermint-0", canonClientID) +} diff --git a/x/lightclient/keeper/canonical_client.go b/x/lightclient/keeper/canonical_client.go index a9570d142..bb1081961 100644 --- a/x/lightclient/keeper/canonical_client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -73,14 +73,18 @@ func (k Keeper) isValidClient(ctx sdk.Context, clientID string, cs exported.Clie if !types.IsCanonicalClientParamsValid(tmClientState) { return false } + maxHeight := stateInfo.GetLatestHeight() - 1 res, err := k.ibcClientKeeper.ConsensusStateHeights(ctx, &ibcclienttypes.QueryConsensusStateHeightsRequest{ ClientId: clientID, - Pagination: &query.PageRequest{Limit: stateInfo.GetLatestHeight()}, + Pagination: &query.PageRequest{Limit: maxHeight}, }) if err != nil { return false } for _, consensusHeight := range res.ConsensusStateHeights { + if consensusHeight.GetRevisionHeight() > maxHeight { + continue + } consensusState, _ := k.ibcClientKeeper.GetClientConsensusState(ctx, clientID, consensusHeight) tmConsensusState, _ := consensusState.(*ibctm.ConsensusState) ibcState := types.IBCState{ diff --git a/x/lightclient/types/params.go b/x/lightclient/types/params.go index aed0edab5..1daeda364 100644 --- a/x/lightclient/types/params.go +++ b/x/lightclient/types/params.go @@ -1,10 +1,12 @@ package types import ( + "bytes" "time" "github.com/cometbft/cometbft/libs/math" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" ics23 "github.com/cosmos/ics23/go" ) @@ -31,10 +33,7 @@ var ExpectedCanonicalClientParams = ibctm.ClientState{ // ProofSpecs defines the ICS-23 standard proof specifications used by // the light client. It is used configure a proof for either existence // or non-existence of a key value pair - ProofSpecs: []*ics23.ProofSpec{ // the proofspecs for a SDK chain - ics23.IavlSpec, - ics23.TendermintSpec, - }, + ProofSpecs: commitmenttypes.GetSDKSpecs(), AllowUpdateAfterExpiry: false, AllowUpdateAfterMisbehaviour: false, // For chains using Cosmos-SDK's default x/upgrade module, the upgrade path is as follows @@ -65,7 +64,7 @@ func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { return false } for i, proofSpec := range clientState.ProofSpecs { - if proofSpec != ExpectedCanonicalClientParams.ProofSpecs[i] { + if !EqualICS23ProofSpecs(*proofSpec, *ExpectedCanonicalClientParams.ProofSpecs[i]) { return false } } @@ -76,3 +75,55 @@ func IsCanonicalClientParamsValid(clientState *ibctm.ClientState) bool { } return true } + +func EqualICS23ProofSpecs(proofSpecs1, proofSpecs2 ics23.ProofSpec) bool { + if proofSpecs1.MaxDepth != proofSpecs2.MaxDepth { + return false + } + if proofSpecs1.MinDepth != proofSpecs2.MinDepth { + return false + } + if proofSpecs1.PrehashKeyBeforeComparison != proofSpecs2.PrehashKeyBeforeComparison { + return false + } + if proofSpecs1.LeafSpec.Hash != proofSpecs2.LeafSpec.Hash { + return false + } + if proofSpecs1.LeafSpec.PrehashKey != proofSpecs2.LeafSpec.PrehashKey { + return false + } + if proofSpecs1.LeafSpec.PrehashValue != proofSpecs2.LeafSpec.PrehashValue { + return false + } + if proofSpecs1.LeafSpec.Length != proofSpecs2.LeafSpec.Length { + return false + } + if !bytes.Equal(proofSpecs1.LeafSpec.Prefix, proofSpecs2.LeafSpec.Prefix) { + return false + } + if len(proofSpecs1.InnerSpec.ChildOrder) != len(proofSpecs2.InnerSpec.ChildOrder) { + return false + } + for i, childOrder := range proofSpecs1.InnerSpec.ChildOrder { + if childOrder != proofSpecs2.InnerSpec.ChildOrder[i] { + return false + } + } + if proofSpecs1.InnerSpec.ChildSize != proofSpecs2.InnerSpec.ChildSize { + return false + } + if proofSpecs1.InnerSpec.MinPrefixLength != proofSpecs2.InnerSpec.MinPrefixLength { + return false + } + if proofSpecs1.InnerSpec.MaxPrefixLength != proofSpecs2.InnerSpec.MaxPrefixLength { + return false + } + if !bytes.Equal(proofSpecs1.InnerSpec.EmptyChild, proofSpecs2.InnerSpec.EmptyChild) { + return false + } + if proofSpecs1.InnerSpec.Hash != proofSpecs2.InnerSpec.Hash { + return false + } + + return true +} From c5a14f9b2f241b5317498ddd9fb534c09d643c07 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:11:39 +0530 Subject: [PATCH 73/87] =?UTF-8?q?linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- x/lightclient/ante/ibc_msgs_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/lightclient/ante/ibc_msgs_test.go b/x/lightclient/ante/ibc_msgs_test.go index 8059eb5d7..eb8ae92e7 100644 --- a/x/lightclient/ante/ibc_msgs_test.go +++ b/x/lightclient/ante/ibc_msgs_test.go @@ -77,7 +77,6 @@ func (m *MockIBCClientKeeper) GetClientState(ctx sdk.Context, clientID string) ( } func (m *MockIBCClientKeeper) IterateClientStates(ctx sdk.Context, prefix []byte, cb func(clientID string, cs exported.ClientState) bool) { - } func (m *MockIBCClientKeeper) ConsensusStateHeights(c context.Context, req *ibcclienttypes.QueryConsensusStateHeightsRequest) (*ibcclienttypes.QueryConsensusStateHeightsResponse, error) { From 5bfa143120f1ffaaee3077c0d2a281e1e1bfe4d2 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:31:26 +0530 Subject: [PATCH 74/87] use block valhash instead of proposer address for fraud punishment --- app/keepers/keepers.go | 1 + testutil/keeper/lightclient.go | 8 +++++ x/lightclient/ante/ibc_msg_update_client.go | 4 +-- .../ante/ibc_msg_update_client_test.go | 10 ++++-- x/lightclient/keeper/genesis.go | 2 +- x/lightclient/keeper/genesis_test.go | 12 +++---- x/lightclient/keeper/hook_listener.go | 16 +++++---- x/lightclient/keeper/hook_listener_test.go | 12 +++---- x/lightclient/keeper/keeper.go | 35 +++++++++++++------ x/lightclient/types/errors.go | 1 + x/lightclient/types/expected_keepers.go | 1 + x/lightclient/types/keys.go | 14 ++++---- 12 files changed, 72 insertions(+), 44 deletions(-) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index f028a6ee6..6452557af 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -370,6 +370,7 @@ func (a *AppKeepers) InitKeepers( a.SequencerKeeper, a.RollappKeeper, ) + a.RollappKeeper.SetSequencerKeeper(a.SequencerKeeper) a.IncentivesKeeper = incentiveskeeper.NewKeeper( diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 1a183f37e..850c8e9e0 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -146,6 +146,14 @@ func (m *MockSequencerKeeper) GetSequencer(ctx sdk.Context, seqAddr string) (seq return seq, ok } +func (m *MockSequencerKeeper) GetAllSequencers(ctx sdk.Context) []sequencertypes.Sequencer { + seqs := make([]sequencertypes.Sequencer, 0, len(m.sequencers)) + for _, seq := range m.sequencers { + seqs = append(seqs, seq) + } + return seqs +} + type MockRollappKeeper struct { } diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 30e4f953c..1de75ef93 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -76,7 +76,5 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli } func (i IBCMessagesDecorator) acceptUpdateOptimistically(ctx sdk.Context, clientID string, header *ibctm.Header) { - proposerAddress := header.Header.ProposerAddress - proposerBech32Address := sdk.AccAddress(proposerAddress).String() - i.lightClientKeeper.SetConsensusStateSigner(ctx, clientID, header.TrustedHeight.RevisionHeight, proposerBech32Address) + i.lightClientKeeper.SetConsensusStateValHash(ctx, clientID, header.TrustedHeight.RevisionHeight, header.Header.ValidatorsHash) } diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index 217403a57..6c3d9397a 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -72,10 +72,12 @@ func TestHandleMsgUpdateClient(t *testing.T) { name: "Could not find state info for height - ensure optimistically accepted and signer stored in state", prepare: func(ctx sdk.Context, k keeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") + seqValHash, err := k.GetSequencerHash(ctx, keepertest.Alice) + require.NoError(t, err) var valSet, trustedVals *cmtproto.ValidatorSet signedHeader := &cmtproto.SignedHeader{ Header: &cmtproto.Header{ - ProposerAddress: []byte("sequencerAddr"), + ValidatorsHash: seqValHash, }, Commit: &cmtproto.Commit{}, } @@ -123,9 +125,11 @@ func TestHandleMsgUpdateClient(t *testing.T) { }, assert: func(ctx sdk.Context, k keeper.Keeper, err error) { require.NoError(t, err) - signer, found := k.GetConsensusStateSigner(ctx, "canon-client-id", 1) + seqValHash, found := k.GetConsensusStateValHash(ctx, "canon-client-id", 1) require.True(t, found) - require.Equal(t, sdk.AccAddress([]byte("sequencerAddr")).String(), signer) + seq, err := k.GetSequencerFromValHash(ctx, seqValHash) + require.NoError(t, err) + require.Equal(t, keepertest.Alice, seq) }, }, { diff --git a/x/lightclient/keeper/genesis.go b/x/lightclient/keeper/genesis.go index a021dbee6..1f8fc9e71 100644 --- a/x/lightclient/keeper/genesis.go +++ b/x/lightclient/keeper/genesis.go @@ -13,7 +13,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genesisState types.GenesisState) { k.SetCanonicalClient(ctx, client.RollappId, client.IbcClientId) } for _, stateSigner := range genesisState.GetConsensusStateSigners() { - k.SetConsensusStateSigner(ctx, stateSigner.IbcClientId, stateSigner.Height, stateSigner.Signer) + k.SetConsensusStateValHash(ctx, stateSigner.IbcClientId, stateSigner.Height, []byte(stateSigner.Signer)) } } diff --git a/x/lightclient/keeper/genesis_test.go b/x/lightclient/keeper/genesis_test.go index bf282f26d..000f599cf 100644 --- a/x/lightclient/keeper/genesis_test.go +++ b/x/lightclient/keeper/genesis_test.go @@ -31,12 +31,12 @@ func TestInitGenesis(t *testing.T) { require.True(t, found) require.Equal(t, "client-2", ibc) - signer, found := keeper.GetConsensusStateSigner(ctx, "client-1", 1) + signer, found := keeper.GetConsensusStateValHash(ctx, "client-1", 1) require.True(t, found) - require.Equal(t, "signer-1", signer) - signer, found = keeper.GetConsensusStateSigner(ctx, "client-1", 2) + require.Equal(t, []byte("signer-1"), signer) + signer, found = keeper.GetConsensusStateValHash(ctx, "client-1", 2) require.True(t, found) - require.Equal(t, "signer-1", signer) + require.Equal(t, []byte("signer-1"), signer) } func TestExportGenesis(t *testing.T) { @@ -44,8 +44,8 @@ func TestExportGenesis(t *testing.T) { keeper.SetCanonicalClient(ctx, "rollapp-1", "client-1") keeper.SetCanonicalClient(ctx, "rollapp-2", "client-2") - keeper.SetConsensusStateSigner(ctx, "client-1", 1, "signer-1") - keeper.SetConsensusStateSigner(ctx, "client-1", 2, "signer-1") + keeper.SetConsensusStateValHash(ctx, "client-1", 1, []byte("signer-1")) + keeper.SetConsensusStateValHash(ctx, "client-1", 2, []byte("signer-1")) genesis := keeper.ExportGenesis(ctx) diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index d19167dbb..0c06d8bc6 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -50,24 +50,24 @@ func (hook rollappHook) AfterUpdateState( for h := latestHeight - 1; h >= stateInfo.StartHeight; h-- { bd, _ := stateInfo.GetBlockDescriptor(h) // Check if any optimistic updates were made for the given height - tmHeaderSigner, found := hook.k.GetConsensusStateSigner(ctx, canonicalClient, bd.GetHeight()) + blockValHash, found := hook.k.GetConsensusStateValHash(ctx, canonicalClient, bd.GetHeight()) if !found { continue } - err := hook.checkStateForHeight(ctx, rollappId, bd, canonicalClient, sequencerPk, tmHeaderSigner) + err := hook.checkStateForHeight(ctx, rollappId, bd, canonicalClient, sequencerPk, blockValHash) if err != nil { return err } } // Check for the last BD from the previous stateInfo as now we have the nextValhash available for that block - tmHeaderSigner, found := hook.k.GetConsensusStateSigner(ctx, canonicalClient, stateInfo.StartHeight-1) + blockValHash, found := hook.k.GetConsensusStateValHash(ctx, canonicalClient, stateInfo.StartHeight-1) if found { previousStateInfo, err := hook.k.rollappKeeper.FindStateInfoByHeight(ctx, rollappId, stateInfo.StartHeight-1) if err != nil { return err } bd, _ := previousStateInfo.GetBlockDescriptor(stateInfo.StartHeight - 1) - err = hook.checkStateForHeight(ctx, rollappId, bd, canonicalClient, sequencerPk, tmHeaderSigner) + err = hook.checkStateForHeight(ctx, rollappId, bd, canonicalClient, sequencerPk, blockValHash) if err != nil { return err } @@ -75,7 +75,7 @@ func (hook rollappHook) AfterUpdateState( return nil } -func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, bd rollapptypes.BlockDescriptor, canonicalClient string, sequencerPk tmprotocrypto.PublicKey, tmHeaderSignerAddress string) error { +func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, bd rollapptypes.BlockDescriptor, canonicalClient string, sequencerPk tmprotocrypto.PublicKey, blockValHash []byte) error { height := ibcclienttypes.NewHeight(ibcRevisionNumber, bd.GetHeight()) consensusState, _ := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash @@ -97,7 +97,11 @@ func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, b // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header - err = hook.k.rollappKeeper.HandleFraud(ctx, rollappId, canonicalClient, bd.GetHeight(), tmHeaderSignerAddress) + sequencerAddress, err := hook.k.GetSequencerFromValHash(ctx, blockValHash) + if err != nil { + return err + } + err = hook.k.rollappKeeper.HandleFraud(ctx, rollappId, canonicalClient, bd.GetHeight(), sequencerAddress) if err != nil { return err } diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index c6d5b0f36..ebc3de56e 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -60,11 +60,9 @@ 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 { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - blockSignerTmPubKey, err := k.GetSequencerPubKey(ctx, keepertest.Alice) + seqValHash, err := k.GetSequencerHash(ctx, keepertest.Alice) require.NoError(t, err) - blockSignerTmPubKeyBytes, err := blockSignerTmPubKey.Marshal() - require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKeyBytes)) + k.SetConsensusStateValHash(ctx, "canon-client-id", 2, seqValHash) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ @@ -99,11 +97,9 @@ func TestAfterUpdateState(t *testing.T) { name: "state is compatible", prepare: func(ctx sdk.Context, k lightClientKeeper.Keeper) testInput { k.SetCanonicalClient(ctx, "rollapp-has-canon-client", "canon-client-id") - blockSignerTmPubKey, err := k.GetSequencerPubKey(ctx, keepertest.Alice) - require.NoError(t, err) - blockSignerTmPubKeyBytes, err := blockSignerTmPubKey.Marshal() + seqValHash, err := k.GetSequencerHash(ctx, keepertest.Alice) require.NoError(t, err) - k.SetConsensusStateSigner(ctx, "canon-client-id", 2, string(blockSignerTmPubKeyBytes)) + k.SetConsensusStateValHash(ctx, "canon-client-id", 2, seqValHash) return testInput{ rollappId: "rollapp-has-canon-client", stateInfo: &rollapptypes.StateInfo{ diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 2e4c0ecda..2759ffc49 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "fmt" "github.com/cometbft/cometbft/libs/log" @@ -58,29 +59,43 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } -// SetConsenusStateSigner sets the bech32 address of the sequencer who signed the block header for the given height of the client -func (k Keeper) SetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64, sequencerAddr string) { +func (k Keeper) GetSequencerFromValHash(ctx sdk.Context, blockValHash []byte) (string, error) { + sequencerList := k.sequencerKeeper.GetAllSequencers(ctx) + for _, seq := range sequencerList { + seqHash, err := seq.GetDymintPubKeyHash() + if err != nil { + return "", err + } + if bytes.Equal(seqHash, blockValHash) { + return seq.Address, nil + } + } + return "", types.ErrSequencerNotFound +} + +// SetConsenusStateSigner sets block valHash for the given height of the client +func (k Keeper) SetConsensusStateValHash(ctx sdk.Context, clientID string, height uint64, blockValHash []byte) { store := ctx.KVStore(k.storeKey) - store.Set(types.ConsensusStateSignerKeyByClientID(clientID, height), []byte(sequencerAddr)) + store.Set(types.ConsensusStateValhashKeyByClientID(clientID, height), blockValHash) } -// GetConsensusStateSigner returns the bech32 address of the sequencer who signed the block header for the given height of the client -func (k Keeper) GetConsensusStateSigner(ctx sdk.Context, clientID string, height uint64) (string, bool) { +// GetConsensusStateValHash returns the block valHash for the given height of the client +func (k Keeper) GetConsensusStateValHash(ctx sdk.Context, clientID string, height uint64) ([]byte, bool) { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.ConsensusStateSignerKeyByClientID(clientID, height)) + bz := store.Get(types.ConsensusStateValhashKeyByClientID(clientID, height)) if bz == nil { - return "", false + return nil, false } - return string(bz), true + return bz, true } func (k Keeper) GetAllConsensusStateSigners(ctx sdk.Context) (signers []types.ConsensusStateSigner) { store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.ConsensusStateSignerKey) + iterator := sdk.KVStorePrefixIterator(store, types.ConsensusStateValhashKey) defer iterator.Close() // nolint: errcheck for ; iterator.Valid(); iterator.Next() { key := iterator.Key() - clientID, height := types.ParseConsensusStateSignerKey(key) + clientID, height := types.ParseConsensusStateValhashKey(key) signers = append(signers, types.ConsensusStateSigner{ IbcClientId: clientID, Height: height, diff --git a/x/lightclient/types/errors.go b/x/lightclient/types/errors.go index f5d6fb055..309fb6e1f 100644 --- a/x/lightclient/types/errors.go +++ b/x/lightclient/types/errors.go @@ -9,4 +9,5 @@ var ( ErrStateRootsMismatch = errorsmod.Wrap(gerrc.ErrFailedPrecondition, "block descriptor state root does not match tendermint header app hash") ErrValidatorHashMismatch = errorsmod.Wrap(gerrc.ErrFailedPrecondition, "next validator hash does not match the sequencer for h+1") ErrTimestampMismatch = errorsmod.Wrap(gerrc.ErrFailedPrecondition, "block descriptor timestamp does not match tendermint header timestamp") + ErrSequencerNotFound = errorsmod.Wrap(gerrc.ErrNotFound, "sequencer for given valhash not found") ) diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 9980b5517..79ac41103 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -13,6 +13,7 @@ import ( type SequencerKeeperExpected interface { GetSequencer(ctx sdk.Context, sequencerAddress string) (val sequencertypes.Sequencer, found bool) + GetAllSequencers(ctx sdk.Context) (list []sequencertypes.Sequencer) } type RollappKeeperExpected interface { diff --git a/x/lightclient/types/keys.go b/x/lightclient/types/keys.go index bebceec8e..3ad12c0ad 100644 --- a/x/lightclient/types/keys.go +++ b/x/lightclient/types/keys.go @@ -19,9 +19,9 @@ const ( ) var ( - RollappClientKey = []byte{0x01} - ConsensusStateSignerKey = []byte{0x03} - canonicalClientKey = []byte{0x04} + RollappClientKey = []byte{0x01} + ConsensusStateValhashKey = []byte{0x03} + canonicalClientKey = []byte{0x04} ) func GetRollappClientKey(rollappId string) []byte { @@ -30,8 +30,8 @@ func GetRollappClientKey(rollappId string) []byte { return key } -func ConsensusStateSignerKeyByClientID(clientID string, height uint64) []byte { - key := ConsensusStateSignerKey +func ConsensusStateValhashKeyByClientID(clientID string, height uint64) []byte { + key := ConsensusStateValhashKey key = append(key, []byte(clientID)...) key = append(key, keySeparator...) key = append(key, sdk.Uint64ToBigEndian(height)...) @@ -44,8 +44,8 @@ func CanonicalClientKey(clientID string) []byte { return key } -func ParseConsensusStateSignerKey(key []byte) (clientID string, height uint64) { - key = key[len(ConsensusStateSignerKey):] +func ParseConsensusStateValhashKey(key []byte) (clientID string, height uint64) { + key = key[len(ConsensusStateValhashKey):] parts := bytes.Split(key, []byte(keySeparator)) clientID = string(parts[0]) height = sdk.BigEndianToUint64(parts[1]) From abce59c800b6fb7a319b58f71dfad434e395d2d3 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 29 Aug 2024 18:26:34 +0530 Subject: [PATCH 75/87] adding e2e test for updating client optimistically when stateinfo does not exist --- ibctesting/light_client_test.go | 56 +++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go index edc7543ce..8a6a9aa9a 100644 --- a/ibctesting/light_client_test.go +++ b/ibctesting/light_client_test.go @@ -1,14 +1,22 @@ package ibctesting_test import ( + "testing" + "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" - "testing" ibctesting "github.com/cosmos/ibc-go/v7/testing" "github.com/stretchr/testify/suite" ) +var canonicalClientConfig = ibctesting.TendermintConfig{ + TrustLevel: types.ExpectedCanonicalClientParams.TrustLevel, + TrustingPeriod: types.ExpectedCanonicalClientParams.TrustingPeriod, + UnbondingPeriod: types.ExpectedCanonicalClientParams.UnbondingPeriod, + MaxClockDrift: types.ExpectedCanonicalClientParams.MaxClockDrift, +} + type lightClientSuite struct { utilSuite path *ibctesting.Path @@ -38,13 +46,7 @@ func (s *lightClientSuite) TestSetCanonicalClient_FailsIncompatibleState() { s.createRollapp(false, nil) s.registerSequencer() // create a custom tm client which matches the trust requirements of a canonical client - newTmConfig := ibctesting.TendermintConfig{ - TrustLevel: types.ExpectedCanonicalClientParams.TrustLevel, - TrustingPeriod: types.ExpectedCanonicalClientParams.TrustingPeriod, - UnbondingPeriod: types.ExpectedCanonicalClientParams.UnbondingPeriod, - MaxClockDrift: types.ExpectedCanonicalClientParams.MaxClockDrift, - } - endpointA := ibctesting.NewEndpoint(s.hubChain(), &newTmConfig, ibctesting.NewConnectionConfig(), ibctesting.NewChannelConfig()) + 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 @@ -66,13 +68,7 @@ func (s *lightClientSuite) TestSetCanonicalClient_Succeeds() { s.createRollapp(false, nil) s.registerSequencer() // create a custom tm client which matches the trust requirements of a canonical client - newTmConfig := ibctesting.TendermintConfig{ - TrustLevel: types.ExpectedCanonicalClientParams.TrustLevel, - TrustingPeriod: types.ExpectedCanonicalClientParams.TrustingPeriod, - UnbondingPeriod: types.ExpectedCanonicalClientParams.UnbondingPeriod, - MaxClockDrift: types.ExpectedCanonicalClientParams.MaxClockDrift, - } - endpointA := ibctesting.NewEndpoint(s.hubChain(), &newTmConfig, ibctesting.NewConnectionConfig(), ibctesting.NewChannelConfig()) + 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 @@ -102,5 +98,33 @@ func (s *lightClientSuite) TestSetCanonicalClient_Succeeds() { canonClientID, found := s.hubApp().LightClientKeeper.GetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID) s.True(found) - s.Equal("07-tendermint-0", canonClientID) + s.Equal(endpointA.ClientID, canonClientID) +} + +func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateDoesntExist() { + s.createRollapp(false, nil) + s.registerSequencer() + currentRollappBlockHeight := uint64(s.rollappChain().App.LastBlockHeight()) + s.updateRollappState(currentRollappBlockHeight) + s.path = s.newTransferPath(s.hubChain(), s.rollappChain()) + s.coordinator.SetupClients(s.path) + s.hubApp().LightClientKeeper.SetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID, s.path.EndpointA.ClientID) + + for i := 0; i < 10; i++ { + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + + height := s.path.EndpointA.GetClientState().GetLatestHeight() + s.NoError(s.path.EndpointA.UpdateClient()) + // As there was no stateinfo found for the height, should have accepted the update optimistically. + seqValHash, found := s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, height.GetRevisionHeight()) + s.True(found) + seqAddr, err := s.hubApp().LightClientKeeper.GetSequencerFromValHash(s.hubCtx(), seqValHash) + s.NoError(err) + s.Equal(s.hubChain().SenderAccount.GetAddress().String(), seqAddr) +} + +func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateExists() { + // todo } From 81161c0a13a259521477cde81cf3f1db96156386 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Thu, 29 Aug 2024 19:11:34 +0530 Subject: [PATCH 76/87] =?UTF-8?q?linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ibctesting/light_client_test.go | 10 ++++++++++ testutil/keeper/lightclient.go | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go index 8a6a9aa9a..1050a9491 100644 --- a/ibctesting/light_client_test.go +++ b/ibctesting/light_client_test.go @@ -127,4 +127,14 @@ func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateDoesntExist() { func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateExists() { // todo + + // state match + // states dont match +} + +func (s *lightClientSuite) TestAfterUpdateState_OptimisticUpdateExists() { + // todo + + // states match + // states dont match - handle fraud } diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index 850c8e9e0..da6656766 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -154,8 +154,7 @@ func (m *MockSequencerKeeper) GetAllSequencers(ctx sdk.Context) []sequencertypes return seqs } -type MockRollappKeeper struct { -} +type MockRollappKeeper struct{} func NewMockRollappKeeper() *MockRollappKeeper { return &MockRollappKeeper{} From 3e18afad7d1b79f15f7ff020bd0423b260983bbc Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:14:59 +0530 Subject: [PATCH 77/87] adding e2e tests for update client ante --- ibctesting/light_client_test.go | 121 +++++++++++++++++- x/lightclient/ante/ibc_msg_update_client.go | 8 +- .../ante/ibc_msg_update_client_test.go | 3 + 3 files changed, 121 insertions(+), 11 deletions(-) diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go index 1050a9491..92b119ab2 100644 --- a/ibctesting/light_client_test.go +++ b/ibctesting/light_client_test.go @@ -1,6 +1,10 @@ package ibctesting_test import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/testing/simapp" "testing" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" @@ -125,16 +129,119 @@ func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateDoesntExist() { s.Equal(s.hubChain().SenderAccount.GetAddress().String(), seqAddr) } -func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateExists() { - // todo +func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateExists_Compatible() { + s.createRollapp(false, nil) + s.registerSequencer() + s.path = s.newTransferPath(s.hubChain(), s.rollappChain()) + s.coordinator.SetupClients(s.path) + s.NoError(s.path.EndpointA.UpdateClient()) + s.hubApp().LightClientKeeper.SetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID, s.path.EndpointA.ClientID) - // state match - // states dont match + bds := rollapptypes.BlockDescriptors{} + for i := 0; i < 2; i++ { + lastHeader := s.rollappChain().LastHeader + bd := rollapptypes.BlockDescriptor{Height: uint64(lastHeader.Header.Height), StateRoot: lastHeader.Header.AppHash, Timestamp: lastHeader.Header.Time} + bds.BD = append(bds.BD, bd) + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + header, err := s.path.EndpointA.Chain.ConstructUpdateTMClientHeader(s.path.EndpointA.Counterparty.Chain, s.path.EndpointA.ClientID) + + for i := 0; i < 2; i++ { + lastHeader := s.rollappChain().LastHeader + bd := rollapptypes.BlockDescriptor{Height: uint64(lastHeader.Header.Height), StateRoot: lastHeader.Header.AppHash, Timestamp: lastHeader.Header.Time} + bds.BD = append(bds.BD, bd) + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + msgUpdateState := rollapptypes.NewMsgUpdateState( + s.hubChain().SenderAccount.GetAddress().String(), + rollappChainID(), + "mock-da-path", + bds.BD[0].Height, uint64(len(bds.BD)), &bds, + ) + _, err = s.rollappMsgServer().UpdateState(s.hubCtx(), msgUpdateState) + s.NoError(err) + + msg, err := clienttypes.NewMsgUpdateClient( + s.path.EndpointA.ClientID, header, + s.path.EndpointA.Chain.SenderAccount.GetAddress().String(), + ) + s.NoError(err) + + // As there was compatible stateinfo found, should accept the ClientUpdate without any error. + _, err = s.path.EndpointA.Chain.SendMsgs(msg) + s.NoError(err) + s.Equal(uint64(header.Header.Height), s.path.EndpointA.GetClientState().GetLatestHeight().GetRevisionHeight()) + // There shouldnt be any optimistic updates as the roots were verified + _, found := s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, uint64(header.Header.Height)) + s.False(found) } -func (s *lightClientSuite) TestAfterUpdateState_OptimisticUpdateExists() { - // todo +func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateExists_NotCompatible() { + s.createRollapp(false, nil) + s.registerSequencer() + s.path = s.newTransferPath(s.hubChain(), s.rollappChain()) + s.coordinator.SetupClients(s.path) + s.NoError(s.path.EndpointA.UpdateClient()) + s.hubApp().LightClientKeeper.SetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID, s.path.EndpointA.ClientID) + + bds := rollapptypes.BlockDescriptors{} + for i := 0; i < 2; i++ { + lastHeader := s.rollappChain().LastHeader + bd := rollapptypes.BlockDescriptor{Height: uint64(lastHeader.Header.Height), StateRoot: lastHeader.Header.AppHash, Timestamp: lastHeader.Header.Time} + bds.BD = append(bds.BD, bd) + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + header, err := s.path.EndpointA.Chain.ConstructUpdateTMClientHeader(s.path.EndpointA.Counterparty.Chain, s.path.EndpointA.ClientID) + + for i := 0; i < 2; i++ { + lastHeader := s.rollappChain().LastHeader + bd := rollapptypes.BlockDescriptor{Height: uint64(lastHeader.Header.Height), StateRoot: lastHeader.Header.AppHash, Timestamp: lastHeader.Header.Time} + bd.Timestamp = bd.Timestamp.AddDate(0, 0, 1) // wrong timestamp to cause state mismatch + bds.BD = append(bds.BD, bd) + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + msgUpdateState := rollapptypes.NewMsgUpdateState( + s.hubChain().SenderAccount.GetAddress().String(), + rollappChainID(), + "mock-da-path", + bds.BD[0].Height, uint64(len(bds.BD)), &bds, + ) + _, err = s.rollappMsgServer().UpdateState(s.hubCtx(), msgUpdateState) + s.NoError(err) + + msg, err := clienttypes.NewMsgUpdateClient( + s.path.EndpointA.ClientID, header, + s.path.EndpointA.Chain.SenderAccount.GetAddress().String(), + ) + s.NoError(err) + // As there was incompatible stateinfo found, should prevent light client update. + s.path.EndpointA.Chain.Coordinator.UpdateTimeForChain(s.path.EndpointA.Chain) + _, _, err = simapp.SignAndDeliver( // Explicitly submitting msg as we expect it to fail + s.path.EndpointA.Chain.T, + s.path.EndpointA.Chain.TxConfig, + s.path.EndpointA.Chain.App.GetBaseApp(), + s.path.EndpointA.Chain.GetContext().BlockHeader(), + []sdk.Msg{msg}, + s.path.EndpointA.Chain.ChainID, + []uint64{s.path.EndpointA.Chain.SenderAccount.GetAccountNumber()}, + []uint64{s.path.EndpointA.Chain.SenderAccount.GetSequence()}, + true, false, s.path.EndpointA.Chain.SenderPrivKey, + ) + s.Error(err) + s.True(errorsmod.IsOf(err, types.ErrTimestampMismatch)) +} + +func (s *lightClientSuite) TestAfterUpdateState_OptimisticUpdateExists_Compatible() { + // todo // states match - // states dont match - handle fraud +} + +func (s *lightClientSuite) TestAfterUpdateState_OptimisticUpdateExists_NotCompatible() { + // todo + // states dont match - fraud } diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 1de75ef93..3efdc96e8 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -38,8 +38,8 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli } // Check if there are existing block descriptors for the given height of client state - height := header.TrustedHeight - stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()) + height := uint64(header.Header.Height) + stateInfo, err := i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height) if err != nil { // No BDs found for given height. // Will accept the update optimistically @@ -47,9 +47,9 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli i.acceptUpdateOptimistically(ctx, msg.ClientId, header) return nil } - bd, _ := stateInfo.GetBlockDescriptor(height.GetRevisionHeight()) + bd, _ := stateInfo.GetBlockDescriptor(height) - stateInfo, err = i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height.GetRevisionHeight()+1) + stateInfo, err = i.rollappKeeper.FindStateInfoByHeight(ctx, rollappID, height+1) if err != nil { // No BDs found for next height. // Will accept the update optimistically diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index 6c3d9397a..fac47d017 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -78,6 +78,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { signedHeader := &cmtproto.SignedHeader{ Header: &cmtproto.Header{ ValidatorsHash: seqValHash, + Height: 1, }, Commit: &cmtproto.Commit{}, } @@ -146,6 +147,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { ProposerAddress: []byte("sequencerAddr"), Time: time.Unix(1724392989, 0), NextValidatorsHash: []byte("nextValHash"), + Height: 1, }, Commit: &cmtproto.Commit{}, } @@ -235,6 +237,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { Time: blocktimestamp, ValidatorsHash: nextValsHash, NextValidatorsHash: nextValsHash, + Height: 1, }, Commit: &cmtproto.Commit{}, } From 110dc2636da9bc3e368295cf676131217bd5d23c Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:27:56 +0530 Subject: [PATCH 78/87] removing the canonical channel hack and updating tests --- app/keepers/keepers.go | 1 - ibctesting/genesis_transfer_test.go | 5 +- x/rollapp/transfergenesis/ante_decorator.go | 5 ++ .../ibc_module_canonical_channel_hack.go | 56 ------------------- 4 files changed, 9 insertions(+), 58 deletions(-) delete mode 100644 x/rollapp/transfergenesis/ibc_module_canonical_channel_hack.go diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 6452557af..3050eed8d 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -512,7 +512,6 @@ func (a *AppKeepers) InitTransferStack() { ) a.TransferStack = a.delayedAckMiddleware a.TransferStack = transfergenesis.NewIBCModule(a.TransferStack, a.DelayedAckKeeper, *a.RollappKeeper, a.TransferKeeper, a.DenomMetadataKeeper) - a.TransferStack = transfergenesis.NewIBCModuleCanonicalChannelHack(a.TransferStack, *a.RollappKeeper, a.IBCKeeper.ChannelKeeper) // Create static IBC router, add transfer route, then set and seal it ibcRouter := ibcporttypes.NewRouter() diff --git a/ibctesting/genesis_transfer_test.go b/ibctesting/genesis_transfer_test.go index d403e7519..f091f44ca 100644 --- a/ibctesting/genesis_transfer_test.go +++ b/ibctesting/genesis_transfer_test.go @@ -31,10 +31,13 @@ func TestTransferGenesisTestSuite(t *testing.T) { func (s *transferGenesisSuite) SetupTest() { s.utilSuite.SetupTest() path := s.newTransferPath(s.hubChain(), s.rollappChain()) - s.coordinator.Setup(path) + s.coordinator.SetupConnections(path) s.createRollapp(false, nil) // genesis protocol is not finished yet s.registerSequencer() s.path = path + // set the canonical client before creating channels + s.hubApp().LightClientKeeper.SetCanonicalClient(s.hubCtx(), rollappChainID(), s.path.EndpointA.ClientID) + s.coordinator.CreateChannels(path) // set hooks to avoid actually creating VFC contract, as this places extra requirements on the test setup // we assume that if the denom metadata was created (checked below), then the hooks ran correctly diff --git a/x/rollapp/transfergenesis/ante_decorator.go b/x/rollapp/transfergenesis/ante_decorator.go index 543a9911b..963fe3789 100644 --- a/x/rollapp/transfergenesis/ante_decorator.go +++ b/x/rollapp/transfergenesis/ante_decorator.go @@ -7,11 +7,16 @@ import ( "github.com/dymensionxyz/sdk-utils/utils/uibc" transferTypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) type GetRollapp func(ctx sdk.Context, rollappId string) (val types.Rollapp, found bool) +type ChannelKeeper interface { + GetChannelClientState(ctx sdk.Context, portID, channelID string) (string, exported.ClientState, error) // implemented by ibc channel keeper +} + // TransferEnabledDecorator only allows ibc transfers to a rollapp if that rollapp has finished // the transfer genesis protocol. type TransferEnabledDecorator struct { diff --git a/x/rollapp/transfergenesis/ibc_module_canonical_channel_hack.go b/x/rollapp/transfergenesis/ibc_module_canonical_channel_hack.go deleted file mode 100644 index d0ade93c2..000000000 --- a/x/rollapp/transfergenesis/ibc_module_canonical_channel_hack.go +++ /dev/null @@ -1,56 +0,0 @@ -package transfergenesis - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" - "github.com/cosmos/ibc-go/v7/modules/core/exported" - rollappkeeper "github.com/dymensionxyz/dymension/v3/x/rollapp/keeper" - uibc "github.com/dymensionxyz/sdk-utils/utils/uibc" -) - -/* -TODO: this whole file is temporary - Prior to this we relied on the whitelist addr to set the canonical channel, but that is no longer possible - This currently file is a hack (not secure) - The real solution will come in a followup PR - See https://github.com/dymensionxyz/research/issues/242 -*/ - -type ChannelKeeper interface { - GetChannelClientState(ctx sdk.Context, portID, channelID string) (string, exported.ClientState, error) // implemented by ibc channel keeper -} - -type IBCModuleCanonicalChannelHack struct { - porttypes.IBCModule // next one - rollappKeeper rollappkeeper.Keeper - channelKeeper ChannelKeeper -} - -func NewIBCModuleCanonicalChannelHack( - next porttypes.IBCModule, - rollappKeeper rollappkeeper.Keeper, - channelKeeper ChannelKeeper, -) *IBCModuleCanonicalChannelHack { - return &IBCModuleCanonicalChannelHack{IBCModule: next, rollappKeeper: rollappKeeper, channelKeeper: channelKeeper} -} - -func (w IBCModuleCanonicalChannelHack) OnRecvPacket( - ctx sdk.Context, - packet channeltypes.Packet, - relayer sdk.AccAddress, -) exported.Acknowledgement { - l := ctx.Logger().With("module", "hack set canonical channel") - - chainID, err := uibc.ChainIDFromPortChannel(ctx, w.channelKeeper, packet.GetDestPort(), packet.GetDestChannel()) - if err != nil { - return channeltypes.NewErrorAcknowledgement(err) - } - ra, ok := w.rollappKeeper.GetRollapp(ctx, chainID) - if ok && ra.ChannelId == "" { - ra.ChannelId = packet.GetDestChannel() - w.rollappKeeper.SetRollapp(ctx, ra) - l.Info("Set the canonical channel.", "channel id", packet.GetDestChannel()) - } - return w.IBCModule.OnRecvPacket(ctx, packet, relayer) -} From d0cf4917b782f203e07f48bf9a4792b4bafb757f Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:41:49 +0530 Subject: [PATCH 79/87] adding e2e tests for afterstatupdate hook --- ibctesting/light_client_test.go | 110 ++++++++++++++++++-- testutil/keeper/lightclient.go | 3 +- x/lightclient/ante/ibc_msg_update_client.go | 2 +- x/lightclient/keeper/canonical_client.go | 4 - x/lightclient/keeper/hook_listener.go | 4 +- x/lightclient/keeper/keeper.go | 5 + 6 files changed, 115 insertions(+), 13 deletions(-) diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go index 92b119ab2..753b441a1 100644 --- a/ibctesting/light_client_test.go +++ b/ibctesting/light_client_test.go @@ -119,10 +119,9 @@ func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateDoesntExist() { s.rollappChain().NextBlock() } - height := s.path.EndpointA.GetClientState().GetLatestHeight() s.NoError(s.path.EndpointA.UpdateClient()) // As there was no stateinfo found for the height, should have accepted the update optimistically. - seqValHash, found := s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, height.GetRevisionHeight()) + seqValHash, found := s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, s.path.EndpointA.GetClientState().GetLatestHeight().GetRevisionHeight()) s.True(found) seqAddr, err := s.hubApp().LightClientKeeper.GetSequencerFromValHash(s.hubCtx(), seqValHash) s.NoError(err) @@ -237,11 +236,110 @@ func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateExists_NotCompatible() } func (s *lightClientSuite) TestAfterUpdateState_OptimisticUpdateExists_Compatible() { - // todo - // states match + s.createRollapp(false, nil) + s.registerSequencer() + s.path = s.newTransferPath(s.hubChain(), s.rollappChain()) + s.coordinator.SetupClients(s.path) + s.NoError(s.path.EndpointA.UpdateClient()) + s.hubApp().LightClientKeeper.SetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID, s.path.EndpointA.ClientID) + + bds := rollapptypes.BlockDescriptors{} + for i := 0; i < 2; i++ { + lastHeader := s.rollappChain().LastHeader + bd := rollapptypes.BlockDescriptor{Height: uint64(lastHeader.Header.Height), StateRoot: lastHeader.Header.AppHash, Timestamp: lastHeader.Header.Time} + bds.BD = append(bds.BD, bd) + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + header, err := s.path.EndpointA.Chain.ConstructUpdateTMClientHeader(s.path.EndpointA.Counterparty.Chain, s.path.EndpointA.ClientID) + + for i := 0; i < 2; i++ { + lastHeader := s.rollappChain().LastHeader + bd := rollapptypes.BlockDescriptor{Height: uint64(lastHeader.Header.Height), StateRoot: lastHeader.Header.AppHash, Timestamp: lastHeader.Header.Time} + bds.BD = append(bds.BD, bd) + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + + msg, err := clienttypes.NewMsgUpdateClient( + s.path.EndpointA.ClientID, header, + s.path.EndpointA.Chain.SenderAccount.GetAddress().String(), + ) + s.NoError(err) + _, err = s.path.EndpointA.Chain.SendMsgs(msg) + s.NoError(err) + // There should be one optimistic update for the header height + _, found := s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, uint64(header.Header.Height)) + s.True(found) + + msgUpdateState := rollapptypes.NewMsgUpdateState( + s.hubChain().SenderAccount.GetAddress().String(), + rollappChainID(), + "mock-da-path", + bds.BD[0].Height, uint64(len(bds.BD)), &bds, + ) + _, err = s.rollappMsgServer().UpdateState(s.hubCtx(), msgUpdateState) + s.NoError(err) + // The optimistic update valhash should be removed as the state has been confirmed to be compatible + _, found = s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, uint64(header.Header.Height)) + s.False(found) + // Ensuring that the stateinfo is now upto date as well + state, found := s.hubApp().RollappKeeper.GetLatestStateInfo(s.hubCtx(), s.rollappChain().ChainID) + s.True(found) + s.True(state.ContainsHeight(uint64(header.Header.Height))) } func (s *lightClientSuite) TestAfterUpdateState_OptimisticUpdateExists_NotCompatible() { - // todo - // states dont match - fraud + s.createRollapp(false, nil) + s.registerSequencer() + s.path = s.newTransferPath(s.hubChain(), s.rollappChain()) + s.coordinator.SetupConnections(s.path) + s.hubApp().LightClientKeeper.SetCanonicalClient(s.hubCtx(), s.rollappChain().ChainID, s.path.EndpointA.ClientID) + s.coordinator.CreateChannels(s.path) + s.NoError(s.path.EndpointA.UpdateClient()) + + bds := rollapptypes.BlockDescriptors{} + for i := 0; i < 2; i++ { + lastHeader := s.rollappChain().LastHeader + bd := rollapptypes.BlockDescriptor{Height: uint64(lastHeader.Header.Height), StateRoot: lastHeader.Header.AppHash, Timestamp: lastHeader.Header.Time} + bds.BD = append(bds.BD, bd) + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + header, err := s.path.EndpointA.Chain.ConstructUpdateTMClientHeader(s.path.EndpointA.Counterparty.Chain, s.path.EndpointA.ClientID) + + for i := 0; i < 2; i++ { + lastHeader := s.rollappChain().LastHeader + bd := rollapptypes.BlockDescriptor{Height: uint64(lastHeader.Header.Height), StateRoot: lastHeader.Header.AppHash, Timestamp: lastHeader.Header.Time} + bd.Timestamp = bd.Timestamp.AddDate(0, 0, 1) // wrong timestamp to cause state mismatch + bds.BD = append(bds.BD, bd) + s.hubChain().NextBlock() + s.rollappChain().NextBlock() + } + + msg, err := clienttypes.NewMsgUpdateClient( + s.path.EndpointA.ClientID, header, + s.path.EndpointA.Chain.SenderAccount.GetAddress().String(), + ) + s.NoError(err) + _, err = s.path.EndpointA.Chain.SendMsgs(msg) + s.NoError(err) + // There should be one optimistic update for the header height + _, found := s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, uint64(header.Header.Height)) + s.True(found) + + msgUpdateState := rollapptypes.NewMsgUpdateState( + s.hubChain().SenderAccount.GetAddress().String(), + rollappChainID(), + "mock-da-path", + bds.BD[0].Height, uint64(len(bds.BD)), &bds, + ) + _, err = s.rollappMsgServer().UpdateState(s.hubCtx(), msgUpdateState) + s.Error(err) + // The optimistic update valhash should be removed as part of fraud handling + _, found = s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, uint64(header.Header.Height)) + s.False(found) + // Ensuring that the rollapp is now frozen as part of fraud handling + rollapp, _ := s.hubApp().RollappKeeper.GetRollapp(s.hubCtx(), s.rollappChain().ChainID) + s.True(rollapp.Frozen) } diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index da6656766..d6934e4a7 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -111,7 +111,8 @@ func (m *MockIBCCLientKeeper) GetClientConsensusState(ctx sdk.Context, clientID } func (m *MockIBCCLientKeeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { - return nil, false + cs, ok := m.clientStates[clientID] + return cs, ok } func (m *MockIBCCLientKeeper) IterateClientStates(ctx sdk.Context, prefix []byte, cb func(clientID string, cs exported.ClientState) bool) { diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 3efdc96e8..09ff75c66 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -76,5 +76,5 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli } func (i IBCMessagesDecorator) acceptUpdateOptimistically(ctx sdk.Context, clientID string, header *ibctm.Header) { - i.lightClientKeeper.SetConsensusStateValHash(ctx, clientID, header.TrustedHeight.RevisionHeight, header.Header.ValidatorsHash) + i.lightClientKeeper.SetConsensusStateValHash(ctx, clientID, uint64(header.Header.Height), header.Header.ValidatorsHash) } diff --git a/x/lightclient/keeper/canonical_client.go b/x/lightclient/keeper/canonical_client.go index bb1081961..2f859fd73 100644 --- a/x/lightclient/keeper/canonical_client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -12,10 +12,6 @@ import ( rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) -const ( - ibcRevisionNumber = 1 -) - // 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. diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 0c06d8bc6..e59164a7a 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -76,7 +76,8 @@ func (hook rollappHook) AfterUpdateState( } func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, bd rollapptypes.BlockDescriptor, canonicalClient string, sequencerPk tmprotocrypto.PublicKey, blockValHash []byte) error { - height := ibcclienttypes.NewHeight(ibcRevisionNumber, bd.GetHeight()) + cs, _ := hook.k.ibcClientKeeper.GetClientState(ctx, canonicalClient) + height := ibcclienttypes.NewHeight(cs.GetLatestHeight().GetRevisionNumber(), bd.GetHeight()) consensusState, _ := hook.k.ibcClientKeeper.GetClientConsensusState(ctx, canonicalClient, height) // Cast consensus state to tendermint consensus state - we need this to check the state root and timestamp and nextValHash tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) @@ -106,5 +107,6 @@ func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, b return err } } + hook.k.RemoveConsensusStateValHash(ctx, canonicalClient, bd.GetHeight()) return nil } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index 2759ffc49..da39c8664 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -79,6 +79,11 @@ func (k Keeper) SetConsensusStateValHash(ctx sdk.Context, clientID string, heigh store.Set(types.ConsensusStateValhashKeyByClientID(clientID, height), blockValHash) } +func (k Keeper) RemoveConsensusStateValHash(ctx sdk.Context, clientID string, height uint64) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ConsensusStateValhashKeyByClientID(clientID, height)) +} + // GetConsensusStateValHash returns the block valHash for the given height of the client func (k Keeper) GetConsensusStateValHash(ctx sdk.Context, clientID string, height uint64) ([]byte, bool) { store := ctx.KVStore(k.storeKey) From 219dad2e93cee5938f31dabd69b243ba50482ac4 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:43:18 +0530 Subject: [PATCH 80/87] =?UTF-8?q?linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ibctesting/light_client_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go index 753b441a1..816fcb538 100644 --- a/ibctesting/light_client_test.go +++ b/ibctesting/light_client_test.go @@ -1,11 +1,12 @@ package ibctesting_test import ( + "testing" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/cosmos/ibc-go/v7/testing/simapp" - "testing" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" From af10515ad9ea8e726128684e92c0209d084300b5 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:02:08 +0530 Subject: [PATCH 81/87] =?UTF-8?q?linting=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ibctesting/light_client_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go index 816fcb538..7d9df63a1 100644 --- a/ibctesting/light_client_test.go +++ b/ibctesting/light_client_test.go @@ -146,6 +146,7 @@ func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateExists_Compatible() { s.rollappChain().NextBlock() } header, err := s.path.EndpointA.Chain.ConstructUpdateTMClientHeader(s.path.EndpointA.Counterparty.Chain, s.path.EndpointA.ClientID) + s.NoError(err) for i := 0; i < 2; i++ { lastHeader := s.rollappChain().LastHeader @@ -195,6 +196,7 @@ func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateExists_NotCompatible() s.rollappChain().NextBlock() } header, err := s.path.EndpointA.Chain.ConstructUpdateTMClientHeader(s.path.EndpointA.Counterparty.Chain, s.path.EndpointA.ClientID) + s.NoError(err) for i := 0; i < 2; i++ { lastHeader := s.rollappChain().LastHeader @@ -253,6 +255,7 @@ func (s *lightClientSuite) TestAfterUpdateState_OptimisticUpdateExists_Compatibl s.rollappChain().NextBlock() } header, err := s.path.EndpointA.Chain.ConstructUpdateTMClientHeader(s.path.EndpointA.Counterparty.Chain, s.path.EndpointA.ClientID) + s.NoError(err) for i := 0; i < 2; i++ { lastHeader := s.rollappChain().LastHeader @@ -308,6 +311,7 @@ func (s *lightClientSuite) TestAfterUpdateState_OptimisticUpdateExists_NotCompat s.rollappChain().NextBlock() } header, err := s.path.EndpointA.Chain.ConstructUpdateTMClientHeader(s.path.EndpointA.Counterparty.Chain, s.path.EndpointA.ClientID) + s.NoError(err) for i := 0; i < 2; i++ { lastHeader := s.rollappChain().LastHeader From a1f6b2e116a93719250a35412efc9c434330c164 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:16:28 +0530 Subject: [PATCH 82/87] using ibctm.ConsensusState instead of custom ibc state type --- x/lightclient/ante/ibc_msg_update_client.go | 7 +------ x/lightclient/keeper/canonical_client.go | 9 +-------- x/lightclient/keeper/hook_listener.go | 9 +-------- x/lightclient/types/state.go | 15 +++------------ x/lightclient/types/state_test.go | 8 +++++--- 5 files changed, 11 insertions(+), 37 deletions(-) diff --git a/x/lightclient/ante/ibc_msg_update_client.go b/x/lightclient/ante/ibc_msg_update_client.go index 09ff75c66..5caf22e73 100644 --- a/x/lightclient/ante/ibc_msg_update_client.go +++ b/x/lightclient/ante/ibc_msg_update_client.go @@ -31,11 +31,6 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli if !ok { return nil } - ibcState := types.IBCState{ - Root: header.Header.GetAppHash(), - NextValidatorsHash: header.Header.NextValidatorsHash, - Timestamp: header.Header.Time, - } // Check if there are existing block descriptors for the given height of client state height := uint64(header.Header.Height) @@ -67,7 +62,7 @@ func (i IBCMessagesDecorator) HandleMsgUpdateClient(ctx sdk.Context, msg *ibccli } // Ensure that the ibc header is compatible with the existing rollapp state // If it's not, we error and prevent the MsgUpdateClient from being processed - err = types.CheckCompatibility(ibcState, rollappState) + err = types.CheckCompatibility(*header.ConsensusState(), rollappState) if err != nil { return err } diff --git a/x/lightclient/keeper/canonical_client.go b/x/lightclient/keeper/canonical_client.go index 2f859fd73..eef96a2ac 100644 --- a/x/lightclient/keeper/canonical_client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -1,8 +1,6 @@ package keeper import ( - "time" - 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" @@ -83,11 +81,6 @@ func (k Keeper) isValidClient(ctx sdk.Context, clientID string, cs exported.Clie } consensusState, _ := k.ibcClientKeeper.GetClientConsensusState(ctx, clientID, consensusHeight) tmConsensusState, _ := consensusState.(*ibctm.ConsensusState) - ibcState := types.IBCState{ - Root: tmConsensusState.GetRoot().GetHash(), - NextValidatorsHash: tmConsensusState.NextValidatorsHash, - Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), - } var rollappState types.RollappState bd, found := stateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) @@ -112,7 +105,7 @@ func (k Keeper) isValidClient(ctx sdk.Context, clientID string, cs exported.Clie rollappState.BlockDescriptor = bd rollappState.NextBlockSequencer = oldSequencer } - err := types.CheckCompatibility(ibcState, rollappState) + err := types.CheckCompatibility(*tmConsensusState, rollappState) if err != nil { return false } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index e59164a7a..f827f8270 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -1,8 +1,6 @@ package keeper import ( - "time" - tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" @@ -84,16 +82,11 @@ func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, b if !ok { return nil } - ibcState := types.IBCState{ - Root: tmConsensusState.GetRoot().GetHash(), - NextValidatorsHash: tmConsensusState.NextValidatorsHash, - Timestamp: time.Unix(0, int64(tmConsensusState.GetTimestamp())), - } rollappState := types.RollappState{ BlockDescriptor: bd, NextBlockSequencer: sequencerPk, } - err := types.CheckCompatibility(ibcState, rollappState) + err := types.CheckCompatibility(*tmConsensusState, rollappState) if err != nil { // If the state is not compatible, // Take this state update as source of truth over the IBC update diff --git a/x/lightclient/types/state.go b/x/lightclient/types/state.go index 8e167f82f..5f5a850d5 100644 --- a/x/lightclient/types/state.go +++ b/x/lightclient/types/state.go @@ -3,12 +3,12 @@ package types import ( "bytes" "errors" - "time" errorsmod "cosmossdk.io/errors" abci "github.com/cometbft/cometbft/abci/types" tmprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" cmttypes "github.com/cometbft/cometbft/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) @@ -18,9 +18,9 @@ import ( // 1. The app root shared by the IBC consensus state matches the block descriptor state root for the same height // 2. The next validator hash shared by the IBC consensus state matches the sequencer hash for the next block descriptor // 3. The block descriptor timestamp matches the tendermint header timestamp (only if timestamp exists for the block descriptor) -func CheckCompatibility(ibcState IBCState, raState RollappState) error { +func CheckCompatibility(ibcState ibctm.ConsensusState, raState RollappState) error { // Check if block descriptor state root matches IBC block header app hash - if !bytes.Equal(ibcState.Root, raState.BlockDescriptor.StateRoot) { + if !bytes.Equal(ibcState.Root.GetHash(), raState.BlockDescriptor.StateRoot) { return errorsmod.Wrap(ErrStateRootsMismatch, "block descriptor state root does not match tendermint header app hash") } // Check if the nextValidatorHash matches for the sequencer for h+1 block descriptor @@ -52,15 +52,6 @@ func GetValHashForSequencer(sequencerTmPubKey tmprotocrypto.PublicKey) ([]byte, return nextValSet.Hash(), nil } -type IBCState struct { - // Root is the app root shared by the IBC consensus state - Root []byte - // NextValidatorsHash is the hash of the next validator set for the next block - NextValidatorsHash []byte - // Timestamp is the block timestamp of the header - Timestamp time.Time -} - type RollappState struct { // BlockDescriptor is the block descriptor for the required height BlockDescriptor rollapptypes.BlockDescriptor diff --git a/x/lightclient/types/state_test.go b/x/lightclient/types/state_test.go index ab517464d..9c4cefa83 100644 --- a/x/lightclient/types/state_test.go +++ b/x/lightclient/types/state_test.go @@ -6,6 +6,8 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + commitmenttypes "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" "github.com/stretchr/testify/require" @@ -17,8 +19,8 @@ var ( valHash, _ = types.GetValHashForSequencer(tmPk) timestamp = time.Unix(1724392989, 0) - validIBCState = types.IBCState{ - Root: []byte("root"), + validIBCState = ibctm.ConsensusState{ + Root: commitmenttypes.NewMerkleRoot([]byte("root")), Timestamp: timestamp, NextValidatorsHash: valHash, } @@ -33,7 +35,7 @@ var ( func TestCheckCompatibility(t *testing.T) { type input struct { - ibcState types.IBCState + ibcState ibctm.ConsensusState raState types.RollappState } testCases := []struct { From 6229eca50546ee41ab658d7d96813b5a371eea3a Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:33:08 +0530 Subject: [PATCH 83/87] add check to ensure if first stateupdate, timestamps exist --- app/apptesting/test_suite.go | 3 +- ibctesting/utils_test.go | 2 ++ x/rollapp/keeper/msg_server_update_state.go | 5 ++++ .../keeper/msg_server_update_state_test.go | 28 +++++++++++++++---- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/app/apptesting/test_suite.go b/app/apptesting/test_suite.go index a5b06a9ad..a42ee0d64 100644 --- a/app/apptesting/test_suite.go +++ b/app/apptesting/test_suite.go @@ -2,6 +2,7 @@ package apptesting import ( "strings" + "time" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" @@ -109,7 +110,7 @@ func (s *KeeperTestHelper) PostStateUpdate(ctx sdk.Context, rollappId, seqAddr s var bds rollapptypes.BlockDescriptors bds.BD = make([]rollapptypes.BlockDescriptor, numOfBlocks) for k := 0; k < int(numOfBlocks); k++ { - bds.BD[k] = rollapptypes.BlockDescriptor{Height: startHeight + uint64(k)} + bds.BD[k] = rollapptypes.BlockDescriptor{Height: startHeight + uint64(k), Timestamp: time.Now().UTC()} } updateState := rollapptypes.MsgUpdateState{ diff --git a/ibctesting/utils_test.go b/ibctesting/utils_test.go index 15ec530b7..929bde634 100644 --- a/ibctesting/utils_test.go +++ b/ibctesting/utils_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "strings" "testing" + "time" tmrand "github.com/cometbft/cometbft/libs/rand" @@ -198,6 +199,7 @@ func (s *utilSuite) updateRollappState(endHeight uint64) { blockDescriptors.BD[i] = rollapptypes.BlockDescriptor{ Height: startHeight + uint64(i), StateRoot: bytes.Repeat([]byte{byte(startHeight) + byte(i)}, 32), + Timestamp: time.Now().UTC(), } } // Update the state diff --git a/x/rollapp/keeper/msg_server_update_state.go b/x/rollapp/keeper/msg_server_update_state.go index 737db74a2..f6f5e9698 100644 --- a/x/rollapp/keeper/msg_server_update_state.go +++ b/x/rollapp/keeper/msg_server_update_state.go @@ -61,6 +61,11 @@ func (k msgServer) UpdateState(goCtx context.Context, msg *types.MsgUpdateState) // bump state index lastIndex = latestStateInfoIndex.Index + } else { + err := msg.BDs.Validate() + if err != nil { + return nil, err + } } newIndex = lastIndex + 1 diff --git a/x/rollapp/keeper/msg_server_update_state_test.go b/x/rollapp/keeper/msg_server_update_state_test.go index 078505d2a..f6992750a 100644 --- a/x/rollapp/keeper/msg_server_update_state_test.go +++ b/x/rollapp/keeper/msg_server_update_state_test.go @@ -236,23 +236,39 @@ func (suite *RollappTestSuite) TestUpdateStateErrNotActiveSequencer() { func (suite *RollappTestSuite) TestUpdateStateDowngradeTimestamp() { rollappId, proposer := suite.CreateDefaultRollappAndProposer() // update state without timestamp - _, err := suite.PostStateUpdate(suite.Ctx, rollappId, proposer, 1, uint64(3)) - suite.NoError(err) + stateInfo := types.StateInfo{ + StateInfoIndex: types.StateInfoIndex{RollappId: rollappId, Index: 1}, + Sequencer: proposer, + StartHeight: 1, + NumBlocks: 1, + DAPath: "", + BDs: types.BlockDescriptors{BD: []types.BlockDescriptor{{Height: 1}}}, + } + suite.App.RollappKeeper.SetLatestStateInfoIndex(suite.Ctx, stateInfo.StateInfoIndex) + suite.App.RollappKeeper.SetStateInfo(suite.Ctx, stateInfo) // update state with timestamp - this "upgrades" the rollapp such that all new state updates must have timestamp in BD updateState := types.MsgUpdateState{ Creator: proposer, RollappId: rollappId, - StartHeight: 4, + StartHeight: 2, NumBlocks: 1, DAPath: "", - BDs: types.BlockDescriptors{BD: []types.BlockDescriptor{{Height: 4, Timestamp: time.Now().UTC()}}}, + BDs: types.BlockDescriptors{BD: []types.BlockDescriptor{{Height: 2, Timestamp: time.Now().UTC()}}}, } - _, err = suite.msgServer.UpdateState(suite.Ctx, &updateState) + _, err := suite.msgServer.UpdateState(suite.Ctx, &updateState) suite.NoError(err) // update state without timestamp - _, err = suite.PostStateUpdate(suite.Ctx, rollappId, proposer, 5, uint64(1)) + updateState = types.MsgUpdateState{ + Creator: proposer, + RollappId: rollappId, + StartHeight: 3, + NumBlocks: 1, + DAPath: "", + BDs: types.BlockDescriptors{BD: []types.BlockDescriptor{{Height: 3}}}, + } + _, err = suite.msgServer.UpdateState(suite.Ctx, &updateState) suite.ErrorIs(err, types.ErrInvalidBlockDescriptorTimestamp) } From 5659bfb39503769324faab9aa248511017359732 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 17:45:11 +0530 Subject: [PATCH 84/87] fixing the issue in canonical client setting where it was using wrong sequencer + refactor + remove the sestcanonicalclient hook test as ibctest for the same is more comprehensive --- x/lightclient/keeper/canonical_client.go | 54 +++++++++------------- x/lightclient/keeper/hook_listener.go | 2 +- x/lightclient/keeper/hook_listener_test.go | 35 -------------- 3 files changed, 24 insertions(+), 67 deletions(-) diff --git a/x/lightclient/keeper/canonical_client.go b/x/lightclient/keeper/canonical_client.go index eef96a2ac..e6cc86204 100644 --- a/x/lightclient/keeper/canonical_client.go +++ b/x/lightclient/keeper/canonical_client.go @@ -7,7 +7,6 @@ import ( "github.com/cosmos/ibc-go/v7/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" "github.com/dymensionxyz/dymension/v3/x/lightclient/types" - rollapptypes "github.com/dymensionxyz/dymension/v3/x/rollapp/types" ) // GetProspectiveCanonicalClient returns the client id of the first IBC client which can be set as the canonical client for the given rollapp. @@ -15,9 +14,9 @@ import ( // 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, stateInfo *rollapptypes.StateInfo) (clientID string, stateCompatible bool) { +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 { - ok := k.isValidClient(ctx, client, cs, rollappId, stateInfo) + ok := k.isValidClient(ctx, client, cs, rollappId, maxHeight) if ok { clientID = client stateCompatible = true @@ -56,7 +55,7 @@ func (k Keeper) GetAllCanonicalClients(ctx sdk.Context) (clients []types.Canonic return } -func (k Keeper) isValidClient(ctx sdk.Context, clientID string, cs exported.ClientState, rollappId string, stateInfo *rollapptypes.StateInfo) bool { +func (k Keeper) isValidClient(ctx sdk.Context, clientID string, cs exported.ClientState, rollappId string, maxHeight uint64) bool { tmClientState, ok := cs.(*ibctm.ClientState) if !ok { return false @@ -67,7 +66,6 @@ func (k Keeper) isValidClient(ctx sdk.Context, clientID string, cs exported.Clie if !types.IsCanonicalClientParamsValid(tmClientState) { return false } - maxHeight := stateInfo.GetLatestHeight() - 1 res, err := k.ibcClientKeeper.ConsensusStateHeights(ctx, &ibcclienttypes.QueryConsensusStateHeightsRequest{ ClientId: clientID, Pagination: &query.PageRequest{Limit: maxHeight}, @@ -76,36 +74,30 @@ func (k Keeper) isValidClient(ctx sdk.Context, clientID string, cs exported.Clie return false } for _, consensusHeight := range res.ConsensusStateHeights { - if consensusHeight.GetRevisionHeight() > maxHeight { - continue + h := consensusHeight.GetRevisionHeight() + if maxHeight < h { + break } consensusState, _ := k.ibcClientKeeper.GetClientConsensusState(ctx, clientID, consensusHeight) tmConsensusState, _ := consensusState.(*ibctm.ConsensusState) - - var rollappState types.RollappState - bd, found := stateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) - if found { - rollappState.BlockDescriptor = bd - sequencerPk, err := k.GetSequencerPubKey(ctx, stateInfo.Sequencer) - if err != nil { - return false - } - rollappState.NextBlockSequencer = sequencerPk - } else { - // Look up the state info for the block descriptor - oldStateInfo, err := k.rollappKeeper.FindStateInfoByHeight(ctx, rollappId, consensusHeight.GetRevisionHeight()) - if err != nil { - return false - } - bd, _ = oldStateInfo.GetBlockDescriptor(consensusHeight.GetRevisionHeight()) - oldSequencer, err := k.GetSequencerPubKey(ctx, oldStateInfo.Sequencer) - if err != nil { - return false - } - rollappState.BlockDescriptor = bd - rollappState.NextBlockSequencer = oldSequencer + stateInfoH, err := k.rollappKeeper.FindStateInfoByHeight(ctx, rollappId, h) + if err != nil { + return false + } + stateInfoHplus1, err := k.rollappKeeper.FindStateInfoByHeight(ctx, rollappId, h+1) + if err != nil { + return false + } + bd, _ := stateInfoH.GetBlockDescriptor(h) + oldSequencer, err := k.GetSequencerPubKey(ctx, stateInfoHplus1.Sequencer) + if err != nil { + return false + } + rollappState := types.RollappState{ + BlockDescriptor: bd, + NextBlockSequencer: oldSequencer, } - err := types.CheckCompatibility(*tmConsensusState, rollappState) + err = types.CheckCompatibility(*tmConsensusState, rollappState) if err != nil { return false } diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index f827f8270..ef0fcf2ac 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -33,7 +33,7 @@ func (hook rollappHook) AfterUpdateState( ) error { canonicalClient, found := hook.k.GetCanonicalClient(ctx, rollappId) if !found { - canonicalClient, foundClient := hook.k.GetProspectiveCanonicalClient(ctx, rollappId, stateInfo) + canonicalClient, foundClient := hook.k.GetProspectiveCanonicalClient(ctx, rollappId, stateInfo.GetLatestHeight()) if foundClient { hook.k.SetCanonicalClient(ctx, rollappId, canonicalClient) } diff --git a/x/lightclient/keeper/hook_listener_test.go b/x/lightclient/keeper/hook_listener_test.go index ebc3de56e..b6386d338 100644 --- a/x/lightclient/keeper/hook_listener_test.go +++ b/x/lightclient/keeper/hook_listener_test.go @@ -144,38 +144,3 @@ func TestAfterUpdateState(t *testing.T) { }) } } - -func TestAfterUpdateState_SetCanonicalClient(t *testing.T) { - keeper, ctx := keepertest.LightClientKeeper(t) - rollappId := "rollapp-wants-canon-client" - stateInfo := &rollapptypes.StateInfo{ - Sequencer: keepertest.Alice, - StartHeight: 1, - NumBlocks: 3, - BDs: rollapptypes.BlockDescriptors{ - BD: []rollapptypes.BlockDescriptor{ - { - Height: 1, - StateRoot: []byte("test"), - Timestamp: time.Unix(1724392989, 0), - }, - { - Height: 2, - StateRoot: []byte("test2"), - Timestamp: time.Unix(1724392989, 0), - }, - { - Height: 3, - StateRoot: []byte("test3"), - Timestamp: time.Unix(1724392989, 0).UTC(), - }, - }, - }, - } - err := keeper.RollappHooks().AfterUpdateState(ctx, rollappId, stateInfo) - require.NoError(t, err) - - clientID, found := keeper.GetCanonicalClient(ctx, rollappId) - require.True(t, found) - require.Equal(t, "canon-client-id", clientID) -} From cef5693e888d677de7c0a84594051250d4276253 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 17:50:02 +0530 Subject: [PATCH 85/87] using GetSequencerbyRollapp instead of GetAllSequencers --- ibctesting/light_client_test.go | 2 +- testutil/keeper/lightclient.go | 2 +- x/lightclient/ante/ibc_msg_update_client_test.go | 2 +- x/lightclient/keeper/hook_listener.go | 2 +- x/lightclient/keeper/keeper.go | 4 ++-- x/lightclient/types/expected_keepers.go | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ibctesting/light_client_test.go b/ibctesting/light_client_test.go index 7d9df63a1..b89a2c33b 100644 --- a/ibctesting/light_client_test.go +++ b/ibctesting/light_client_test.go @@ -124,7 +124,7 @@ func (s *lightClientSuite) TestMsgUpdateClient_StateUpdateDoesntExist() { // As there was no stateinfo found for the height, should have accepted the update optimistically. seqValHash, found := s.hubApp().LightClientKeeper.GetConsensusStateValHash(s.hubCtx(), s.path.EndpointA.ClientID, s.path.EndpointA.GetClientState().GetLatestHeight().GetRevisionHeight()) s.True(found) - seqAddr, err := s.hubApp().LightClientKeeper.GetSequencerFromValHash(s.hubCtx(), seqValHash) + seqAddr, err := s.hubApp().LightClientKeeper.GetSequencerFromValHash(s.hubCtx(), s.rollappChain().ChainID, seqValHash) s.NoError(err) s.Equal(s.hubChain().SenderAccount.GetAddress().String(), seqAddr) } diff --git a/testutil/keeper/lightclient.go b/testutil/keeper/lightclient.go index d6934e4a7..fe68aa265 100644 --- a/testutil/keeper/lightclient.go +++ b/testutil/keeper/lightclient.go @@ -147,7 +147,7 @@ func (m *MockSequencerKeeper) GetSequencer(ctx sdk.Context, seqAddr string) (seq return seq, ok } -func (m *MockSequencerKeeper) GetAllSequencers(ctx sdk.Context) []sequencertypes.Sequencer { +func (m *MockSequencerKeeper) GetSequencersByRollapp(ctx sdk.Context, rollappId string) (list []sequencertypes.Sequencer) { seqs := make([]sequencertypes.Sequencer, 0, len(m.sequencers)) for _, seq := range m.sequencers { seqs = append(seqs, seq) diff --git a/x/lightclient/ante/ibc_msg_update_client_test.go b/x/lightclient/ante/ibc_msg_update_client_test.go index fac47d017..c17e3e67c 100644 --- a/x/lightclient/ante/ibc_msg_update_client_test.go +++ b/x/lightclient/ante/ibc_msg_update_client_test.go @@ -128,7 +128,7 @@ func TestHandleMsgUpdateClient(t *testing.T) { require.NoError(t, err) seqValHash, found := k.GetConsensusStateValHash(ctx, "canon-client-id", 1) require.True(t, found) - seq, err := k.GetSequencerFromValHash(ctx, seqValHash) + seq, err := k.GetSequencerFromValHash(ctx, "rollapp-has-canon-client", seqValHash) require.NoError(t, err) require.Equal(t, keepertest.Alice, seq) }, diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index ef0fcf2ac..4056197b5 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -91,7 +91,7 @@ func (hook rollappHook) checkStateForHeight(ctx sdk.Context, rollappId string, b // If the state is not compatible, // Take this state update as source of truth over the IBC update // Punish the block proposer of the IBC signed header - sequencerAddress, err := hook.k.GetSequencerFromValHash(ctx, blockValHash) + sequencerAddress, err := hook.k.GetSequencerFromValHash(ctx, rollappId, blockValHash) if err != nil { return err } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index da39c8664..bede2d225 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -59,8 +59,8 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } -func (k Keeper) GetSequencerFromValHash(ctx sdk.Context, blockValHash []byte) (string, error) { - sequencerList := k.sequencerKeeper.GetAllSequencers(ctx) +func (k Keeper) GetSequencerFromValHash(ctx sdk.Context, rollappID string, blockValHash []byte) (string, error) { + sequencerList := k.sequencerKeeper.GetSequencersByRollapp(ctx, rollappID) for _, seq := range sequencerList { seqHash, err := seq.GetDymintPubKeyHash() if err != nil { diff --git a/x/lightclient/types/expected_keepers.go b/x/lightclient/types/expected_keepers.go index 79ac41103..a014f7e99 100644 --- a/x/lightclient/types/expected_keepers.go +++ b/x/lightclient/types/expected_keepers.go @@ -13,7 +13,7 @@ import ( type SequencerKeeperExpected interface { GetSequencer(ctx sdk.Context, sequencerAddress string) (val sequencertypes.Sequencer, found bool) - GetAllSequencers(ctx sdk.Context) (list []sequencertypes.Sequencer) + GetSequencersByRollapp(ctx sdk.Context, rollappId string) (list []sequencertypes.Sequencer) } type RollappKeeperExpected interface { From ce42631f9e32cc9104b1c967654dba13a714d581 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 17:58:46 +0530 Subject: [PATCH 86/87] update the genesis field from signer to blockvalhash --- .../dymension/lightclient/genesis.proto | 4 +- x/lightclient/keeper/genesis.go | 2 +- x/lightclient/keeper/genesis_test.go | 8 +-- x/lightclient/keeper/keeper.go | 6 +- x/lightclient/types/genesis.go | 2 +- x/lightclient/types/genesis.pb.go | 69 ++++++++++--------- x/lightclient/types/genesis_test.go | 10 +-- 7 files changed, 51 insertions(+), 50 deletions(-) diff --git a/proto/dymensionxyz/dymension/lightclient/genesis.proto b/proto/dymensionxyz/dymension/lightclient/genesis.proto index b7344c921..308a1ef4c 100644 --- a/proto/dymensionxyz/dymension/lightclient/genesis.proto +++ b/proto/dymensionxyz/dymension/lightclient/genesis.proto @@ -20,6 +20,6 @@ message ConsensusStateSigner { string ibc_client_id = 1; // height is the client height which was updated optimistically uint64 height = 2; - // signer is the bech32 address of the block proposer which signed the tm header - string signer = 3; + // blockValHash is the valhash of the block which was updated optimistically + string blockValHash = 3; } \ No newline at end of file diff --git a/x/lightclient/keeper/genesis.go b/x/lightclient/keeper/genesis.go index 1f8fc9e71..19b4f5260 100644 --- a/x/lightclient/keeper/genesis.go +++ b/x/lightclient/keeper/genesis.go @@ -13,7 +13,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genesisState types.GenesisState) { k.SetCanonicalClient(ctx, client.RollappId, client.IbcClientId) } for _, stateSigner := range genesisState.GetConsensusStateSigners() { - k.SetConsensusStateValHash(ctx, stateSigner.IbcClientId, stateSigner.Height, []byte(stateSigner.Signer)) + k.SetConsensusStateValHash(ctx, stateSigner.IbcClientId, stateSigner.Height, []byte(stateSigner.BlockValHash)) } } diff --git a/x/lightclient/keeper/genesis_test.go b/x/lightclient/keeper/genesis_test.go index 000f599cf..657c96814 100644 --- a/x/lightclient/keeper/genesis_test.go +++ b/x/lightclient/keeper/genesis_test.go @@ -15,8 +15,8 @@ func TestInitGenesis(t *testing.T) { {RollappId: "rollapp-2", IbcClientId: "client-2"}, } stateSigners := []types.ConsensusStateSigner{ - {IbcClientId: "client-1", Height: 1, Signer: "signer-1"}, - {IbcClientId: "client-1", Height: 2, Signer: "signer-1"}, + {IbcClientId: "client-1", Height: 1, BlockValHash: "signer-1"}, + {IbcClientId: "client-1", Height: 2, BlockValHash: "signer-1"}, } keeper.InitGenesis(ctx, types.GenesisState{ @@ -59,6 +59,6 @@ func TestExportGenesis(t *testing.T) { require.Equal(t, "client-1", genesis.ConsensusStateSigners[1].IbcClientId) require.Equal(t, uint64(1), genesis.ConsensusStateSigners[0].Height) require.Equal(t, uint64(2), genesis.ConsensusStateSigners[1].Height) - require.Equal(t, "signer-1", genesis.ConsensusStateSigners[0].Signer) - require.Equal(t, "signer-1", genesis.ConsensusStateSigners[1].Signer) + require.Equal(t, "signer-1", genesis.ConsensusStateSigners[0].BlockValHash) + require.Equal(t, "signer-1", genesis.ConsensusStateSigners[1].BlockValHash) } diff --git a/x/lightclient/keeper/keeper.go b/x/lightclient/keeper/keeper.go index bede2d225..eeecadafa 100644 --- a/x/lightclient/keeper/keeper.go +++ b/x/lightclient/keeper/keeper.go @@ -102,9 +102,9 @@ func (k Keeper) GetAllConsensusStateSigners(ctx sdk.Context) (signers []types.Co key := iterator.Key() clientID, height := types.ParseConsensusStateValhashKey(key) signers = append(signers, types.ConsensusStateSigner{ - IbcClientId: clientID, - Height: height, - Signer: string(iterator.Value()), + IbcClientId: clientID, + Height: height, + BlockValHash: string(iterator.Value()), }) } return diff --git a/x/lightclient/types/genesis.go b/x/lightclient/types/genesis.go index 5da1a4270..dc655184f 100644 --- a/x/lightclient/types/genesis.go +++ b/x/lightclient/types/genesis.go @@ -25,7 +25,7 @@ func (g GenesisState) Validate() error { if stateSigner.Height == 0 { return fmt.Errorf("invalid height: %v", stateSigner) } - if stateSigner.Signer == "" { + if stateSigner.BlockValHash == "" { return fmt.Errorf("invalid signer: %v", stateSigner) } } diff --git a/x/lightclient/types/genesis.pb.go b/x/lightclient/types/genesis.pb.go index e04a435c9..bac2bb4eb 100644 --- a/x/lightclient/types/genesis.pb.go +++ b/x/lightclient/types/genesis.pb.go @@ -132,8 +132,8 @@ type ConsensusStateSigner struct { IbcClientId string `protobuf:"bytes,1,opt,name=ibc_client_id,json=ibcClientId,proto3" json:"ibc_client_id,omitempty"` // height is the client height which was updated optimistically Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` - // signer is the bech32 address of the block proposer which signed the tm header - Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` + // blockValHash is the valhash of the block which was updated optimistically + BlockValHash string `protobuf:"bytes,3,opt,name=blockValHash,proto3" json:"blockValHash,omitempty"` } func (m *ConsensusStateSigner) Reset() { *m = ConsensusStateSigner{} } @@ -183,9 +183,9 @@ func (m *ConsensusStateSigner) GetHeight() uint64 { return 0 } -func (m *ConsensusStateSigner) GetSigner() string { +func (m *ConsensusStateSigner) GetBlockValHash() string { if m != nil { - return m.Signer + return m.BlockValHash } return "" } @@ -201,29 +201,30 @@ func init() { } var fileDescriptor_5520440548912168 = []byte{ - // 343 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4e, 0xfa, 0x40, - 0x10, 0xc6, 0xbb, 0x40, 0x48, 0x58, 0xfe, 0xff, 0xa8, 0x0d, 0x6a, 0x63, 0x62, 0x25, 0x3d, 0x71, - 0x6a, 0x8d, 0x5c, 0x38, 0xc3, 0xc1, 0x70, 0x2d, 0x9e, 0xbc, 0x34, 0xed, 0x76, 0x5d, 0xd6, 0x94, - 0xdd, 0x86, 0x59, 0x08, 0xf8, 0x14, 0x3e, 0x16, 0x47, 0x8e, 0x9e, 0x8c, 0x81, 0xbb, 0xcf, 0x60, - 0xba, 0x2d, 0x04, 0x04, 0xa3, 0xb7, 0x7e, 0xd3, 0xf9, 0x7d, 0xdf, 0xec, 0x64, 0xf0, 0x6d, 0x3c, - 0x1f, 0x51, 0x01, 0x5c, 0x8a, 0xd9, 0xfc, 0xc5, 0xdb, 0x0a, 0x2f, 0xe1, 0x6c, 0xa8, 0x48, 0xc2, - 0xa9, 0x50, 0x1e, 0xa3, 0x82, 0x02, 0x07, 0x37, 0x1d, 0x4b, 0x25, 0x4d, 0x67, 0x97, 0x70, 0xb7, - 0xc2, 0xdd, 0x21, 0xae, 0x1a, 0x4c, 0x32, 0xa9, 0xdb, 0xbd, 0xec, 0x2b, 0x27, 0x9d, 0x4f, 0x84, - 0xff, 0xdd, 0xe7, 0x5e, 0x03, 0x15, 0x2a, 0x6a, 0x3e, 0xe1, 0x33, 0x12, 0x0a, 0x29, 0x38, 0x09, - 0x93, 0x20, 0x47, 0xc1, 0x42, 0xcd, 0x72, 0xab, 0x7e, 0xd7, 0x76, 0x7f, 0x8f, 0x71, 0x7b, 0x1b, - 0xb8, 0xa7, 0x75, 0xb7, 0xb2, 0x78, 0xbf, 0x31, 0xfc, 0x53, 0xb2, 0x5f, 0x06, 0x73, 0x8a, 0x2f, - 0x89, 0x14, 0x40, 0x05, 0x4c, 0x20, 0x80, 0x2c, 0x3a, 0x00, 0xce, 0x04, 0x1d, 0x83, 0x55, 0xd2, - 0x69, 0x9d, 0x3f, 0xa5, 0x6d, 0x2c, 0xf4, 0xf0, 0x03, 0x6d, 0x50, 0x44, 0x9e, 0x93, 0x23, 0xff, - 0xc0, 0x79, 0xc0, 0x27, 0xdf, 0x46, 0x34, 0xaf, 0x31, 0x1e, 0xcb, 0x24, 0x09, 0xd3, 0x34, 0xe0, - 0xb1, 0x85, 0x9a, 0xa8, 0x55, 0xf3, 0x6b, 0x45, 0xa5, 0x1f, 0x9b, 0x0e, 0xfe, 0xcf, 0x23, 0x52, - 0xec, 0x22, 0xeb, 0x28, 0xe9, 0x8e, 0x3a, 0x8f, 0x48, 0x6e, 0xd0, 0x8f, 0x9d, 0x67, 0xdc, 0x38, - 0x36, 0xca, 0x21, 0x8b, 0x0e, 0x58, 0xf3, 0x02, 0x57, 0x87, 0x34, 0x7b, 0x93, 0x36, 0xae, 0xf8, - 0x85, 0xca, 0xea, 0xf9, 0x46, 0xac, 0xb2, 0x86, 0x0a, 0xd5, 0xf5, 0x17, 0x2b, 0x1b, 0x2d, 0x57, - 0x36, 0xfa, 0x58, 0xd9, 0xe8, 0x75, 0x6d, 0x1b, 0xcb, 0xb5, 0x6d, 0xbc, 0xad, 0x6d, 0xe3, 0xb1, - 0xc3, 0xb8, 0x1a, 0x4e, 0x22, 0x97, 0xc8, 0x91, 0xf7, 0xc3, 0x0d, 0x4d, 0xdb, 0xde, 0x6c, 0xef, - 0x90, 0xd4, 0x3c, 0xa5, 0x10, 0x55, 0xf5, 0x35, 0xb4, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x94, - 0x4c, 0x39, 0x70, 0x7b, 0x02, 0x00, 0x00, + // 354 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4e, 0xea, 0x40, + 0x14, 0x86, 0x3b, 0x40, 0x48, 0x18, 0xb8, 0xb9, 0xf7, 0x36, 0xa8, 0x8d, 0x89, 0x95, 0x74, 0xc5, + 0xaa, 0x35, 0xb2, 0x61, 0x0d, 0x0b, 0x65, 0x5b, 0x8c, 0x0b, 0x37, 0x4d, 0x3b, 0x1d, 0xdb, 0x89, + 0x65, 0xa6, 0xe1, 0x0c, 0x04, 0x7c, 0x0a, 0x1f, 0x8b, 0x25, 0x4b, 0x57, 0xc6, 0xc0, 0xde, 0x67, + 0x30, 0x9d, 0x16, 0x02, 0x82, 0xd1, 0xdd, 0xfc, 0x67, 0xce, 0xf7, 0xff, 0x67, 0x26, 0x07, 0x5f, + 0x85, 0xf3, 0x11, 0xe5, 0xc0, 0x04, 0x9f, 0xcd, 0x9f, 0x9d, 0xad, 0x70, 0x12, 0x16, 0xc5, 0x92, + 0x24, 0x8c, 0x72, 0xe9, 0x44, 0x94, 0x53, 0x60, 0x60, 0xa7, 0x63, 0x21, 0x85, 0x6e, 0xed, 0x12, + 0xf6, 0x56, 0xd8, 0x3b, 0xc4, 0x79, 0x33, 0x12, 0x91, 0x50, 0xed, 0x4e, 0x76, 0xca, 0x49, 0xeb, + 0x03, 0xe1, 0xc6, 0x4d, 0xee, 0x35, 0x94, 0xbe, 0xa4, 0xfa, 0x23, 0xfe, 0x4f, 0x7c, 0x2e, 0x38, + 0x23, 0x7e, 0xe2, 0xe5, 0x28, 0x18, 0xa8, 0x55, 0x6e, 0xd7, 0xaf, 0x3b, 0xf6, 0xcf, 0x31, 0x76, + 0x7f, 0x03, 0xf7, 0x95, 0xee, 0x55, 0x16, 0x6f, 0x97, 0x9a, 0xfb, 0x8f, 0xec, 0x97, 0x41, 0x9f, + 0xe2, 0x33, 0x22, 0x38, 0x50, 0x0e, 0x13, 0xf0, 0x20, 0x8b, 0xf6, 0x80, 0x45, 0x9c, 0x8e, 0xc1, + 0x28, 0xa9, 0xb4, 0xee, 0xaf, 0xd2, 0x36, 0x16, 0x6a, 0xf8, 0xa1, 0x32, 0x28, 0x22, 0x4f, 0xc8, + 0x91, 0x3b, 0xb0, 0xee, 0xf0, 0xdf, 0x2f, 0x23, 0xea, 0x17, 0x18, 0x8f, 0x45, 0x92, 0xf8, 0x69, + 0xea, 0xb1, 0xd0, 0x40, 0x2d, 0xd4, 0xae, 0xb9, 0xb5, 0xa2, 0x32, 0x08, 0x75, 0x0b, 0xff, 0x61, + 0x01, 0x29, 0xfe, 0x22, 0xeb, 0x28, 0xa9, 0x8e, 0x3a, 0x0b, 0x48, 0x6e, 0x30, 0x08, 0xad, 0x29, + 0x6e, 0x1e, 0x1b, 0xe5, 0x90, 0x45, 0x07, 0xac, 0x7e, 0x8a, 0xab, 0x31, 0xcd, 0xde, 0xa4, 0x8c, + 0x2b, 0x6e, 0xa1, 0x74, 0x0b, 0x37, 0x82, 0x44, 0x90, 0xa7, 0x7b, 0x3f, 0xb9, 0xf5, 0x21, 0x36, + 0xca, 0x0a, 0xdd, 0xab, 0xf5, 0xdc, 0xc5, 0xca, 0x44, 0xcb, 0x95, 0x89, 0xde, 0x57, 0x26, 0x7a, + 0x59, 0x9b, 0xda, 0x72, 0x6d, 0x6a, 0xaf, 0x6b, 0x53, 0x7b, 0xe8, 0x46, 0x4c, 0xc6, 0x93, 0xc0, + 0x26, 0x62, 0xe4, 0x7c, 0xb3, 0x4f, 0xd3, 0x8e, 0x33, 0xdb, 0x5b, 0x2a, 0x39, 0x4f, 0x29, 0x04, + 0x55, 0xb5, 0x19, 0x9d, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x49, 0x76, 0xae, 0x91, 0x87, 0x02, + 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -334,10 +335,10 @@ func (m *ConsensusStateSigner) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.Signer) > 0 { - i -= len(m.Signer) - copy(dAtA[i:], m.Signer) - i = encodeVarintGenesis(dAtA, i, uint64(len(m.Signer))) + if len(m.BlockValHash) > 0 { + i -= len(m.BlockValHash) + copy(dAtA[i:], m.BlockValHash) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.BlockValHash))) i-- dAtA[i] = 0x1a } @@ -418,7 +419,7 @@ func (m *ConsensusStateSigner) Size() (n int) { if m.Height != 0 { n += 1 + sovGenesis(uint64(m.Height)) } - l = len(m.Signer) + l = len(m.BlockValHash) if l > 0 { n += 1 + l + sovGenesis(uint64(l)) } @@ -745,7 +746,7 @@ func (m *ConsensusStateSigner) Unmarshal(dAtA []byte) error { } case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BlockValHash", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -773,7 +774,7 @@ func (m *ConsensusStateSigner) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Signer = string(dAtA[iNdEx:postIndex]) + m.BlockValHash = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex diff --git a/x/lightclient/types/genesis_test.go b/x/lightclient/types/genesis_test.go index 0be957cd3..a0c3a9317 100644 --- a/x/lightclient/types/genesis_test.go +++ b/x/lightclient/types/genesis_test.go @@ -21,8 +21,8 @@ func TestGenesisValidate(t *testing.T) { {RollappId: "rollapp-2", IbcClientId: "client-2"}, }, ConsensusStateSigners: []types.ConsensusStateSigner{ - {IbcClientId: "client-1", Height: 1, Signer: "signer-1"}, - {IbcClientId: "client-1", Height: 2, Signer: "signer-1"}, + {IbcClientId: "client-1", Height: 1, BlockValHash: "signer-1"}, + {IbcClientId: "client-1", Height: 2, BlockValHash: "signer-1"}, }, }, valid: true, @@ -49,16 +49,16 @@ func TestGenesisValidate(t *testing.T) { name: "invalid height", g: types.GenesisState{ ConsensusStateSigners: []types.ConsensusStateSigner{ - {IbcClientId: "client-1", Height: 0, Signer: "signer-1"}, + {IbcClientId: "client-1", Height: 0, BlockValHash: "signer-1"}, }, }, valid: false, }, { - name: "invalid signer", + name: "invalid blockvalhash", g: types.GenesisState{ ConsensusStateSigners: []types.ConsensusStateSigner{ - {IbcClientId: "client-1", Height: 1, Signer: ""}, + {IbcClientId: "client-1", Height: 1, BlockValHash: ""}, }, }, valid: false, From 200a7fdebef67f919de84802e151e110178697f5 Mon Sep 17 00:00:00 2001 From: Spoorthi Satheesha <9302666+spoo-bar@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:06:05 +0530 Subject: [PATCH 87/87] dont attempt state check for last height as h+1 wont exist anyway --- x/lightclient/keeper/hook_listener.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/lightclient/keeper/hook_listener.go b/x/lightclient/keeper/hook_listener.go index 4056197b5..315419831 100644 --- a/x/lightclient/keeper/hook_listener.go +++ b/x/lightclient/keeper/hook_listener.go @@ -33,7 +33,7 @@ func (hook rollappHook) AfterUpdateState( ) error { canonicalClient, found := hook.k.GetCanonicalClient(ctx, rollappId) if !found { - canonicalClient, foundClient := hook.k.GetProspectiveCanonicalClient(ctx, rollappId, stateInfo.GetLatestHeight()) + canonicalClient, foundClient := hook.k.GetProspectiveCanonicalClient(ctx, rollappId, stateInfo.GetLatestHeight()-1) if foundClient { hook.k.SetCanonicalClient(ctx, rollappId, canonicalClient) }