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  }