@agoric/network 0.1.1-dev-91a9ad7.0.91a9ad7 → 0.1.1-dev-c35956d.0.c35956d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +13 -10
- package/src/ibc/index.d.ts +2 -0
- package/src/ibc/index.d.ts.map +1 -0
- package/src/ibc/index.js +1 -0
- package/src/ibc/types.d.ts +36 -0
- package/src/ibc/types.d.ts.map +1 -0
- package/src/ibc/types.js +44 -0
- package/src/ibc/utils.d.ts +67 -0
- package/src/ibc/utils.d.ts.map +1 -0
- package/src/ibc/utils.js +270 -0
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ You must also prepare a `ConnectionHandler` object to manage the connection you'
|
|
|
48
48
|
Then you will call the `connect()` method on your local `Port`. This will return a `Promise` that will fire with a new `Connection` object, on which you can send data. Your `ConnectionHandler` will be notified about the new channel, and will receive inbound data from the other side.
|
|
49
49
|
|
|
50
50
|
```js
|
|
51
|
-
import { encodeIbcEndpoint } from '@agoric/
|
|
51
|
+
import { encodeIbcEndpoint } from '@agoric/network/ibc/utils.js';
|
|
52
52
|
const remoteEndpoint = encodeIbcEndpoint({
|
|
53
53
|
hops: [connectionID],
|
|
54
54
|
portID,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/network",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-dev-c35956d.0.c35956d",
|
|
4
4
|
"description": "Agoric's network protocol API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"author": "Agoric",
|
|
22
22
|
"license": "Apache-2.0",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@agoric/internal": "0.3.3-dev-
|
|
25
|
-
"@agoric/store": "0.9.3-dev-
|
|
26
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
24
|
+
"@agoric/internal": "0.3.3-dev-c35956d.0.c35956d",
|
|
25
|
+
"@agoric/store": "0.9.3-dev-c35956d.0.c35956d",
|
|
26
|
+
"@agoric/vat-data": "0.5.3-dev-c35956d.0.c35956d",
|
|
27
27
|
"@endo/base64": "^1.0.12",
|
|
28
28
|
"@endo/errors": "^1.2.13",
|
|
29
29
|
"@endo/far": "^1.1.14",
|
|
@@ -32,16 +32,19 @@
|
|
|
32
32
|
"@endo/promise-kit": "^1.1.13"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@agoric/
|
|
36
|
-
"@agoric/swingset-
|
|
37
|
-
"@agoric/
|
|
38
|
-
"@agoric/
|
|
35
|
+
"@agoric/cosmic-proto": "0.4.1-dev-c35956d.0.c35956d",
|
|
36
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-c35956d.0.c35956d",
|
|
37
|
+
"@agoric/swingset-vat": "0.32.3-dev-c35956d.0.c35956d",
|
|
38
|
+
"@agoric/vow": "0.1.1-dev-c35956d.0.c35956d",
|
|
39
|
+
"@agoric/zone": "0.2.3-dev-c35956d.0.c35956d",
|
|
39
40
|
"@endo/bundle-source": "^4.1.2",
|
|
40
41
|
"ava": "^6.4.1",
|
|
41
42
|
"c8": "^10.1.3"
|
|
42
43
|
},
|
|
43
44
|
"exports": {
|
|
44
|
-
".": "./src/index.js"
|
|
45
|
+
".": "./src/index.js",
|
|
46
|
+
"./ibc": "./src/ibc/index.js",
|
|
47
|
+
"./ibc/utils.js": "./src/ibc/utils.js"
|
|
45
48
|
},
|
|
46
49
|
"files": [
|
|
47
50
|
"CHANGELOG.md",
|
|
@@ -67,5 +70,5 @@
|
|
|
67
70
|
"typeCoverage": {
|
|
68
71
|
"atLeast": 91.16
|
|
69
72
|
},
|
|
70
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "c35956dcf414e6cc7f24db886cb647f36cb1eb90"
|
|
71
74
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":""}
|
package/src/ibc/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { State as IBCChannelState, Order } from '@agoric/cosmic-proto/ibc/core/channel/v1/channel.js';
|
|
2
|
+
import type { State as IBCConnectionState } from '@agoric/cosmic-proto/ibc/core/connection/v1/connection.js';
|
|
3
|
+
export type RemoteIbcAddress = `/${string}ibc-port/${string}/${'ordered' | 'unordered'}/${string}`;
|
|
4
|
+
export type LocalIbcAddress = `/ibc-port/${string}`;
|
|
5
|
+
export type IBCConnectionID = `connection-${number}`;
|
|
6
|
+
export type IBCChannelOrdering = 'ORDERED' | 'UNORDERED';
|
|
7
|
+
export type IBCPortID = string;
|
|
8
|
+
export type IBCChannelID = `channel-${number}`;
|
|
9
|
+
export interface IBCEndpoint {
|
|
10
|
+
hops: IBCConnectionID[];
|
|
11
|
+
portID: IBCPortID;
|
|
12
|
+
order?: IBCChannelOrdering;
|
|
13
|
+
version?: string;
|
|
14
|
+
channelID?: IBCChannelID;
|
|
15
|
+
}
|
|
16
|
+
export interface IBCTransferChannelInfo {
|
|
17
|
+
portId: string;
|
|
18
|
+
channelId: IBCChannelID;
|
|
19
|
+
counterPartyPortId: string;
|
|
20
|
+
counterPartyChannelId: IBCChannelID;
|
|
21
|
+
ordering: Order;
|
|
22
|
+
state: IBCChannelState;
|
|
23
|
+
version: string;
|
|
24
|
+
}
|
|
25
|
+
/** Represents an IBC Connection between two chains, which can contain multiple Channels. */
|
|
26
|
+
export interface IBCConnectionInfo {
|
|
27
|
+
id: IBCConnectionID;
|
|
28
|
+
client_id: string;
|
|
29
|
+
state: IBCConnectionState;
|
|
30
|
+
counterparty: {
|
|
31
|
+
client_id: string;
|
|
32
|
+
connection_id: IBCConnectionID;
|
|
33
|
+
};
|
|
34
|
+
transferChannel: IBCTransferChannelInfo;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,IAAI,eAAe,EACxB,KAAK,EACN,MAAM,qDAAqD,CAAC;AAC7D,OAAO,KAAK,EAAE,KAAK,IAAI,kBAAkB,EAAE,MAAM,2DAA2D,CAAC;AAE7G,MAAM,MAAM,gBAAgB,GAC1B,IAAI,MAAM,YAAY,MAAM,IAAI,SAAS,GAAG,WAAW,IAAI,MAAM,EAAE,CAAC;AACtE,MAAM,MAAM,eAAe,GAAG,aAAa,MAAM,EAAE,CAAC;AAEpD,MAAM,MAAM,eAAe,GAAG,cAAc,MAAM,EAAE,CAAC;AACrD,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,WAAW,CAAC;AACzD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAC/B,MAAM,MAAM,YAAY,GAAG,WAAW,MAAM,EAAE,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,YAAY,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,YAAY,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,YAAY,CAAC;IACpC,QAAQ,EAAE,KAAK,CAAC;IAChB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,4FAA4F;AAC5F,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,eAAe,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,YAAY,EAAE;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,eAAe,CAAC;KAChC,CAAC;IACF,eAAe,EAAE,sBAAsB,CAAC;CACzC"}
|
package/src/ibc/types.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/** Represents an IBC Connection between two chains, which can contain multiple Channels. */
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Conversion from IBC endpoints to Agoric Network API Endpoints
|
|
3
|
+
* @summary Utilities for constructing and parsing Agoric Network API IBC addresses, which
|
|
4
|
+
* are used to identify IBC ports and channels in a Cosmos-participating
|
|
5
|
+
* blockchain.
|
|
6
|
+
*
|
|
7
|
+
* Agoric Network API addresses are roughly akin to Multiaddrs (@ref
|
|
8
|
+
* https://github.com/multiformats/js-multiaddr) but with a less rigid
|
|
9
|
+
* structure. Both share a similar textual representation, which is
|
|
10
|
+
* encapsulated protocols and values separated by slashes.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* @import {Endpoint} from '../types.js';
|
|
14
|
+
* @import {
|
|
15
|
+
* IBCChannelID,
|
|
16
|
+
* IBCChannelOrdering,
|
|
17
|
+
* IBCConnectionID,
|
|
18
|
+
* IBCEndpoint,
|
|
19
|
+
* IBCPortID,
|
|
20
|
+
* LocalIbcAddress,
|
|
21
|
+
* RemoteIbcAddress,
|
|
22
|
+
* } from './types.js';
|
|
23
|
+
*/
|
|
24
|
+
export const IBC_ADDR_RE: RegExp;
|
|
25
|
+
export const VALID_ORDERS: readonly ["ordered", "unordered", "ORDERED", "UNORDERED"];
|
|
26
|
+
export function encodeSlashes(raw: string): string;
|
|
27
|
+
export function decodeSlashes(encoded: string): string;
|
|
28
|
+
export function validateIbcAddress(addr: any): any;
|
|
29
|
+
export function decodeIbcHops(h: string): IBCConnectionID[];
|
|
30
|
+
export function decodeIbcEndpoint(addr: string): IBCEndpoint;
|
|
31
|
+
export function encodeIbcEndpoint({ hops, portID, order: rawOrder, version, channelID, }: {
|
|
32
|
+
hops?: string[] | undefined;
|
|
33
|
+
portID: string;
|
|
34
|
+
order: "ordered" | "unordered" | "ORDERED" | "UNORDERED";
|
|
35
|
+
version: string;
|
|
36
|
+
channelID?: string | undefined;
|
|
37
|
+
}): Endpoint;
|
|
38
|
+
/**
|
|
39
|
+
* @overload
|
|
40
|
+
* @param {string} remoteAddr
|
|
41
|
+
* @param {undefined | false} [returnMatch]
|
|
42
|
+
* @returns {boolean}
|
|
43
|
+
*/
|
|
44
|
+
export function validateRemoteIbcAddress(remoteAddr: string, returnMatch?: undefined | false): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* @overload
|
|
47
|
+
* @param {string} remoteAddr
|
|
48
|
+
* @param {true} returnMatch
|
|
49
|
+
* @returns {RegExpMatchArray}
|
|
50
|
+
*/
|
|
51
|
+
export function validateRemoteIbcAddress(remoteAddr: string, returnMatch: true): RegExpMatchArray;
|
|
52
|
+
export function decodeRemoteIbcAddress(remoteAddr: string): {
|
|
53
|
+
rPortID: string;
|
|
54
|
+
order: IBCChannelOrdering;
|
|
55
|
+
version: string;
|
|
56
|
+
hops: `connection-${number}`[];
|
|
57
|
+
};
|
|
58
|
+
export function localAddrToPortID(localAddr: LocalIbcAddress): string;
|
|
59
|
+
export function encodeRemoteIbcAddress(hops: string[], portID: string, order: "ordered" | "unordered" | "ORDERED" | "UNORDERED", version: string, channelID?: string): RemoteIbcAddress;
|
|
60
|
+
export function encodeLocalIbcAddress(portID: string, order: "ordered" | "unordered" | "ORDERED" | "UNORDERED", version: string): string;
|
|
61
|
+
import type { IBCConnectionID } from './types.js';
|
|
62
|
+
import type { IBCEndpoint } from './types.js';
|
|
63
|
+
import type { Endpoint } from '../types.js';
|
|
64
|
+
import type { IBCChannelOrdering } from './types.js';
|
|
65
|
+
import type { LocalIbcAddress } from './types.js';
|
|
66
|
+
import type { RemoteIbcAddress } from './types.js';
|
|
67
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["utils.js"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;GAWG;AAEH,iCACsJ;AAGtJ,qFAKG;AAII,mCADK,MAAM,UAC8C;AAIzD,uCADK,MAAM,UACsD;AAGjE,mDASN;AAQM,iCAHI,MAAM,GACJ,eAAe,EAAE,CAsB7B;AAOM,wCAHI,MAAM,GACJ,WAAW,CAkBvB;AAYM,0FAPJ;IAAwB,IAAI;IACP,MAAM,EAAnB,MAAM;IACkD,KAAK,EAA7D,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW;IACpC,OAAO,EAApB,MAAM;IACQ,SAAS;CAC/B,GAAU,QAAQ,CAwCpB;;;;;;;AAIE,qDACQ,MAAM,gBACN,SAAS,GAAG,KAAK,GACf,OAAO,CACnB;;;;;;;AAEE,qDACQ,MAAM,eACN,IAAI,GACF,gBAAgB,CAC5B;AA0BM,mDADK,MAAM;;;;;EAYjB;AAOM,6CAHI,eAAe,GACb,MAAM,CAqBlB;AAWM,6CAPI,MAAM,EAAE,UACR,MAAM,SACN,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,WACjD,MAAM,cACN,MAAM,GACJ,gBAAgB,CAiB5B;AAQM,8CAJI,MAAM,SACN,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,WACjD,MAAM,UAIhB;qCAtPS,YAAY;iCAAZ,YAAY;8BATK,aAAa;wCAS9B,YAAY;qCAAZ,YAAY;sCAAZ,YAAY"}
|
package/src/ibc/utils.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Conversion from IBC endpoints to Agoric Network API Endpoints
|
|
3
|
+
* @summary Utilities for constructing and parsing Agoric Network API IBC addresses, which
|
|
4
|
+
* are used to identify IBC ports and channels in a Cosmos-participating
|
|
5
|
+
* blockchain.
|
|
6
|
+
*
|
|
7
|
+
* Agoric Network API addresses are roughly akin to Multiaddrs (@ref
|
|
8
|
+
* https://github.com/multiformats/js-multiaddr) but with a less rigid
|
|
9
|
+
* structure. Both share a similar textual representation, which is
|
|
10
|
+
* encapsulated protocols and values separated by slashes.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @import {Endpoint} from '../types.js';
|
|
15
|
+
* @import {
|
|
16
|
+
* IBCChannelID,
|
|
17
|
+
* IBCChannelOrdering,
|
|
18
|
+
* IBCConnectionID,
|
|
19
|
+
* IBCEndpoint,
|
|
20
|
+
* IBCPortID,
|
|
21
|
+
* LocalIbcAddress,
|
|
22
|
+
* RemoteIbcAddress,
|
|
23
|
+
* } from './types.js';
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export const IBC_ADDR_RE =
|
|
27
|
+
/^(?<hops>\/ibc-hop\/[^/]+)*\/ibc-port\/(?<portID>[^/]+)(\/(?<order>ordered|unordered)\/(?<version>[^/]+)(\/ibc-channel\/(?<channelID>[^/]+))?)?$/s;
|
|
28
|
+
harden(IBC_ADDR_RE);
|
|
29
|
+
|
|
30
|
+
export const VALID_ORDERS = /** @type {const} */ ([
|
|
31
|
+
'ordered',
|
|
32
|
+
'unordered',
|
|
33
|
+
'ORDERED',
|
|
34
|
+
'UNORDERED',
|
|
35
|
+
]);
|
|
36
|
+
harden(VALID_ORDERS);
|
|
37
|
+
|
|
38
|
+
/** @param {string} raw */
|
|
39
|
+
export const encodeSlashes = raw => raw.replaceAll('/', '\\x2f');
|
|
40
|
+
harden(encodeSlashes);
|
|
41
|
+
|
|
42
|
+
/** @param {string} encoded */
|
|
43
|
+
export const decodeSlashes = encoded => encoded.replaceAll('\\x2f', '/');
|
|
44
|
+
harden(decodeSlashes);
|
|
45
|
+
|
|
46
|
+
export const validateIbcAddress = addr => {
|
|
47
|
+
const match = addr.match(IBC_ADDR_RE);
|
|
48
|
+
// .groups is to inform TS
|
|
49
|
+
if (!(match && match.groups)) {
|
|
50
|
+
throw TypeError(
|
|
51
|
+
`IBC address ${addr} must be '(/ibc-hop/CONNECTION)*/ibc-port/PORT/(ordered|unordered)/VERSION(/ibc-channel/CHANNEL_ID)?'`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return match;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Decode the /ibc-hop/CONNECTION segments of an IBC address.
|
|
59
|
+
*
|
|
60
|
+
* @param {string} h
|
|
61
|
+
* @returns {IBCConnectionID[]}
|
|
62
|
+
*/
|
|
63
|
+
export const decodeIbcHops = h => {
|
|
64
|
+
/** @type {IBCConnectionID[]} */
|
|
65
|
+
const hops = [];
|
|
66
|
+
|
|
67
|
+
/** @type {string} */
|
|
68
|
+
while (h) {
|
|
69
|
+
const m = h.match(/^\/ibc-hop\/(?<hop>[^/]+)/);
|
|
70
|
+
if (!m || !m.groups) {
|
|
71
|
+
throw Error(
|
|
72
|
+
`internal: ${JSON.stringify(h)} did not begin with "/ibc-hop/XXX"`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
h = h.slice(m[0].length);
|
|
76
|
+
|
|
77
|
+
const hop = /** @type {IBCConnectionID} */ (m.groups.hop);
|
|
78
|
+
assert(typeof hop === 'string' && hop.startsWith('connection-'));
|
|
79
|
+
hops.push(hop);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return hops;
|
|
83
|
+
};
|
|
84
|
+
harden(decodeIbcHops);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {string} addr
|
|
88
|
+
* @returns {IBCEndpoint}
|
|
89
|
+
*/
|
|
90
|
+
export const decodeIbcEndpoint = addr => {
|
|
91
|
+
const match = validateIbcAddress(addr);
|
|
92
|
+
if (!match.groups)
|
|
93
|
+
throw Error('Unexpected error, validateRemoteIbcAddress should throw.');
|
|
94
|
+
|
|
95
|
+
const hops = decodeIbcHops(match.groups.hops);
|
|
96
|
+
|
|
97
|
+
// Generate a circuit.
|
|
98
|
+
const { portID, version, channelID, order } = match.groups;
|
|
99
|
+
return {
|
|
100
|
+
hops,
|
|
101
|
+
portID,
|
|
102
|
+
...(order && { order: order.toUpperCase() }),
|
|
103
|
+
...(version && { version: decodeSlashes(version) }),
|
|
104
|
+
...(channelID && { channelID }),
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
harden(decodeIbcEndpoint);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {object} spec
|
|
111
|
+
* @param {string[]} [spec.hops]
|
|
112
|
+
* @param {string} spec.portID
|
|
113
|
+
* @param {'ordered' | 'unordered' | 'ORDERED' | 'UNORDERED'} spec.order
|
|
114
|
+
* @param {string} spec.version
|
|
115
|
+
* @param {string} [spec.channelID]
|
|
116
|
+
* @returns {Endpoint}
|
|
117
|
+
*/
|
|
118
|
+
export const encodeIbcEndpoint = ({
|
|
119
|
+
hops = [],
|
|
120
|
+
portID,
|
|
121
|
+
order: rawOrder,
|
|
122
|
+
version,
|
|
123
|
+
channelID,
|
|
124
|
+
}) => {
|
|
125
|
+
const isLegitString = str =>
|
|
126
|
+
typeof str === 'string' && str && !str.includes('/');
|
|
127
|
+
let addr = '';
|
|
128
|
+
assert(Array.isArray(hops), 'hops must be an array');
|
|
129
|
+
for (const hop of hops) {
|
|
130
|
+
assert(isLegitString(hop), `invalid hop ${hop}`);
|
|
131
|
+
addr += `/ibc-hop/${hop}`;
|
|
132
|
+
}
|
|
133
|
+
assert(isLegitString(portID), `invalid portID ${portID}`);
|
|
134
|
+
addr += `/ibc-port/${portID}`;
|
|
135
|
+
|
|
136
|
+
if (rawOrder !== undefined) {
|
|
137
|
+
assert(isLegitString(rawOrder), `invalid order ${rawOrder}`);
|
|
138
|
+
|
|
139
|
+
assert(
|
|
140
|
+
VALID_ORDERS.includes(rawOrder),
|
|
141
|
+
`invalid order ${rawOrder}; ${rawOrder} not found in ${JSON.stringify(VALID_ORDERS)}`,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const order = rawOrder.toLowerCase();
|
|
145
|
+
|
|
146
|
+
assert(typeof version === 'string' && version, 'missing version');
|
|
147
|
+
addr += `/${order}/${encodeSlashes(version)}`;
|
|
148
|
+
|
|
149
|
+
if (channelID !== undefined) {
|
|
150
|
+
assert(isLegitString(channelID), `invalid channelID ${channelID}`);
|
|
151
|
+
addr += `/ibc-channel/${channelID}`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return addr;
|
|
156
|
+
};
|
|
157
|
+
harden(encodeIbcEndpoint);
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @overload
|
|
161
|
+
* @param {string} remoteAddr
|
|
162
|
+
* @param {undefined | false} [returnMatch]
|
|
163
|
+
* @returns {boolean}
|
|
164
|
+
*/
|
|
165
|
+
/**
|
|
166
|
+
* @overload
|
|
167
|
+
* @param {string} remoteAddr
|
|
168
|
+
* @param {true} returnMatch
|
|
169
|
+
* @returns {RegExpMatchArray}
|
|
170
|
+
*/
|
|
171
|
+
/**
|
|
172
|
+
* Validates a remote IBC address format and returns true if the address is
|
|
173
|
+
* valid.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} remoteAddr
|
|
176
|
+
* @param {boolean} [returnMatch]
|
|
177
|
+
*/
|
|
178
|
+
export const validateRemoteIbcAddress = (remoteAddr, returnMatch = false) => {
|
|
179
|
+
const match = validateIbcAddress(remoteAddr);
|
|
180
|
+
// .groups is to inform TS https://github.com/microsoft/TypeScript/issues/32098
|
|
181
|
+
if (!(match && match.groups)) {
|
|
182
|
+
throw TypeError(
|
|
183
|
+
`Remote address ${remoteAddr} must be '(/ibc-hop/CONNECTION)*/ibc-port/PORT/(ordered|unordered)/VERSION'`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
if (match.groups.channelID) {
|
|
187
|
+
throw TypeError(
|
|
188
|
+
`Remote address ${remoteAddr} must not include channelID; found ${match.groups.channelID}`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return returnMatch ? match : true;
|
|
192
|
+
};
|
|
193
|
+
harden(validateRemoteIbcAddress);
|
|
194
|
+
|
|
195
|
+
/** @param {string} remoteAddr */
|
|
196
|
+
export const decodeRemoteIbcAddress = remoteAddr => {
|
|
197
|
+
validateRemoteIbcAddress(remoteAddr);
|
|
198
|
+
const {
|
|
199
|
+
hops,
|
|
200
|
+
portID: rPortID,
|
|
201
|
+
order,
|
|
202
|
+
version,
|
|
203
|
+
} = decodeIbcEndpoint(remoteAddr);
|
|
204
|
+
assert(order !== undefined, 'missing order');
|
|
205
|
+
assert(version !== undefined, 'missing version');
|
|
206
|
+
return { rPortID, order, version, hops };
|
|
207
|
+
};
|
|
208
|
+
harden(decodeRemoteIbcAddress);
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @param {LocalIbcAddress} localAddr
|
|
212
|
+
* @returns {string}
|
|
213
|
+
*/
|
|
214
|
+
export const localAddrToPortID = localAddr => {
|
|
215
|
+
const m = localAddr.match(IBC_ADDR_RE);
|
|
216
|
+
// .groups is to inform TS https://github.com/microsoft/TypeScript/issues/32098
|
|
217
|
+
if (!(m && m.groups)) {
|
|
218
|
+
throw TypeError(
|
|
219
|
+
`Invalid port specification ${localAddr}; expected "/ibc-port/PORT"`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
for (const group of Object.keys(m.groups)) {
|
|
223
|
+
if (group === 'portID') {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (m.groups[group] !== undefined) {
|
|
227
|
+
throw TypeError(
|
|
228
|
+
`Invalid port specification ${localAddr}; unexpected ${group}=${m.groups[group]}`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return m.groups.portID;
|
|
233
|
+
};
|
|
234
|
+
harden(localAddrToPortID);
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @param {string[]} hops
|
|
238
|
+
* @param {string} portID
|
|
239
|
+
* @param {'ordered' | 'unordered' | 'ORDERED' | 'UNORDERED'} order
|
|
240
|
+
* @param {string} version
|
|
241
|
+
* @param {string} [channelID]
|
|
242
|
+
* @returns {RemoteIbcAddress}
|
|
243
|
+
*/
|
|
244
|
+
export const encodeRemoteIbcAddress = (
|
|
245
|
+
hops,
|
|
246
|
+
portID,
|
|
247
|
+
order,
|
|
248
|
+
version,
|
|
249
|
+
channelID,
|
|
250
|
+
) => {
|
|
251
|
+
const ep = encodeIbcEndpoint({
|
|
252
|
+
hops,
|
|
253
|
+
portID,
|
|
254
|
+
order,
|
|
255
|
+
version,
|
|
256
|
+
...(channelID && { channelID }),
|
|
257
|
+
});
|
|
258
|
+
return /** @type {RemoteIbcAddress} */ (ep);
|
|
259
|
+
};
|
|
260
|
+
harden(encodeRemoteIbcAddress);
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @param {string} portID
|
|
264
|
+
* @param {'ordered' | 'unordered' | 'ORDERED' | 'UNORDERED'} order
|
|
265
|
+
* @param {string} version
|
|
266
|
+
*/
|
|
267
|
+
export const encodeLocalIbcAddress = (portID, order, version) => {
|
|
268
|
+
return encodeIbcEndpoint({ portID, order, version });
|
|
269
|
+
};
|
|
270
|
+
harden(encodeLocalIbcAddress);
|