github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/core/02-client/keeper/client.go (about)

     1  package keeper
     2  
     3  import (
     4  	"encoding/hex"
     5  
     6  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     7  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
     8  	"github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/02-client/types"
     9  	"github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported"
    10  )
    11  
    12  // CreateClient creates a new client state and populates it with a given consensus
    13  // state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create
    14  func (k Keeper) CreateClient(
    15  	ctx sdk.Context, clientState exported.ClientState, consensusState exported.ConsensusState,
    16  ) (string, error) {
    17  	params := k.GetParams(ctx)
    18  	if !params.IsAllowedClient(clientState.ClientType()) {
    19  		return "", sdkerrors.Wrapf(
    20  			types.ErrInvalidClientType,
    21  			"client state type %s is not registered in the allowlist", clientState.ClientType(),
    22  		)
    23  	}
    24  
    25  	clientID := k.GenerateClientIdentifier(ctx, clientState.ClientType())
    26  
    27  	k.SetClientState(ctx, clientID, clientState)
    28  	k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String())
    29  
    30  	// verifies initial consensus state against client state and initializes client store with any client-specific metadata
    31  	// e.g. set ProcessedTime in Tendermint clients
    32  	if err := clientState.Initialize(ctx, k.cdc, k.ClientStore(ctx, clientID), consensusState); err != nil {
    33  		return "", err
    34  	}
    35  
    36  	// check if consensus state is nil in case the created client is Localhost
    37  	if consensusState != nil {
    38  		k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState)
    39  	}
    40  
    41  	k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String())
    42  
    43  	return clientID, nil
    44  }
    45  
    46  // UpdateClient updates the consensus state and the state root from a provided header.
    47  func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error {
    48  	clientState, found := k.GetClientState(ctx, clientID)
    49  	if !found {
    50  		return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID)
    51  	}
    52  	clientStore := k.ClientStore(ctx, clientID)
    53  	// prevent update if the client is frozen before or at header height
    54  	if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active {
    55  		return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot update client (%s) with status %s", clientID, status)
    56  	}
    57  
    58  	eventType := types.EventTypeUpdateClient
    59  
    60  	// Any writes made in CheckHeaderAndUpdateState are persisted on both valid updates and misbehaviour updates.
    61  	// Light client implementations are responsible for writing the correct metadata (if any) in either case.
    62  	newClientState, newConsensusState, err := clientState.CheckHeaderAndUpdateState(ctx, k.cdc, clientStore, header)
    63  	if err != nil {
    64  		return sdkerrors.Wrapf(err, "cannot update client with ID %s", clientID)
    65  	}
    66  
    67  	// emit the full header in events
    68  	var (
    69  		headerStr       string
    70  		consensusHeight exported.Height
    71  	)
    72  	if header != nil {
    73  		// Marshal the Header as an Any and encode the resulting bytes to hex.
    74  		// This prevents the event value from containing invalid UTF-8 characters
    75  		// which may cause data to be lost when JSON encoding/decoding.
    76  		headerStr = hex.EncodeToString(types.MustMarshalHeader(k.cdc, header))
    77  		// set default consensus height with header height
    78  		consensusHeight = header.GetHeight()
    79  
    80  	}
    81  
    82  	// set new client state regardless of if update is valid update or misbehaviour
    83  	k.SetClientState(ctx, clientID, newClientState)
    84  	// If client state is not frozen after clientState CheckHeaderAndUpdateState,
    85  	// then update was valid. Write the update state changes, and set new consensus state.
    86  	// Else the update was proof of misbehaviour and we must emit appropriate misbehaviour events.
    87  	if status := newClientState.Status(ctx, clientStore, k.cdc); status != exported.Frozen {
    88  		// if update is not misbehaviour then update the consensus state
    89  		// we don't set consensus state for localhost client
    90  		if header != nil && clientID != exported.Localhost {
    91  			k.SetClientConsensusState(ctx, clientID, header.GetHeight(), newConsensusState)
    92  		} else {
    93  			consensusHeight = types.GetSelfHeight(ctx)
    94  		}
    95  
    96  		k.Logger(ctx).Info("client state updated", "client-id", clientID, "height", consensusHeight.String())
    97  
    98  	} else {
    99  		// set eventType to SubmitMisbehaviour
   100  		eventType = types.EventTypeSubmitMisbehaviour
   101  
   102  		k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID)
   103  	}
   104  	// emit the full header in events
   105  
   106  	// emitting events in the keeper emits for both begin block and handler client updates
   107  	ctx.EventManager().EmitEvent(
   108  		sdk.NewEvent(
   109  			eventType,
   110  			sdk.NewAttribute(types.AttributeKeyClientID, clientID),
   111  			sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()),
   112  			sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()),
   113  			sdk.NewAttribute(types.AttributeKeyHeader, headerStr),
   114  		),
   115  	)
   116  
   117  	return nil
   118  }
   119  
   120  // UpgradeClient upgrades the client to a new client state if this new client was committed to
   121  // by the old client at the specified upgrade height
   122  func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient exported.ClientState, upgradedConsState exported.ConsensusState,
   123  	proofUpgradeClient, proofUpgradeConsState []byte) error {
   124  	clientState, found := k.GetClientState(ctx, clientID)
   125  	if !found {
   126  		return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID)
   127  	}
   128  
   129  	clientStore := k.ClientStore(ctx, clientID)
   130  
   131  	if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active {
   132  		return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot upgrade client (%s) with status %s", clientID, status)
   133  	}
   134  
   135  	updatedClientState, updatedConsState, err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore,
   136  		upgradedClient, upgradedConsState, proofUpgradeClient, proofUpgradeConsState)
   137  	if err != nil {
   138  		return sdkerrors.Wrapf(err, "cannot upgrade client with ID %s", clientID)
   139  	}
   140  
   141  	k.SetClientState(ctx, clientID, updatedClientState)
   142  	k.SetClientConsensusState(ctx, clientID, updatedClientState.GetLatestHeight(), updatedConsState)
   143  
   144  	k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", updatedClientState.GetLatestHeight().String())
   145  
   146  	// emitting events in the keeper emits for client upgrades
   147  	ctx.EventManager().EmitEvent(
   148  		sdk.NewEvent(
   149  			types.EventTypeUpgradeClient,
   150  			sdk.NewAttribute(types.AttributeKeyClientID, clientID),
   151  			sdk.NewAttribute(types.AttributeKeyClientType, updatedClientState.ClientType()),
   152  			sdk.NewAttribute(types.AttributeKeyConsensusHeight, updatedClientState.GetLatestHeight().String()),
   153  		),
   154  	)
   155  
   156  	return nil
   157  }
   158  
   159  // CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the
   160  // client if so.
   161  func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour exported.Misbehaviour) error {
   162  	clientState, found := k.GetClientState(ctx, misbehaviour.GetClientID())
   163  	if !found {
   164  		return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID())
   165  	}
   166  
   167  	clientStore := k.ClientStore(ctx, misbehaviour.GetClientID())
   168  
   169  	if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active {
   170  		return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot process misbehaviour for client (%s) with status %s", misbehaviour.GetClientID(), status)
   171  	}
   172  
   173  	if err := misbehaviour.ValidateBasic(); err != nil {
   174  		return err
   175  	}
   176  
   177  	clientState, err := clientState.CheckMisbehaviourAndUpdateState(ctx, k.cdc, clientStore, misbehaviour)
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	k.SetClientState(ctx, misbehaviour.GetClientID(), clientState)
   183  	k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", misbehaviour.GetClientID())
   184  
   185  	return nil
   186  }