cosmos-sdk/x/ibc/03-connection/client/utils/utils.go

214 lines
6.9 KiB
Go

package utils
import (
"context"
"fmt"
"io/ioutil"
"github.com/pkg/errors"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
clientutils "github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/utils"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
// QueryConnection returns a connection end.
// 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 QueryConnection(
clientCtx client.Context, connectionID string, prove bool,
) (*types.QueryConnectionResponse, error) {
if prove {
return queryConnectionABCI(clientCtx, connectionID)
}
queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryConnectionRequest{
ConnectionID: connectionID,
}
return queryClient.Connection(context.Background(), req)
}
func queryConnectionABCI(clientCtx client.Context, connectionID string) (*types.QueryConnectionResponse, error) {
req := abci.RequestQuery{
Path: "store/ibc/key",
Data: host.KeyConnection(connectionID),
Prove: true,
}
res, err := clientCtx.QueryABCI(req)
if err != nil {
return nil, err
}
var connection types.ConnectionEnd
if err := clientCtx.LegacyAmino.UnmarshalBinaryBare(res.Value, &connection); err != nil {
return nil, err
}
proofBz, err := clientCtx.LegacyAmino.MarshalBinaryBare(res.Proof)
if err != nil {
return nil, err
}
return types.NewQueryConnectionResponse(connectionID, connection, proofBz, res.Height), nil
}
// QueryClientConnections queries the connection paths registered for a particular client.
// 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 QueryClientConnections(
clientCtx client.Context, clientID string, prove bool,
) (*types.QueryClientConnectionsResponse, error) {
if prove {
return queryClientConnectionsABCI(clientCtx, clientID)
}
queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryClientConnectionsRequest{
ClientID: clientID,
}
return queryClient.ClientConnections(context.Background(), req)
}
func queryClientConnectionsABCI(clientCtx client.Context, clientID string) (*types.QueryClientConnectionsResponse, error) {
req := abci.RequestQuery{
Path: "store/ibc/key",
Data: host.KeyClientConnections(clientID),
Prove: true,
}
res, err := clientCtx.QueryABCI(req)
if err != nil {
return nil, err
}
var paths []string
if err := clientCtx.LegacyAmino.UnmarshalBinaryBare(res.Value, &paths); err != nil {
return nil, err
}
proofBz, err := clientCtx.LegacyAmino.MarshalBinaryBare(res.Proof)
if err != nil {
return nil, err
}
return types.NewQueryClientConnectionsResponse(clientID, paths, proofBz, res.Height), nil
}
// QueryConnectionClientState returns the ClientState of a connection end. 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 QueryConnectionClientState(
clientCtx client.Context, connectionID string, prove bool,
) (*types.QueryConnectionClientStateResponse, error) {
queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryConnectionClientStateRequest{
ConnectionID: connectionID,
}
res, err := queryClient.ConnectionClientState(context.Background(), req)
if err != nil {
return nil, err
}
if prove {
clientState, proof, proofHeight, err := clientutils.QueryClientStateABCI(clientCtx, res.IdentifiedClientState.ID)
if err != nil {
return nil, err
}
// use client state returned from ABCI query in case query height differs
identifiedClientState := clienttypes.NewIdentifiedClientState(res.IdentifiedClientState.ID, clientState)
res = types.NewQueryConnectionClientStateResponse(identifiedClientState, proof, int64(proofHeight))
}
return res, nil
}
// QueryConnectionConsensusState returns the ConsensusState of a connection end. 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 QueryConnectionConsensusState(
clientCtx client.Context, connectionID string, height uint64, prove bool,
) (*types.QueryConnectionConsensusStateResponse, error) {
queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryConnectionConsensusStateRequest{
ConnectionID: connectionID,
Height: height,
}
res, err := queryClient.ConnectionConsensusState(context.Background(), req)
if err != nil {
return nil, err
}
consensusState, err := clienttypes.UnpackConsensusState(res.ConsensusState)
if err != nil {
return nil, err
}
if prove {
consensusState, proof, proofHeight, err := clientutils.QueryConsensusStateABCI(clientCtx, res.ClientID, consensusState.GetHeight())
if err != nil {
return nil, err
}
// use consensus state returned from ABCI query in case query height differs
anyConsensusState, err := clienttypes.PackConsensusState(consensusState)
if err != nil {
return nil, err
}
res = types.NewQueryConnectionConsensusStateResponse(res.ClientID, anyConsensusState, consensusState.GetHeight(), proof, int64(proofHeight))
}
return res, nil
}
// ParsePrefix unmarshals an cmd input argument from a JSON string to a commitment
// Prefix. If the input is not a JSON, it looks for a path to the JSON file.
func ParsePrefix(cdc *codec.LegacyAmino, arg string) (commitmenttypes.MerklePrefix, error) {
var prefix commitmenttypes.MerklePrefix
if err := cdc.UnmarshalJSON([]byte(arg), &prefix); err != nil {
// check for file path if JSON input is not provided
contents, err := ioutil.ReadFile(arg)
if err != nil {
return commitmenttypes.MerklePrefix{}, errors.New("neither JSON input nor path to .json file were provided")
}
if err := cdc.UnmarshalJSON(contents, &prefix); err != nil {
return commitmenttypes.MerklePrefix{}, errors.Wrap(err, "error unmarshalling commitment prefix")
}
}
return prefix, nil
}
// ParseProof unmarshals a cmd input argument from a JSON string to a commitment
// Proof. If the input is not a JSON, it looks for a path to the JSON file. It
// then marshals the commitment proof into a proto encoded byte array.
func ParseProof(cdc *codec.LegacyAmino, arg string) ([]byte, error) {
var merkleProof commitmenttypes.MerkleProof
if err := cdc.UnmarshalJSON([]byte(arg), &merkleProof); err != nil {
// check for file path if JSON input is not provided
contents, err := ioutil.ReadFile(arg)
if err != nil {
return nil, errors.New("neither JSON input nor path to .json file were provided")
}
if err := cdc.UnmarshalJSON(contents, &merkleProof); err != nil {
return nil, fmt.Errorf("error unmarshalling commitment proof: %w", err)
}
}
return cdc.MarshalBinaryBare(&merkleProof)
}