icq

package module
v5.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2023 License: Apache-2.0 Imports: 22 Imported by: 0

README


ics: TBD title: Interchain Queries stage: Draft category: IBC/APP requires: 25, 26 kind: instantiation author: Ali Zahid Raja ali@polymerlabs.org, Ehsan Saradar ehsan@quasar.fi created: 2022-06-22 modified: 2022-08-30

Synopsis

This document serves as a guide for better understanding the interchain queries implementation that uses ABCI Queries.

Motivation

Interchain Queries enable blockchains to query the state of an account on another chain without the need for ICA auth. ICS-27, Interchain Accounts, is used for IBC transactions, e.g. to transfer coins from one interchain account to another, whereas Interchain Queries are used for IBC Queries, e.g. to query the balance of an account on another chain. In short, ICA is cross chain writes while ICQ is cross chain reads.

Definitions

  • Host Chain: The chain where the query is sent. The host chain listens for IBC packets from a controller chain which contain instructions describing an ABCI Query Request.
  • Controller Chain: The chain sending the query to the host chain. The controller chain sends IBC packets to the host chain to query information.
  • Interchain Query: An IBC packet that contains information about the query in the form of an ABCI RequestQuery.

The chain which sends the query becomes the controller chain, and the chain which receives the query and responds becomes the host chain.

Desired properties

  • Permissionless: An interchain query may be created by any actor without the approval of a third party (e.g. chain governance)

Technical Specification

model

ABCI Query

ABCI RequestQuery enables blockchains to request information made by the end-users of applications. A query is received by a full node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via BaseApp's query router so that it can be processed by the module's query service.

ICQ can only return information from stale reads, for a read that requires consensus, ICA (ICS-27) will be used.

SendQuery

SendQuery is used to send an IBC packet containing query information to an interchain account on a host chain.

func (k Keeper) SendQuery(ctx sdk.Context, sourcePort, sourceChannel string, chanCap *capabilitytypes.Capability, 
reqs []abci.RequestQuery, timeoutHeight clienttypes.Height, timeoutTimestamp uint64) (uint64, error) {
	
    sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel)
	if !found {
		return 0, sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", sourcePort, sourceChannel)
	}

	destinationPort := sourceChannelEnd.GetCounterparty().GetPortID()
	destinationChannel := sourceChannelEnd.GetCounterparty().GetChannelID()

	icqPacketData := types.InterchainQueryPacketData{
		Requests: reqs,
	}

	return k.createOutgoingPacket(ctx, sourcePort, sourceChannel, destinationPort, destinationChannel, chanCap, icqPacketData, timeoutTimestamp)
}
authenticateQuery

authenticateQuery is called before executeQuery.

authenticateQuery checks that the query is a part of the whitelisted queries.

func (k Keeper) authenticateQuery(ctx sdk.Context, q abci.RequestQuery) error {
	allowQueries := k.GetAllowQueries(ctx)
	if !types.ContainsQueryPath(allowQueries, q.Path) {
		return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "query path not allowed: %s", q.Path)
	}
	if !(q.Height == 0 || q.Height == ctx.BlockHeight()) {
		return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "query height not allowed: %d", q.Height)
	}
	if q.Prove {
		return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "query proof not allowed")
	}

	return nil
}
executeQuery

Executes each query sent by the controller chain.

func (k Keeper) executeQuery(ctx sdk.Context, reqs []abci.RequestQuery) ([]byte, error) {
	resps := make([]abci.ResponseQuery, len(reqs))
	for i, req := range reqs {
		if err := k.authenticateQuery(ctx, req); err != nil {
			return nil, err
		}

		resp := k.querier.Query(req)
		// Remove non-deterministic fields from response
		resps[i] = abci.ResponseQuery{
			Code:   resp.Code,
			Index:  resp.Index,
			Key:    resp.Key,
			Value:  resp.Value,
			Height: resp.Height,
		}
	}

	bz, err := types.SerializeCosmosResponse(resps)
	if err != nil {
		return nil, err
	}
	ack := types.InterchainQueryPacketAck{
		Data: bz,
	}
	data, err := types.ModuleCdc.MarshalJSON(&ack)
	if err != nil {
		return nil, sdkerrors.Wrap(err, "failed to marshal tx data")
	}

	return data, nil
}

Packet Data

InterchainQueryPacketData is comprised of a raw query.

message InterchainQueryPacketData  {
    bytes data = 1;
}

InterchainQueryPacketAck is comprised of an ABCI query response with non-deterministic fields left empty (e.g. Codespace, Log, Info).

message InterchainQueryPacketAck {
	bytes data = 1;
}

CosmosQuery contains a list of tendermint ABCI query requests. It should be used when sending queries to an SDK host chain.

message CosmosQuery {
  repeated tendermint.abci.RequestQuery requests = 1 [(gogoproto.nullable) = false];
}

CosmosResponse contains a list of tendermint ABCI query responses. It should be used when receiving responses from an SDK host chain.

message CosmosResponse {
  repeated tendermint.abci.ResponseQuery responses = 1 [(gogoproto.nullable) = false];
}

