github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/core/02-client/keeper/keeper.go (about) 1 package keeper 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "reflect" 7 "strings" 8 9 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/upgrade" 10 11 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 12 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/prefix" 13 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 14 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/params" 16 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/02-client/types" 17 commitmenttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/23-commitment/types" 18 host "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/24-host" 19 "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported" 20 ibctmtypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/light-clients/07-tendermint/types" 21 "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 22 lite "github.com/fibonacci-chain/fbc/libs/tendermint/lite2" 23 ) 24 25 type Keeper struct { 26 storeKey sdk.StoreKey 27 cdc *codec.CodecProxy 28 paramSpace params.Subspace 29 stakingKeeper types.StakingKeeper 30 upgradeKeeper types.UpgradeKeeper 31 } 32 33 // NewKeeper creates a new NewKeeper instance 34 func NewKeeper(cdc *codec.CodecProxy, key sdk.StoreKey, paramSpace params.Subspace, sk types.StakingKeeper, uk types.UpgradeKeeper) Keeper { 35 // set KeyTable if it has not already been set 36 if !paramSpace.HasKeyTable() { 37 paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) 38 } 39 40 return Keeper{ 41 storeKey: key, 42 cdc: cdc, 43 paramSpace: paramSpace, 44 stakingKeeper: sk, 45 upgradeKeeper: uk, 46 } 47 } 48 49 // Logger returns a module-specific logger. 50 func (k Keeper) Logger(ctx sdk.Context) log.Logger { 51 return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) 52 } 53 54 // GenerateClientIdentifier returns the next client identifier. 55 func (k Keeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { 56 nextClientSeq := k.GetNextClientSequence(ctx) 57 clientID := types.FormatClientIdentifier(clientType, nextClientSeq) 58 59 nextClientSeq++ 60 k.SetNextClientSequence(ctx, nextClientSeq) 61 return clientID 62 } 63 64 // GetClientState gets a particular client from the store 65 func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { 66 store := k.ClientStore(ctx, clientID) 67 bz := store.Get(host.ClientStateKey()) 68 if bz == nil { 69 return nil, false 70 } 71 72 clientState := k.MustUnmarshalClientState(bz) 73 return clientState, true 74 } 75 76 // SetClientState sets a particular Client to the store 77 func (k Keeper) SetClientState(ctx sdk.Context, clientID string, clientState exported.ClientState) { 78 store := k.ClientStore(ctx, clientID) 79 store.Set(host.ClientStateKey(), k.MustMarshalClientState(clientState)) 80 } 81 82 // GetClientConsensusState gets the stored consensus state from a client at a given height. 83 func (k Keeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { 84 store := k.ClientStore(ctx, clientID) 85 bz := store.Get(host.ConsensusStateKey(height)) 86 if bz == nil { 87 return nil, false 88 } 89 90 consensusState := k.MustUnmarshalConsensusState(bz) 91 return consensusState, true 92 } 93 94 // SetClientConsensusState sets a ConsensusState to a particular client at the given 95 // height 96 func (k Keeper) SetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height, consensusState exported.ConsensusState) { 97 store := k.ClientStore(ctx, clientID) 98 k.Logger(ctx).Info("set consensusState", "clientId", clientID, "height", height, "hash", 99 hex.EncodeToString(consensusState.GetRoot().GetHash()), 100 "consensusHeight", height) 101 store.Set(host.ConsensusStateKey(height), k.MustMarshalConsensusState(consensusState)) 102 } 103 104 // GetNextClientSequence gets the next client sequence from the store. 105 func (k Keeper) GetNextClientSequence(ctx sdk.Context) uint64 { 106 store := ctx.KVStore(k.storeKey) 107 bz := store.Get([]byte(types.KeyNextClientSequence)) 108 if bz == nil { 109 panic("next client sequence is nil") 110 } 111 112 return sdk.BigEndianToUint64(bz) 113 } 114 115 // IterateConsensusStates provides an iterator over all stored consensus states. 116 // objects. For each State object, cb will be called. If the cb returns true, 117 // the iterator will close and stop. 118 func (k Keeper) IterateConsensusStates(ctx sdk.Context, cb func(clientID string, cs types.ConsensusStateWithHeight) bool) { 119 store := ctx.KVStore(k.storeKey) 120 iterator := sdk.KVStorePrefixIterator(store, host.KeyClientStorePrefix) 121 122 defer iterator.Close() 123 for ; iterator.Valid(); iterator.Next() { 124 keySplit := strings.Split(string(iterator.Key()), "/") 125 // consensus key is in the format "clients/<clientID>/consensusStates/<height>" 126 if len(keySplit) != 4 || keySplit[2] != string(host.KeyConsensusStatePrefix) { 127 continue 128 } 129 clientID := keySplit[1] 130 height := types.MustParseHeight(keySplit[3]) 131 consensusState := k.MustUnmarshalConsensusState(iterator.Value()) 132 133 consensusStateWithHeight := types.NewConsensusStateWithHeight(height, consensusState) 134 135 if cb(clientID, consensusStateWithHeight) { 136 break 137 } 138 } 139 } 140 141 // GetAllGenesisClients returns all the clients in state with their client ids returned as IdentifiedClientState 142 func (k Keeper) GetAllGenesisClients(ctx sdk.Context) types.IdentifiedClientStates { 143 var genClients types.IdentifiedClientStates 144 k.IterateClients(ctx, func(clientID string, cs exported.ClientState) bool { 145 genClients = append(genClients, types.NewIdentifiedClientState(clientID, cs)) 146 return false 147 }) 148 149 return genClients.Sort() 150 } 151 152 // GetAllClientMetadata will take a list of IdentifiedClientState and return a list 153 // of IdentifiedGenesisMetadata necessary for exporting and importing client metadata 154 // into the client store. 155 func (k Keeper) GetAllClientMetadata(ctx sdk.Context, genClients []types.IdentifiedClientState) ([]types.IdentifiedGenesisMetadata, error) { 156 genMetadata := make([]types.IdentifiedGenesisMetadata, 0) 157 for _, ic := range genClients { 158 cs, err := types.UnpackClientState(ic.ClientState) 159 if err != nil { 160 return nil, err 161 } 162 gms := cs.ExportMetadata(k.ClientStore(ctx, ic.ClientId)) 163 if len(gms) == 0 { 164 continue 165 } 166 clientMetadata := make([]types.GenesisMetadata, len(gms)) 167 for i, metadata := range gms { 168 cmd, ok := metadata.(types.GenesisMetadata) 169 if !ok { 170 return nil, sdkerrors.Wrapf(types.ErrInvalidClientMetadata, "expected metadata type: %T, got: %T", 171 types.GenesisMetadata{}, cmd) 172 } 173 clientMetadata[i] = cmd 174 } 175 genMetadata = append(genMetadata, types.NewIdentifiedGenesisMetadata( 176 ic.ClientId, 177 clientMetadata, 178 )) 179 } 180 return genMetadata, nil 181 } 182 183 // SetAllClientMetadata takes a list of IdentifiedGenesisMetadata and stores all of the metadata in the client store at the appropriate paths. 184 func (k Keeper) SetAllClientMetadata(ctx sdk.Context, genMetadata []types.IdentifiedGenesisMetadata) { 185 for _, igm := range genMetadata { 186 // create client store 187 store := k.ClientStore(ctx, igm.ClientId) 188 // set all metadata kv pairs in client store 189 for _, md := range igm.ClientMetadata { 190 store.Set(md.GetKey(), md.GetValue()) 191 } 192 } 193 } 194 195 // GetAllConsensusStates returns all stored client consensus states. 196 func (k Keeper) GetAllConsensusStates(ctx sdk.Context) types.ClientsConsensusStates { 197 clientConsStates := make(types.ClientsConsensusStates, 0) 198 mapClientIDToConsStateIdx := make(map[string]int) 199 200 k.IterateConsensusStates(ctx, func(clientID string, cs types.ConsensusStateWithHeight) bool { 201 idx, ok := mapClientIDToConsStateIdx[clientID] 202 if ok { 203 clientConsStates[idx].ConsensusStates = append(clientConsStates[idx].ConsensusStates, cs) 204 return false 205 } 206 207 clientConsState := types.ClientConsensusStates{ 208 ClientId: clientID, 209 ConsensusStates: []types.ConsensusStateWithHeight{cs}, 210 } 211 212 clientConsStates = append(clientConsStates, clientConsState) 213 mapClientIDToConsStateIdx[clientID] = len(clientConsStates) - 1 214 return false 215 }) 216 217 return clientConsStates.Sort() 218 } 219 220 // HasClientConsensusState returns if keeper has a ConsensusState for a particular 221 // client at the given height 222 func (k Keeper) HasClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) bool { 223 store := k.ClientStore(ctx, clientID) 224 return store.Has(host.ConsensusStateKey(height)) 225 } 226 227 // GetLatestClientConsensusState gets the latest ConsensusState stored for a given client 228 func (k Keeper) GetLatestClientConsensusState(ctx sdk.Context, clientID string) (exported.ConsensusState, bool) { 229 clientState, ok := k.GetClientState(ctx, clientID) 230 if !ok { 231 return nil, false 232 } 233 return k.GetClientConsensusState(ctx, clientID, clientState.GetLatestHeight()) 234 } 235 236 // GetSelfConsensusState introspects the (self) past historical info at a given height 237 // and returns the expected consensus state at that height. 238 // For now, can only retrieve self consensus states for the current revision 239 func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) { 240 selfHeight, ok := height.(types.Height) 241 if !ok { 242 return nil, false 243 } 244 // check that height revision matches chainID revision 245 revision := types.ParseChainID(ctx.ChainID()) 246 if revision != height.GetRevisionNumber() { 247 return nil, false 248 } 249 histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(selfHeight.RevisionHeight)) 250 if !found { 251 return nil, false 252 } 253 254 consensusState := &ibctmtypes.ConsensusState{ 255 Timestamp: histInfo.Header.Time, 256 Root: commitmenttypes.NewMerkleRoot(histInfo.Header.GetAppHash()), 257 NextValidatorsHash: histInfo.Header.NextValidatorsHash, 258 } 259 k.Logger(ctx).Info("GetSelfConsensusState", "height", height, "hash", consensusState.GetRoot().GetHash()) 260 return consensusState, true 261 } 262 263 func (k Keeper) GetSelfConsensusStateV4(ctx sdk.Context, height exported.Height) (exported.ConsensusState, error) { 264 selfHeight, ok := height.(types.Height) 265 if !ok { 266 return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", types.Height{}, height) 267 } 268 // check that height revision matches chainID revision 269 revision := types.ParseChainID(ctx.ChainID()) 270 if revision != height.GetRevisionNumber() { 271 return nil, sdkerrors.Wrapf(types.ErrInvalidHeight, "chainID revision number does not match height revision number: expected %d, got %d", revision, height.GetRevisionNumber()) 272 } 273 histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(selfHeight.RevisionHeight)) 274 if !found { 275 return nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "no historical info found at height %d", selfHeight.RevisionHeight) 276 } 277 278 consensusState := &ibctmtypes.ConsensusState{ 279 Timestamp: histInfo.Header.Time, 280 Root: commitmenttypes.NewMerkleRoot(histInfo.Header.GetAppHash()), 281 NextValidatorsHash: histInfo.Header.NextValidatorsHash, 282 } 283 return consensusState, nil 284 } 285 286 // ValidateSelfClient validates the client parameters for a client of the running chain 287 // This function is only used to validate the client state the counterparty stores for this chain 288 // Client must be in same revision as the executing chain 289 func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error { 290 291 tmClient, ok := clientState.(*ibctmtypes.ClientState) 292 if !ok { 293 return sdkerrors.Wrapf(types.ErrInvalidClient, "client must be a Tendermint client, expected: %T, got: %T", 294 &ibctmtypes.ClientState{}, tmClient) 295 } 296 297 // old version 298 if !tmClient.FrozenHeight.IsZero() { 299 return types.ErrClientFrozen 300 } 301 302 if ctx.ChainID() != tmClient.ChainId { 303 return sdkerrors.Wrapf(types.ErrInvalidClient, "invalid chain-id. expected: %s, got: %s", 304 ctx.ChainID(), tmClient.ChainId) 305 } 306 307 revision := types.ParseChainID(ctx.ChainID()) 308 309 // client must be in the same revision as executing chain 310 if tmClient.LatestHeight.RevisionNumber != revision { 311 return sdkerrors.Wrapf(types.ErrInvalidClient, "client is not in the same revision as the chain. expected revision: %d, got: %d", 312 tmClient.LatestHeight.RevisionNumber, revision) 313 } 314 315 selfHeight := types.NewHeight(revision, uint64(ctx.BlockHeight())) 316 if tmClient.LatestHeight.GTE(selfHeight) { 317 return sdkerrors.Wrapf(types.ErrInvalidClient, "client has LatestHeight %d greater than or equal to chain height %d", 318 tmClient.LatestHeight, selfHeight) 319 } 320 321 expectedProofSpecs := commitmenttypes.GetSDKSpecs() 322 if !reflect.DeepEqual(expectedProofSpecs, tmClient.ProofSpecs) { 323 return sdkerrors.Wrapf(types.ErrInvalidClient, "client has invalid proof specs. expected: %v got: %v", 324 expectedProofSpecs, tmClient.ProofSpecs) 325 } 326 327 if err := lite.ValidateTrustLevel(tmClient.TrustLevel.ToTendermint()); err != nil { 328 return sdkerrors.Wrapf(types.ErrInvalidClient, "trust-level invalid: %v", err) 329 } 330 331 expectedUbdPeriod := k.stakingKeeper.UnbondingTime(ctx) 332 if expectedUbdPeriod != tmClient.UnbondingPeriod { 333 return sdkerrors.Wrapf(types.ErrInvalidClient, "invalid unbonding period. expected: %s, got: %s", 334 expectedUbdPeriod, tmClient.UnbondingPeriod) 335 } 336 337 if tmClient.UnbondingPeriod < tmClient.TrustingPeriod { 338 return sdkerrors.Wrapf(types.ErrInvalidClient, "unbonding period must be greater than trusting period. unbonding period (%d) < trusting period (%d)", 339 tmClient.UnbondingPeriod, tmClient.TrustingPeriod) 340 } 341 342 if len(tmClient.UpgradePath) != 0 { 343 // For now, SDK IBC implementation assumes that upgrade path (if defined) is defined by SDK upgrade module 344 345 expectedUpgradePath := []string{UpgradeModuleName, KeyUpgradedIBCState} 346 if !reflect.DeepEqual(expectedUpgradePath, tmClient.UpgradePath) { 347 return sdkerrors.Wrapf(types.ErrInvalidClient, "upgrade path must be the upgrade path defined by upgrade module. expected %v, got %v", 348 expectedUpgradePath, tmClient.UpgradePath) 349 } 350 } 351 return nil 352 } 353 354 var ( 355 // ModuleName is the name of this module 356 UpgradeModuleName = "upgrade" 357 KeyUpgradedIBCState = "upgradedIBCState" 358 ) 359 360 // IterateClients provides an iterator over all stored light client State 361 // objects. For each State object, cb will be called. If the cb returns true, 362 // the iterator will close and stop. 363 func (k Keeper) IterateClients(ctx sdk.Context, cb func(clientID string, cs exported.ClientState) bool) { 364 store := ctx.KVStore(k.storeKey) 365 iterator := sdk.KVStorePrefixIterator(store, host.KeyClientStorePrefix) 366 367 defer iterator.Close() 368 for ; iterator.Valid(); iterator.Next() { 369 keySplit := strings.Split(string(iterator.Key()), "/") 370 if keySplit[len(keySplit)-1] != host.KeyClientState { 371 continue 372 } 373 clientState := k.MustUnmarshalClientState(iterator.Value()) 374 375 // key is ibc/{clientid}/clientState 376 // Thus, keySplit[1] is clientID 377 if cb(keySplit[1], clientState) { 378 break 379 } 380 } 381 } 382 383 // SetNextClientSequence sets the next client sequence to the store. 384 func (k Keeper) SetNextClientSequence(ctx sdk.Context, sequence uint64) { 385 store := ctx.KVStore(k.storeKey) 386 bz := sdk.Uint64ToBigEndian(sequence) 387 store.Set([]byte(types.KeyNextClientSequence), bz) 388 } 389 390 // GetAllClients returns all stored light client State objects. 391 func (k Keeper) GetAllClients(ctx sdk.Context) (states []exported.ClientState) { 392 k.IterateClients(ctx, func(_ string, state exported.ClientState) bool { 393 states = append(states, state) 394 return false 395 }) 396 return states 397 } 398 399 // ClientStore returns isolated prefix store for each client so they can read/write in separate 400 // namespace without being able to read/write other client's data 401 func (k Keeper) ClientStore(ctx sdk.Context, clientID string) sdk.KVStore { 402 clientPrefix := []byte(fmt.Sprintf("%s/%s/", host.KeyClientStorePrefix, clientID)) 403 return prefix.NewStore(ctx.KVStore(k.storeKey), clientPrefix) 404 } 405 406 // GetUpgradePlan executes the upgrade keeper GetUpgradePlan function. 407 func (k Keeper) GetUpgradePlan(ctx sdk.Context) (plan upgrade.Plan, havePlan bool) { 408 return k.upgradeKeeper.GetUpgradePlan(ctx) 409 } 410 411 // GetUpgradedClient executes the upgrade keeper GetUpgradeClient function. 412 func (k Keeper) GetUpgradedClient(ctx sdk.Context, planHeight int64) ([]byte, bool) { 413 return k.upgradeKeeper.GetUpgradedClient(ctx, planHeight) 414 } 415 416 // GetUpgradedConsensusState returns the upgraded consensus state 417 func (k Keeper) GetUpgradedConsensusState(ctx sdk.Context, planHeight int64) ([]byte, bool) { 418 return k.upgradeKeeper.GetUpgradedConsensusState(ctx, planHeight) 419 } 420 421 // SetUpgradedConsensusState executes the upgrade keeper SetUpgradedConsensusState function. 422 func (k Keeper) SetUpgradedConsensusState(ctx sdk.Context, planHeight int64, bz []byte) error { 423 return k.upgradeKeeper.SetUpgradedConsensusState(ctx, planHeight, bz) 424 }