cosmos-sdk/x/ibc/02-client/client/utils/utils.go

200 lines
5.5 KiB
Go

package utils
import (
"context"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/client"
"github.com/cosmos/cosmos-sdk/x/ibc/exported"
)
// QueryClientState returns a client state. If prove is true, it performs an ABCI store query
// in order to retrieve the merkle proof. Otherwise, it uses the gRPC query client.
func QueryClientState(
clientCtx client.Context, clientID string, prove bool,
) (*types.QueryClientStateResponse, error) {
if prove {
return QueryClientStateABCI(clientCtx, clientID)
}
queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryClientStateRequest{
ClientId: clientID,
}
return queryClient.ClientState(context.Background(), req)
}
// QueryClientStateABCI queries the store to get the light client state and a merkle proof.
func QueryClientStateABCI(
clientCtx client.Context, clientID string,
) (*types.QueryClientStateResponse, error) {
key := host.FullKeyClientPath(clientID, host.KeyClientState())
value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key)
if err != nil {
return nil, err
}
// check if client exists
if len(value) == 0 {
return nil, sdkerrors.Wrap(types.ErrClientNotFound, clientID)
}
cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry)
clientState, err := types.UnmarshalClientState(cdc, value)
if err != nil {
return nil, err
}
anyClientState, err := types.PackClientState(clientState)
if err != nil {
return nil, err
}
clientStateRes := types.NewQueryClientStateResponse(clientID, anyClientState, proofBz, proofHeight)
return clientStateRes, nil
}
// QueryConsensusState returns a consensus state. If prove is true, it performs an ABCI store
// query in order to retrieve the merkle proof. Otherwise, it uses the gRPC query client.
func QueryConsensusState(
clientCtx client.Context, clientID string, height exported.Height, prove, latestHeight bool,
) (*types.QueryConsensusStateResponse, error) {
if prove {
return QueryConsensusStateABCI(clientCtx, clientID, height)
}
queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryConsensusStateRequest{
ClientId: clientID,
EpochNumber: height.GetEpochNumber(),
EpochHeight: height.GetEpochHeight(),
LatestHeight: latestHeight,
}
return queryClient.ConsensusState(context.Background(), req)
}
// QueryConsensusStateABCI queries the store to get the consensus state of a light client and a
// merkle proof of its existence or non-existence.
func QueryConsensusStateABCI(
clientCtx client.Context, clientID string, height exported.Height,
) (*types.QueryConsensusStateResponse, error) {
key := host.FullKeyClientPath(clientID, host.KeyConsensusState(height))
value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key)
if err != nil {
return nil, err
}
// check if consensus state exists
if len(value) == 0 {
return nil, sdkerrors.Wrap(types.ErrConsensusStateNotFound, clientID)
}
cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry)
cs, err := types.UnmarshalConsensusState(cdc, value)
if err != nil {
return nil, err
}
anyConsensusState, err := types.PackConsensusState(cs)
if err != nil {
return nil, err
}
return types.NewQueryConsensusStateResponse(clientID, anyConsensusState, proofBz, proofHeight), nil
}
// QueryTendermintHeader takes a client context and returns the appropriate
// tendermint header
func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, error) {
node, err := clientCtx.GetNode()
if err != nil {
return ibctmtypes.Header{}, 0, err
}
info, err := node.ABCIInfo()
if err != nil {
return ibctmtypes.Header{}, 0, err
}
height := info.Response.LastBlockHeight
commit, err := node.Commit(&height)
if err != nil {
return ibctmtypes.Header{}, 0, err
}
page := 0
count := 10_000
validators, err := node.Validators(&height, &page, &count)
if err != nil {
return ibctmtypes.Header{}, 0, err
}
protoCommit := commit.SignedHeader.ToProto()
protoValset, err := tmtypes.NewValidatorSet(validators.Validators).ToProto()
if err != nil {
return ibctmtypes.Header{}, 0, err
}
header := ibctmtypes.Header{
SignedHeader: protoCommit,
ValidatorSet: protoValset,
}
return header, height, nil
}
// QueryNodeConsensusState takes a client context and returns the appropriate
// tendermint consensus state
func QueryNodeConsensusState(clientCtx client.Context) (*ibctmtypes.ConsensusState, int64, error) {
node, err := clientCtx.GetNode()
if err != nil {
return &ibctmtypes.ConsensusState{}, 0, err
}
info, err := node.ABCIInfo()
if err != nil {
return &ibctmtypes.ConsensusState{}, 0, err
}
height := info.Response.LastBlockHeight
commit, err := node.Commit(&height)
if err != nil {
return &ibctmtypes.ConsensusState{}, 0, err
}
page := 1
count := 10_000
nextHeight := height + 1
nextVals, err := node.Validators(&nextHeight, &page, &count)
if err != nil {
return &ibctmtypes.ConsensusState{}, 0, err
}
state := &ibctmtypes.ConsensusState{
Timestamp: commit.Time,
Root: commitmenttypes.NewMerkleRoot(commit.AppHash),
NextValidatorsHash: tmtypes.NewValidatorSet(nextVals.Validators).Hash(),
}
return state, height, nil
}