Packet relay

onRecvPacket is called by the routing module when a packet addressed to this module has been received.

func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) ([]byte, error) {
	var data types.InterchainQueryPacketData

	if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
		// UnmarshalJSON errors are indeterminate and therefore are not wrapped and included in failed acks
		return nil, sdkerrors.Wrapf(types.ErrUnknownDataType, "cannot unmarshal ICQ packet data")
	}

	reqs, err := types.DeserializeCosmosQuery(data.GetData())
	if err != nil {
		return nil, err
	}

	response, err := k.executeQuery(ctx, reqs)
	if err != nil {
		return nil, err
	}
	return response, err
}

Sample Query

To get the balance of an address we can use the following In order to send a packet from a module, first you need to prepare your query Data and encapsule it in an ABCI RequestQuery. Then you can use SerializeCosmosQuery to construct the Data of InterchainQueryPacketData packet data. After these steps you should use ICS 4 wrapper to send your packet to the host chain through a valid channel.

q := banktypes.QueryAllBalancesRequest{
    Address: "cosmos1tshnze3yrtv3hk9x536p7znpxeckd4v9ha0trg",
    Pagination: &query.PageRequest{
        Offset: 0,
        Limit: 10,
    },
}

reqs := []abcitypes.RequestQuery{
	{
		Path: "/cosmos.bank.v1beta1.Query/AllBalances",
		Data: k.cdc.MustMarshal(&q),
	},
}

bz, err := icqtypes.SerializeCosmosQuery(reqs)
if err != nil {
	return 0, err
}
icqPacketData := icqtypes.InterchainQueryPacketData{
	Data: bz,
}

packet := channeltypes.NewPacket(
		icqPacketData.GetBytes(),
		sequence,
		sourcePort,
		sourceChannel,
		destinationPort,
		destinationChannel,
		clienttypes.ZeroHeight(),
		timeoutTimestamp,
)

// Send the `packet` with ICS-4 interface

Sample Acknowledgement Response

Acknowledgements are written to the host chain's state on a successful OnRecvPacket and then the controller chain verifies that acknowledgement in OnAcknowledgement. It is here that developers should implement there custom handling logic for the various expected responses from the queries.

A successful acknowledgment will be sent back to the querier module as an InterchainQueryPacketAck. The Data field should be deserialized to an array of ABCI ResponseQuery with the DeserializeCosmosResponse function.

switch resp := ack.Response.(type) {
	case *channeltypes.Acknowledgement_Result:
		var ackData icqtypes.InterchainQueryPacketAck
		if err := icqtypes.ModuleCdc.UnmarshalJSON(resp.Result, &ackData); err != nil {
			return sdkerrors.Wrap(err, "failed to unmarshal interchain query packet ack")
		}

        resps, err := icqtypes.DeserializeCosmosResponse(ackData.Data)
        if err != nil {
            return sdkerrors.Wrap(err, "failed to unmarshal interchain query packet ack to cosmos response")
        }

		if len(resps) < 1 {
			return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "no responses in interchain query packet ack")
		}

		var r banktypes.QueryAllBalancesResponse
		if err := k.cdc.Unmarshal(resps[0].Value, &r); err != nil {
			return sdkerrors.Wrapf(err, "failed to unmarshal interchain query response to type %T", resp)
		}

        // `r` is the response of your query
...

Other Implementations

Another implementation of Interchain Queries is by the use of the KV store which can be seen implemented here by QuickSilver.

The implementation works even if the host side hasn't implemented ICQ, however, it does not fully leverage the IBC standards.

History

June 22, 2022 - Draft

August 30, 2022 - Major Revisions, added ICQ to IBC-test

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ValidateICQChannelParams

func ValidateICQChannelParams(
	ctx sdk.Context,
	keeper keeper.Keeper,
	order channeltypes.Order,
	portID string,
	_ string,
) error

Types

type AppModule

type AppModule struct {
	AppModuleBasic
	// contains filtered or unexported fields
}

AppModule is the application module for the IBC interchain query module

func NewAppModule

func NewAppModule(keeper keeper.Keeper) AppModule

NewAppModule creates a new IBC interchain query module

func (AppModule) BeginBlock

func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock)

BeginBlock implements the AppModule interface

func (AppModule) ConsensusVersion

func (AppModule) ConsensusVersion() uint64

ConsensusVersion implements AppModule/ConsensusVersion.

func (AppModule) EndBlock

EndBlock implements the AppModule interface

func (AppModule) ExportGenesis

func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage

ExportGenesis returns the exported genesis state as raw bytes for the icq module.

func (AppModule) InitGenesis

func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate

InitGenesis performs genesis initialization for the icq module. It returns no validator updates.

func (AppModule) InitModule

func (am AppModule) InitModule(ctx sdk.Context, params types.Params)

InitModule will initialize the interchain query moudule. It should only be called once and as an alternative to InitGenesis.

func (AppModule) LegacyQuerierHandler

func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier

