Skip to content

Commit

Permalink
shuffle channel config and provide a method to accurately determine i…
Browse files Browse the repository at this point in the history
…f a chain is gateway enabled
  • Loading branch information
barnjamin committed Oct 13, 2023
1 parent 65c5fb4 commit 9a7229e
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 100 deletions.
2 changes: 0 additions & 2 deletions connect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ export * as circle from "./circle-api";
export * as api from "./api";

// Re-export from core packages

/** @namespace */
export {
contracts,
Chain,
Expand Down
53 changes: 33 additions & 20 deletions connect/src/protocols/gatewayTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ export class GatewayTransfer implements WormholeTransfer {

private readonly wh: Wormhole;

private readonly wc: ChainContext<PlatformName>;
// Wormchain context
private readonly gateway: ChainContext<PlatformName>;
// Wormchain IBC Bridge
private readonly gatewayIbcBridge: IbcBridge<PlatformName>;
// Contract address
private readonly gatewayAddress: ChainAddress;

// state machine tracker
Expand All @@ -70,7 +74,12 @@ export class GatewayTransfer implements WormholeTransfer {
// Any transfers we do over ibc
ibcTransfers?: IbcTransferInfo[];

private constructor(wh: Wormhole, transfer: GatewayTransferDetails) {
private constructor(
wh: Wormhole,
transfer: GatewayTransferDetails,
gateway: ChainContext<PlatformName>,
gatewayIbc: IbcBridge<PlatformName>,
) {
this.state = TransferState.Created;
this.wh = wh;
this.transfer = transfer;
Expand All @@ -86,7 +95,10 @@ export class GatewayTransfer implements WormholeTransfer {
};

// cache the wormchain chain context since we need it for checks
this.wc = this.wh.getChain(GatewayTransfer.chain);
this.gateway = gateway;
this.gatewayIbcBridge = gatewayIbc;

// cache the message since we don't want to regenerate it any time we need it
this.msg = gatewayTransferMsg(this.transfer);
}

Expand All @@ -112,8 +124,10 @@ export class GatewayTransfer implements WormholeTransfer {
from: GatewayTransferDetails | WormholeMessageId | TransactionId,
): Promise<GatewayTransfer> {
// Fresh new transfer
const wc = wh.getChain(GatewayTransfer.chain);
const wcibc = await wc.getIbcBridge();
if (isGatewayTransferDetails(from)) {
return new GatewayTransfer(wh, from);
return new GatewayTransfer(wh, from, wc, wcibc);
}

// Picking up where we left off
Expand All @@ -129,7 +143,7 @@ export class GatewayTransfer implements WormholeTransfer {
throw new Error("Invalid `from` parameter for GatewayTransfer");
}

const gt = new GatewayTransfer(wh, gtd);
const gt = new GatewayTransfer(wh, gtd, wc, wcibc);
gt.transactions = txns;

// Since we're picking up from somewhere we can move the
Expand Down Expand Up @@ -401,8 +415,6 @@ export class GatewayTransfer implements WormholeTransfer {
const attestations: AttestationId[] = [];
this.ibcTransfers = [];

const wcIbc = await this.wc.getIbcBridge();

// collect ibc transfers and additional transaction ids
if (this.fromGateway()) {
// assume all the txs are from the same chain
Expand Down Expand Up @@ -433,7 +445,8 @@ export class GatewayTransfer implements WormholeTransfer {

// now find the corresponding wormchain transaction given the ibcTransfer info
const retryInterval = 5000;
const task = () => wcIbc.lookupMessageFromIbcMsgId(xfer.id);
const task = () =>
this.gatewayIbcBridge.lookupMessageFromIbcMsgId(xfer.id);
const whm = await retry<WormholeMessageId>(task, retryInterval);
if (!whm)
throw new Error(
Expand Down Expand Up @@ -472,7 +485,7 @@ export class GatewayTransfer implements WormholeTransfer {

// Wait until the vaa is redeemed before trying to look up the
// transfer message
const wcTb = await this.wc.getTokenBridge();
const wcTb = await this.gateway.getTokenBridge();
// Since we want to retry until its redeemed, return null
// in the case that its not redeemed
const isRedeemedTask = async () => {
Expand All @@ -487,7 +500,8 @@ export class GatewayTransfer implements WormholeTransfer {
// Note: Because we search by GatewayTransferMsg payload
// there is a possibility of dupe messages being returned
// using a nonce should help
const wcTransferTask = () => fetchIbcXfer(wcIbc, this.msg);
const wcTransferTask = () =>
fetchIbcXfer(this.gatewayIbcBridge, this.msg);
const wcTransfer = await retry<IbcTransferInfo>(
wcTransferTask,
retryInterval,
Expand Down Expand Up @@ -588,18 +602,17 @@ export class GatewayTransfer implements WormholeTransfer {
throw new Error(`No serde defined for type: ${partial.payload[0]}`);
}

// TODO: Is this a good enough check for what we want to do?
// Implicitly determine if the chain is Gateway enabled by
// checking to see if the Gateway IBC bridge has a transfer channel setup
private fromGateway(): boolean {
//IbcBridge.getChannels();

return chainToPlatform(this.transfer.from.chain) === "Cosmwasm";
// return networkChainToChannelId.has(
// this.wh.network,
// this.transfer.from.chain,
// );
return (
this.gatewayIbcBridge.getTransferChannel(this.transfer.from.chain) !==
null
);
}
private toGateway(): boolean {
return chainToPlatform(this.transfer.to.chain) === "Cosmwasm";
// return networkChainToChannelId.has(this.wh.network, this.transfer.to.chain);
return (
this.gatewayIbcBridge.getTransferChannel(this.transfer.to.chain) !== null
);
}
}
10 changes: 6 additions & 4 deletions core/definitions/src/protocols/ibc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export function gatewayTransferMsg(

export function makeGatewayTransferMsg(
chain: ChainName | ChainId,
recipient: NativeAddress<"Cosmwasm"> | string,
recipient: NativeAddress<PlatformName> | string,
fee: bigint = 0n,
nonce: number,
payload?: string,
Expand All @@ -161,7 +161,7 @@ export function makeGatewayTransferMsg(
const address =
typeof recipient === "string"
? recipient
: // @ts-ignore
: //@ts-ignore
Buffer.from(recipient.toString()).toString("base64");

const common = {
Expand Down Expand Up @@ -229,8 +229,10 @@ export interface IbcBridge<P extends PlatformName> {
): AsyncGenerator<UnsignedTransaction>;

// cached from config
//getChannels(): IbcChannel | null;
//fetchChannels(): Promise<IbcChannel | null>;
getTransferChannel(chain: ChainName): string | null;

// fetched from contract
fetchTransferChannel(chain: ChainName): Promise<string | null>;

lookupMessageFromIbcMsgId(msg: IbcMessageId): Promise<WormholeMessageId>;

Expand Down
40 changes: 11 additions & 29 deletions platforms/cosmwasm/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,50 +148,32 @@ export const cosmwasmNetworkChainToRestUrls = constMap(
cosmwasmNetworkChainRestUrl,
);

export type IbcChannel = {
srcChannel: string;
dstChannel: string;
};
export type IbcChannels = Partial<Record<CosmwasmChainName, string>>;

// IBC Channels from the perspective of Wormchain
// For each chain, add the channel id for each other chain
const gatewayConnections = [
[
"Mainnet",
[
["Cosmoshub", { srcChannel: "channel-5", dstChannel: "" }],
["Osmosis", { srcChannel: "channel-4", dstChannel: "" }],
],
[["Wormchain", { Cosmoshub: "channel-5", Osmosis: "channel-4" }]],
],
[
"Testnet",
[
[
"Cosmoshub",
{
srcChannel: "channel-5",
dstChannel: "channel-3086",
},
],
[
"Osmosis",
{
srcChannel: "channel-9",
dstChannel: "channel-3906",
},
],
["Wormchain", { Cosmoshub: "channel-5", Osmosis: "channel-9" }],
["Cosmoshub", { Wormchain: "channel-3086" }],
["Osmosis", { Wormchain: "channel-3906" }],
],
],
[
"Devnet",
[
["Cosmoshub", { srcChannel: "", dstChannel: "" }],
["Osmosis", { srcChannel: "", dstChannel: "" }],
["Wormchain", { Cosmoshub: "channel-1", Osmosis: "channel-2" }],
["Cosmoshub", { Wormchain: "channel-1" }],
["Osmosis", { Wormchain: "channel-1" }],
],
],
] as const satisfies RoArray<
readonly [Network, RoArray<readonly [CosmwasmChainName, IbcChannel]>]
readonly [Network, RoArray<readonly [CosmwasmChainName, IbcChannels]>]
>;

export const networkChainToChannelId = constMap(gatewayConnections);
export const networkChannelToChain = constMap(gatewayConnections, [0, [2, 1]]);
export const networkToChannelMap = constMap(gatewayConnections, [0, [1, 2]]);
export const networkChainToChannels = constMap(gatewayConnections);
19 changes: 12 additions & 7 deletions platforms/cosmwasm/src/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,27 @@ export module Gateway {
return new CosmwasmAddress(factoryAddress);
}

// Returns the destination channel on wormchain for given source chain
// Returns the destination channel from the perspective of wormchain for given source chain
export function getDestinationChannel(chain: CosmwasmChainName): string {
const channels = CosmwasmPlatform.getIbcChannel(chain);
const channels = CosmwasmPlatform.getIbcChannels(Gateway.name);
if (!channels) throw new Error("No channels configured for chain " + chain);
return channels.dstChannel;
if (!(chain in channels))
throw new Error("No channel configured for chain " + chain);
return channels[chain]!;
}
// Gets the source channel on wormchain for a given chain

// Gets the source channel from the perspective of wormchain for a given chain
export function getSourceChannel(chain: CosmwasmChainName): string {
const channels = CosmwasmPlatform.getIbcChannel(chain);
const channels = CosmwasmPlatform.getIbcChannels(chain);
if (!channels) throw new Error("No channels configured for chain " + chain);
return channels.srcChannel;
if (!(Gateway.name in channels))
throw new Error("No channel configured for chain " + chain);
return channels[Gateway.name]!;
}

// Returns whether or not a given chain is gateway supported
export function isSupported(chain: CosmwasmChainName): boolean {
return CosmwasmPlatform.getIbcChannel(chain) !== null;
return CosmwasmPlatform.getIbcChannels(chain) !== null;
}

// Derive the Token Address with context for whether or not its managed
Expand Down
12 changes: 6 additions & 6 deletions platforms/cosmwasm/src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import {

import { CosmwasmChain } from "./chain";
import {
IbcChannel,
IbcChannels,
chainToNativeDenoms,
networkChainToChannelId,
networkChainToChannels,
} from "./constants";
import { CosmwasmContracts } from "./contracts";
import { Gateway } from "./gateway";
Expand Down Expand Up @@ -118,11 +118,11 @@ export module CosmwasmPlatform {
};

// cached channels from config if available
export const getIbcChannel = (
export const getIbcChannels = (
chain: CosmwasmChainName,
): IbcChannel | null => {
return networkChainToChannelId.has(network, chain)
? networkChainToChannelId.get(network, chain)!
): IbcChannels | null => {
return networkChainToChannels.has(network, chain)
? networkChainToChannels.get(network, chain)!
: null;
};
}
56 changes: 24 additions & 32 deletions platforms/cosmwasm/src/protocols/ibc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { IndexedTx, MsgTransferEncodeObject, coin } from "@cosmjs/stargate";
import {
ChainAddress,
ChainName,
GatewayIbcTransferMsg,
GatewayTransferMsg,
GatewayTransferWithPayloadMsg,
Expand Down Expand Up @@ -30,8 +31,8 @@ import {
IBC_PACKET_SRC_PORT,
IBC_TIMEOUT_MILLIS,
IBC_TRANSFER_PORT,
IbcChannel,
networkToChannelMap,
IbcChannels,
networkChainToChannels,
} from "../constants";
import { CosmwasmContracts } from "../contracts";
import { Gateway } from "../gateway";
Expand All @@ -51,7 +52,8 @@ export class CosmwasmIbcBridge implements IbcBridge<"Cosmwasm"> {
private gatewayChannel?: string;

// map the local channel ids to the remote chain
private channelMap: Map<string, CosmwasmChainName> = new Map();
private channelToChain: Map<string, CosmwasmChainName> = new Map();
private chainToChannel: Map<CosmwasmChainName, string> = new Map();

private constructor(
readonly network: Network,
Expand All @@ -61,15 +63,15 @@ export class CosmwasmIbcBridge implements IbcBridge<"Cosmwasm"> {
) {
this.gatewayAddress = this.contracts.getContracts(Gateway.name).gateway!;

const isGateway = this.chain === Gateway.name;
for (const [chain, channel] of networkToChannelMap(network)) {
if (!isGateway && this.chain === chain)
this.gatewayChannel = channel.dstChannel;
if (!networkChainToChannels.has(network, chain))
throw new Error("Unsupported IBC Chain, no channels available: " + chain);

this.channelMap.set(
channel[isGateway ? "dstChannel" : "srcChannel"],
chain,
);
// @ts-ignore
const channels: IbcChannels = networkChainToChannels(network, chain);

for (const [chain, channel] of Object.entries(channels)) {
this.channelToChain.set(channel, chain as CosmwasmChainName);
this.chainToChannel.set(chain as CosmwasmChainName, channel);
}
}

Expand All @@ -81,6 +83,10 @@ export class CosmwasmIbcBridge implements IbcBridge<"Cosmwasm"> {
return new CosmwasmIbcBridge(network, chain, rpc, contracts);
}

getTransferChannel(chain: ChainName): string | null {
return this.chainToChannel.get(chain as CosmwasmChainName) ?? null;
}

async *transfer(
sender: UniversalOrCosmwasm,
recipient: ChainAddress,
Expand Down Expand Up @@ -299,7 +305,7 @@ export class CosmwasmIbcBridge implements IbcBridge<"Cosmwasm"> {
msgId.chain =
packet.type === IBC_PACKET_SEND
? this.chain
: this.channelMap.get(msgId.srcChannel!)!;
: this.channelToChain.get(msgId.srcChannel!)!;

// Note: using the type guard to tell us if we have all the fields we expect
if (isIbcMessageId(msgId)) xfer.id = msgId;
Expand Down Expand Up @@ -341,26 +347,12 @@ export class CosmwasmIbcBridge implements IbcBridge<"Cosmwasm"> {
return transfers;
}

// Fetches the channel information between wormchain and a given chain
async fetchChannel(chain: CosmwasmChainName): Promise<IbcChannel> {
const queryClient = CosmwasmUtils.asQueryClient(this.rpc);

const { channel: srcChannel } = await this.rpc.queryContractSmart(
this.gatewayAddress,
{ ibc_channel: { chain_id: toChainId(chain) } },
);
const conn = await queryClient.ibc.channel.channel(
IBC_TRANSFER_PORT,
srcChannel,
);

const dstChannel = conn.channel?.counterparty?.channelId;
if (!dstChannel)
throw new Error(
`No destination channel found for chain ${chain} on ${this.chain}`,
);

return { srcChannel, dstChannel };
// Fetches the local channel for the given chain
async fetchTransferChannel(chain: CosmwasmChainName): Promise<string> {
const { channel } = await this.rpc.queryContractSmart(this.gatewayAddress, {
ibc_channel: { chain_id: toChainId(chain) },
});
return channel;
}

private createUnsignedTx(
Expand Down

0 comments on commit 9a7229e

Please sign in to comment.