diff --git a/packages/stargate/src/queries/ibc.ts b/packages/stargate/src/queries/ibc.ts index c3a84de1..4fd9ed52 100644 --- a/packages/stargate/src/queries/ibc.ts +++ b/packages/stargate/src/queries/ibc.ts @@ -3,8 +3,11 @@ import { toAscii } from "@cosmjs/encoding"; import { Uint64 } from "@cosmjs/math"; import Long from "long"; +import { Any } from "../codec/google/protobuf/any"; import { Channel } from "../codec/ibc/core/channel/v1/channel"; import { + QueryChannelClientStateResponse, + QueryChannelConsensusStateResponse, QueryChannelResponse, QueryChannelsResponse, QueryClientImpl as ChannelQuery, @@ -14,18 +17,50 @@ import { QueryPacketAcknowledgementsResponse, QueryPacketCommitmentResponse, QueryPacketCommitmentsResponse, + QueryPacketReceiptResponse, QueryUnreceivedAcksResponse, QueryUnreceivedPacketsResponse, } from "../codec/ibc/core/channel/v1/query"; +import { Height } from "../codec/ibc/core/client/v1/client"; +import { + QueryClientImpl as ClientQuery, + QueryClientParamsResponse, + QueryClientStateResponse, + QueryClientStatesResponse, + QueryConsensusStateRequest, + QueryConsensusStateResponse, + QueryConsensusStatesResponse, +} from "../codec/ibc/core/client/v1/query"; import { QueryClientConnectionsResponse, QueryClientImpl as ConnectionQuery, + QueryConnectionClientStateResponse, + QueryConnectionConsensusStateRequest, + QueryConnectionConsensusStateResponse, QueryConnectionResponse, QueryConnectionsResponse, } from "../codec/ibc/core/connection/v1/query"; +import { + ClientState as TendermintClientState, + ConsensusState as TendermintConsensusState, +} from "../codec/ibc/lightclients/tendermint/v1/tendermint"; import { QueryClient } from "./queryclient"; import { createPagination, createProtobufRpcClient } from "./utils"; +function decodeTendermintClientStateAny(clientState: Any | undefined): TendermintClientState { + if (clientState?.typeUrl !== "/ibc.lightclients.tendermint.v1.ClientState") { + throw new Error(`Unexpected client state type: ${clientState?.typeUrl}`); + } + return TendermintClientState.decode(clientState.value); +} + +function decodeTendermintConsensusStateAny(clientState: Any | undefined): TendermintConsensusState { + if (clientState?.typeUrl !== "/ibc.lightclients.tendermint.v1.ConsensusState") { + throw new Error(`Unexpected client state type: ${clientState?.typeUrl}`); + } + return TendermintConsensusState.decode(clientState.value); +} + export interface IbcExtension { readonly ibc: { readonly channel: (portId: string, channelId: string) => Promise; @@ -37,53 +72,98 @@ export interface IbcExtension { ) => Promise; readonly nextSequenceReceive: (portId: string, channelId: string) => Promise; readonly unverified: { - // Queries for ibc.core.channel.v1 - readonly channel: (portId: string, channelId: string) => Promise; - readonly channels: (paginationKey?: Uint8Array) => Promise; - readonly connectionChannels: ( - connection: string, - paginationKey?: Uint8Array, - ) => Promise; - readonly packetCommitment: ( - portId: string, - channelId: string, - sequence: number, - ) => Promise; - readonly packetCommitments: ( - portId: string, - channelId: string, - paginationKey?: Uint8Array, - ) => Promise; - readonly packetAcknowledgement: ( - portId: string, - channelId: string, - sequence: number, - ) => Promise; - readonly packetAcknowledgements: ( - portId: string, - channelId: string, - paginationKey?: Uint8Array, - ) => Promise; - readonly unreceivedPackets: ( - portId: string, - channelId: string, - packetCommitmentSequences: readonly number[], - ) => Promise; - readonly unreceivedAcks: ( - portId: string, - channelId: string, - packetCommitmentSequences: readonly number[], - ) => Promise; - readonly nextSequenceReceive: ( - portId: string, - channelId: string, - ) => Promise; - - // Queries for ibc.core.connection.v1 - - readonly connection: (connectionId: string) => Promise; - readonly connections: (paginationKey?: Uint8Array) => Promise; - readonly clientConnections: (clientId: string) => Promise; + readonly channel: { + readonly channel: (portId: string, channelId: string) => Promise; + readonly channels: (paginationKey?: Uint8Array) => Promise; + readonly allChannels: () => Promise; + readonly connectionChannels: ( + connection: string, + paginationKey?: Uint8Array, + ) => Promise; + readonly allConnectionChannels: (connection: string) => Promise; + readonly clientState: (portId: string, channelId: string) => Promise; + readonly consensusState: ( + portId: string, + channelId: string, + revisionNumber: number, + revisionHeight: number, + ) => Promise; + readonly packetCommitment: ( + portId: string, + channelId: string, + sequence: Long, + ) => Promise; + readonly packetCommitments: ( + portId: string, + channelId: string, + paginationKey?: Uint8Array, + ) => Promise; + readonly allPacketCommitments: ( + portId: string, + channelId: string, + ) => Promise; + readonly packetReceipt: ( + portId: string, + channelId: string, + sequence: number, + ) => Promise; + readonly packetAcknowledgement: ( + portId: string, + channelId: string, + sequence: number, + ) => Promise; + readonly packetAcknowledgements: ( + portId: string, + channelId: string, + paginationKey?: Uint8Array, + ) => Promise; + readonly allPacketAcknowledgements: ( + portId: string, + channelId: string, + ) => Promise; + readonly unreceivedPackets: ( + portId: string, + channelId: string, + packetCommitmentSequences: readonly number[], + ) => Promise; + readonly unreceivedAcks: ( + portId: string, + channelId: string, + packetAckSequences: readonly number[], + ) => Promise; + readonly nextSequenceReceive: ( + portId: string, + channelId: string, + ) => Promise; + }; + readonly client: { + readonly state: (clientId: string) => Promise; + readonly states: (paginationKey?: Uint8Array) => Promise; + readonly allStates: () => Promise; + readonly consensusState: (clientId: string, height?: number) => Promise; + readonly consensusStates: ( + clientId: string, + paginationKey?: Uint8Array, + ) => Promise; + readonly allConsensusStates: (clientId: string) => Promise; + readonly params: () => Promise; + readonly stateTm: (clientId: string) => Promise; + readonly statesTm: (paginationKey?: Uint8Array) => Promise; + readonly allStatesTm: () => Promise; + readonly consensusStateTm: (clientId: string, height?: Height) => Promise; + }; + readonly connection: { + readonly connection: (connectionId: string) => Promise; + readonly connections: (paginationKey?: Uint8Array) => Promise; + readonly allConnections: () => Promise; + readonly clientConnections: (clientId: string) => Promise; + readonly clientState: (connectionId: string) => Promise; + readonly consensusState: ( + connectionId: string, + revisionNumber: number, + revisionHeight: number, + ) => Promise; + }; }; }; } @@ -93,6 +173,7 @@ export function setupIbcExtension(base: QueryClient): IbcExtension { // Use these services to get easy typed access to query methods // These cannot be used for proof verification const channelQueryService = new ChannelQuery(rpc); + const clientQueryService = new ClientQuery(rpc); const connectionQueryService = new ConnectionQuery(rpc); return { @@ -129,103 +210,286 @@ export function setupIbcExtension(base: QueryClient): IbcExtension { }, unverified: { - // Queries for ibc.core.channel.v1 - channel: async (portId: string, channelId: string) => { - const response = await channelQueryService.Channel({ portId: portId, channelId: channelId }); - return response; + channel: { + channel: async (portId: string, channelId: string) => + channelQueryService.Channel({ + portId: portId, + channelId: channelId, + }), + channels: async (paginationKey?: Uint8Array) => + channelQueryService.Channels({ + pagination: createPagination(paginationKey), + }), + allChannels: async () => { + const channels = []; + let response: QueryChannelsResponse; + let key: Uint8Array | undefined; + do { + response = await channelQueryService.Channels({ + pagination: createPagination(key), + }); + channels.push(...response.channels); + key = response.pagination?.nextKey; + } while (key); + return { + channels: channels, + height: response.height, + }; + }, + connectionChannels: async (connection: string, paginationKey?: Uint8Array) => + channelQueryService.ConnectionChannels({ + connection: connection, + pagination: createPagination(paginationKey), + }), + allConnectionChannels: async (connection: string) => { + const channels = []; + let response: QueryConnectionChannelsResponse; + let key: Uint8Array | undefined; + do { + response = await channelQueryService.ConnectionChannels({ + connection: connection, + pagination: createPagination(key), + }); + channels.push(...response.channels); + key = response.pagination?.nextKey; + } while (key); + return { + channels: channels, + height: response.height, + }; + }, + clientState: async (portId: string, channelId: string) => + channelQueryService.ChannelClientState({ + portId: portId, + channelId: channelId, + }), + consensusState: async ( + portId: string, + channelId: string, + revisionNumber: number, + revisionHeight: number, + ) => + channelQueryService.ChannelConsensusState({ + portId: portId, + channelId: channelId, + revisionNumber: Long.fromNumber(revisionNumber, true), + revisionHeight: Long.fromNumber(revisionHeight, true), + }), + packetCommitment: async (portId: string, channelId: string, sequence: Long) => + channelQueryService.PacketCommitment({ + portId: portId, + channelId: channelId, + sequence: sequence, + }), + packetCommitments: async (portId: string, channelId: string, paginationKey?: Uint8Array) => + channelQueryService.PacketCommitments({ + channelId: channelId, + portId: portId, + pagination: createPagination(paginationKey), + }), + allPacketCommitments: async (portId: string, channelId: string) => { + const commitments = []; + let response: QueryPacketCommitmentsResponse; + let key: Uint8Array | undefined; + do { + response = await channelQueryService.PacketCommitments({ + channelId: channelId, + portId: portId, + pagination: createPagination(key), + }); + commitments.push(...response.commitments); + key = response.pagination?.nextKey; + } while (key); + return { + commitments: commitments, + height: response.height, + }; + }, + packetReceipt: async (portId: string, channelId: string, sequence: number) => + channelQueryService.PacketReceipt({ + portId: portId, + channelId: channelId, + sequence: Long.fromNumber(sequence, true), + }), + packetAcknowledgement: async (portId: string, channelId: string, sequence: number) => + channelQueryService.PacketAcknowledgement({ + portId: portId, + channelId: channelId, + sequence: Long.fromNumber(sequence, true), + }), + packetAcknowledgements: async (portId: string, channelId: string, paginationKey?: Uint8Array) => + channelQueryService.PacketAcknowledgements({ + portId: portId, + channelId: channelId, + pagination: createPagination(paginationKey), + }), + allPacketAcknowledgements: async (portId: string, channelId: string) => { + const acknowledgements = []; + let response: QueryPacketAcknowledgementsResponse; + let key: Uint8Array | undefined; + do { + response = await channelQueryService.PacketAcknowledgements({ + channelId: channelId, + portId: portId, + pagination: createPagination(key), + }); + acknowledgements.push(...response.acknowledgements); + key = response.pagination?.nextKey; + } while (key); + return { + acknowledgements: acknowledgements, + height: response.height, + }; + }, + unreceivedPackets: async ( + portId: string, + channelId: string, + packetCommitmentSequences: readonly number[], + ) => + channelQueryService.UnreceivedPackets({ + portId: portId, + channelId: channelId, + packetCommitmentSequences: packetCommitmentSequences.map((s) => Long.fromNumber(s, true)), + }), + unreceivedAcks: async (portId: string, channelId: string, packetAckSequences: readonly number[]) => + channelQueryService.UnreceivedAcks({ + portId: portId, + channelId: channelId, + packetAckSequences: packetAckSequences.map((s) => Long.fromNumber(s, true)), + }), + nextSequenceReceive: async (portId: string, channelId: string) => + channelQueryService.NextSequenceReceive({ + portId: portId, + channelId: channelId, + }), }, - channels: async (paginationKey?: Uint8Array) => { - const request = { - pagination: createPagination(paginationKey), - }; - const response = await channelQueryService.Channels(request); - return response; + client: { + state: (clientId: string) => clientQueryService.ClientState({ clientId }), + states: (paginationKey?: Uint8Array) => + clientQueryService.ClientStates({ + pagination: createPagination(paginationKey), + }), + allStates: async () => { + const clientStates = []; + let response: QueryClientStatesResponse; + let key: Uint8Array | undefined; + do { + response = await clientQueryService.ClientStates({ + pagination: createPagination(key), + }); + clientStates.push(...response.clientStates); + key = response.pagination?.nextKey; + } while (key); + return { + clientStates: clientStates, + }; + }, + consensusState: (clientId: string, consensusHeight?: number) => + clientQueryService.ConsensusState( + QueryConsensusStateRequest.fromPartial({ + clientId: clientId, + revisionHeight: + consensusHeight !== undefined ? Long.fromNumber(consensusHeight, true) : undefined, + latestHeight: consensusHeight === undefined, + }), + ), + consensusStates: (clientId: string, paginationKey?: Uint8Array) => + clientQueryService.ConsensusStates({ + clientId: clientId, + pagination: createPagination(paginationKey), + }), + allConsensusStates: async (clientId: string) => { + const consensusStates = []; + let response: QueryConsensusStatesResponse; + let key: Uint8Array | undefined; + do { + response = await clientQueryService.ConsensusStates({ + clientId: clientId, + pagination: createPagination(key), + }); + consensusStates.push(...response.consensusStates); + key = response.pagination?.nextKey; + } while (key); + return { + consensusStates: consensusStates, + }; + }, + params: () => clientQueryService.ClientParams({}), + stateTm: async (clientId: string) => { + const response = await clientQueryService.ClientState({ clientId }); + return decodeTendermintClientStateAny(response.clientState); + }, + statesTm: async (paginationKey?: Uint8Array) => { + const { clientStates } = await clientQueryService.ClientStates({ + pagination: createPagination(paginationKey), + }); + return clientStates.map(({ clientState }) => decodeTendermintClientStateAny(clientState)); + }, + allStatesTm: async () => { + const clientStates = []; + let response: QueryClientStatesResponse; + let key: Uint8Array | undefined; + do { + response = await clientQueryService.ClientStates({ + pagination: createPagination(key), + }); + clientStates.push(...response.clientStates); + key = response.pagination?.nextKey; + } while (key); + return clientStates.map(({ clientState }) => decodeTendermintClientStateAny(clientState)); + }, + consensusStateTm: async (clientId: string, consensusHeight?: Height) => { + const response = await clientQueryService.ConsensusState( + QueryConsensusStateRequest.fromPartial({ + clientId: clientId, + revisionHeight: consensusHeight?.revisionHeight, + revisionNumber: consensusHeight?.revisionNumber, + latestHeight: consensusHeight === undefined, + }), + ); + return decodeTendermintConsensusStateAny(response.consensusState); + }, }, - connectionChannels: async (connection: string, paginationKey?: Uint8Array) => { - const request = { - connection: connection, - pagination: createPagination(paginationKey), - }; - const response = await channelQueryService.ConnectionChannels(request); - return response; - }, - packetCommitment: async (portId: string, channelId: string, sequence: number) => { - const response = await channelQueryService.PacketCommitment({ - portId: portId, - channelId: channelId, - sequence: Long.fromNumber(sequence, true), - }); - return response; - }, - packetCommitments: async (portId: string, channelId: string, paginationKey?: Uint8Array) => { - const request = { - channelId: channelId, - portId: portId, - pagination: createPagination(paginationKey), - }; - const response = await channelQueryService.PacketCommitments(request); - return response; - }, - packetAcknowledgement: async (portId: string, channelId: string, sequence: number) => { - const response = await channelQueryService.PacketAcknowledgement({ - portId: portId, - channelId: channelId, - sequence: Long.fromNumber(sequence, true), - }); - return response; - }, - packetAcknowledgements: async (portId: string, channelId: string, paginationKey?: Uint8Array) => { - const response = await channelQueryService.PacketAcknowledgements({ - portId: portId, - channelId: channelId, - pagination: createPagination(paginationKey), - }); - return response; - }, - unreceivedPackets: async ( - portId: string, - channelId: string, - packetCommitmentSequences: readonly number[], - ) => { - const response = await channelQueryService.UnreceivedPackets({ - portId: portId, - channelId: channelId, - packetCommitmentSequences: packetCommitmentSequences.map((s) => Long.fromNumber(s, true)), - }); - return response; - }, - unreceivedAcks: async (portId: string, channelId: string, packetAckSequences: readonly number[]) => { - const response = await channelQueryService.UnreceivedAcks({ - portId: portId, - channelId: channelId, - packetAckSequences: packetAckSequences.map((s) => Long.fromNumber(s, true)), - }); - return response; - }, - nextSequenceReceive: async (portId: string, channelId: string) => { - const response = await channelQueryService.NextSequenceReceive({ - portId: portId, - channelId: channelId, - }); - return response; - }, - - // Queries for ibc.core.connection.v1 - - connection: async (connectionId: string) => { - const response = await connectionQueryService.Connection({ connectionId: connectionId }); - return response; - }, - connections: async (paginationKey?: Uint8Array) => { - const request = { - pagination: createPagination(paginationKey), - }; - const response = await connectionQueryService.Connections(request); - return response; - }, - clientConnections: async (clientId: string) => { - const response = await connectionQueryService.ClientConnections({ clientId: clientId }); - return response; + connection: { + connection: async (connectionId: string) => + connectionQueryService.Connection({ + connectionId: connectionId, + }), + connections: async (paginationKey?: Uint8Array) => + connectionQueryService.Connections({ + pagination: createPagination(paginationKey), + }), + allConnections: async () => { + const connections = []; + let response: QueryConnectionsResponse; + let key: Uint8Array | undefined; + do { + response = await connectionQueryService.Connections({ + pagination: createPagination(key), + }); + connections.push(...response.connections); + key = response.pagination?.nextKey; + } while (key); + return { + connections: connections, + height: response.height, + }; + }, + clientConnections: async (clientId: string) => + connectionQueryService.ClientConnections({ + clientId: clientId, + }), + clientState: async (connectionId: string) => + connectionQueryService.ConnectionClientState({ + connectionId: connectionId, + }), + consensusState: async (connectionId: string, revisionHeight: number) => + connectionQueryService.ConnectionConsensusState( + QueryConnectionConsensusStateRequest.fromPartial({ + connectionId: connectionId, + revisionHeight: Long.fromNumber(revisionHeight, true), + }), + ), }, }, },