LegacyQuerierHandler implements the AppModule interface

func (AppModule) NewHandler

func (AppModule) NewHandler() sdk.Handler

NewHandler implements the AppModule interface

func (AppModule) QuerierRoute

func (AppModule) QuerierRoute() string

QuerierRoute implements the AppModule interface

func (AppModule) RegisterInvariants

func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry)

RegisterInvariants implements the AppModule interface

func (AppModule) RegisterServices

func (am AppModule) RegisterServices(cfg module.Configurator)

RegisterServices registers module services

func (AppModule) Route

func (AppModule) Route() sdk.Route

Route implements the AppModule interface

type AppModuleBasic

type AppModuleBasic struct{}

AppModuleBasic is the IBC interchain query AppModuleBasic

func (AppModuleBasic) DefaultGenesis

func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage

DefaultGenesis returns default genesis state as raw bytes for the IBC interchain query module

func (AppModuleBasic) GetQueryCmd

func (AppModuleBasic) GetQueryCmd() *cobra.Command

GetQueryCmd implements AppModuleBasic interface

func (AppModuleBasic) GetTxCmd

func (AppModuleBasic) GetTxCmd() *cobra.Command

GetTxCmd implements AppModuleBasic interface

func (AppModuleBasic) Name

func (AppModuleBasic) Name() string

Name implements AppModuleBasic interface

func (AppModuleBasic) RegisterGRPCGatewayRoutes

func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux)

RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the interchain query module.

func (AppModuleBasic) RegisterInterfaces

func (AppModuleBasic) RegisterInterfaces(_ codectypes.InterfaceRegistry)

RegisterInterfaces registers module concrete types into protobuf Any

func (AppModuleBasic) RegisterLegacyAminoCodec

func (AppModuleBasic) RegisterLegacyAminoCodec(_ *codec.LegacyAmino)

RegisterLegacyAminoCodec implements AppModuleBasic.

func (AppModuleBasic) RegisterRESTRoutes

func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router)

RegisterRESTRoutes implements AppModuleBasic interface

func (AppModuleBasic) ValidateGenesis

ValidateGenesis performs genesis state validation for the IBC interchain acounts module

type IBCModule

type IBCModule struct {
	// contains filtered or unexported fields
}

IBCModule implements the ICS26 interface for interchain query host chains

func NewIBCModule

func NewIBCModule(k keeper.Keeper) IBCModule

NewIBCModule creates a new IBCModule given the associated keeper

func (IBCModule) OnAcknowledgementPacket

func (im IBCModule) OnAcknowledgementPacket(
	_ sdk.Context,
	_ channeltypes.Packet,
	_ []byte,
	_ sdk.AccAddress,
) error

OnAcknowledgementPacket implements the IBCModule interface

func (IBCModule) OnChanCloseConfirm

func (im IBCModule) OnChanCloseConfirm(
	_ sdk.Context,
	_ string,
	_ string,
) error

OnChanCloseConfirm implements the IBCModule interface

func (IBCModule) OnChanCloseInit

func (im IBCModule) OnChanCloseInit(
	_ sdk.Context,
	_ string,
	_ string,
) error

OnChanCloseInit implements the IBCModule interface

func (IBCModule) OnChanOpenAck

func (im IBCModule) OnChanOpenAck(
	ctx sdk.Context,
	_ string,
	_ string,
	_ string,
	counterpartyVersion string,
) error

OnChanOpenAck implements the IBCModule interface

func (IBCModule) OnChanOpenConfirm

func (im IBCModule) OnChanOpenConfirm(
	ctx sdk.Context,
	_ string,
	_ string,
) error

OnChanOpenAck implements the IBCModule interface

func (IBCModule) OnChanOpenInit

func (im IBCModule) OnChanOpenInit(
	ctx sdk.Context,
	order channeltypes.Order,
	_ []string,
	portID string,
	channelID string,
	chanCap *capabilitytypes.Capability,
	_ channeltypes.Counterparty,
	version string,
) (string, error)

OnChanOpenInit implements the IBCModule interface

func (IBCModule) OnChanOpenTry

func (im IBCModule) OnChanOpenTry(
	ctx sdk.Context,
	order channeltypes.Order,
	_ []string,
	portID,
	channelID string,
	chanCap *capabilitytypes.Capability,
	_ channeltypes.Counterparty,
	counterpartyVersion string,
) (string, error)

OnChanOpenTry implements the IBCModule interface

func (IBCModule) OnRecvPacket

func (im IBCModule) OnRecvPacket(
	ctx sdk.Context,
	packet channeltypes.Packet,
	_ sdk.AccAddress,
) ibcexported.Acknowledgement

OnRecvPacket implements the IBCModule interface

func (IBCModule) OnTimeoutPacket

func (im IBCModule) OnTimeoutPacket(
	_ sdk.Context,
	_ channeltypes.Packet,
	_ sdk.AccAddress,
) error

OnTimeoutPacket implements the IBCModule interface

Directories

Path Synopsis
Package types is a reverse proxy.
Package types is a reverse proxy.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL