@agoric/vats 0.15.1 → 0.15.2-calypso-dev-84eb287.0

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.
Files changed (208) hide show
  1. package/README.md +3 -0
  2. package/index.d.ts +6 -0
  3. package/index.d.ts.map +1 -0
  4. package/index.js +9 -0
  5. package/package.json +43 -48
  6. package/scripts/build-bundles.js +19 -0
  7. package/src/bridge-target.d.ts +193 -0
  8. package/src/bridge-target.d.ts.map +1 -0
  9. package/src/bridge-target.js +316 -0
  10. package/src/bridge.d.ts +14 -4
  11. package/src/bridge.d.ts.map +1 -1
  12. package/src/bridge.js +45 -16
  13. package/src/centralSupply.d.ts +3 -3
  14. package/src/centralSupply.d.ts.map +1 -1
  15. package/src/centralSupply.js +3 -5
  16. package/src/core/README.md +1 -1
  17. package/src/core/basic-behaviors.d.ts +24 -39
  18. package/src/core/basic-behaviors.d.ts.map +1 -1
  19. package/src/core/basic-behaviors.js +63 -63
  20. package/src/core/boot-chain.d.ts +178 -44
  21. package/src/core/boot-chain.d.ts.map +1 -1
  22. package/src/core/boot-chain.js +5 -5
  23. package/src/core/boot-sim.d.ts +5 -43
  24. package/src/core/boot-sim.d.ts.map +1 -1
  25. package/src/core/boot-sim.js +3 -4
  26. package/src/core/boot-solo.d.ts +5 -43
  27. package/src/core/boot-solo.d.ts.map +1 -1
  28. package/src/core/boot-solo.js +4 -5
  29. package/src/core/chain-behaviors.d.ts +10 -26
  30. package/src/core/chain-behaviors.d.ts.map +1 -1
  31. package/src/core/chain-behaviors.js +38 -42
  32. package/src/core/client-behaviors.d.ts +2 -1
  33. package/src/core/client-behaviors.d.ts.map +1 -1
  34. package/src/core/client-behaviors.js +6 -5
  35. package/src/core/core-eval-env.d.ts +40 -0
  36. package/src/core/demoIssuers.d.ts +24 -26
  37. package/src/core/demoIssuers.d.ts.map +1 -1
  38. package/src/core/demoIssuers.js +37 -37
  39. package/src/core/lib-boot.d.ts +17 -53
  40. package/src/core/lib-boot.d.ts.map +1 -1
  41. package/src/core/lib-boot.js +52 -60
  42. package/src/core/promise-space.d.ts +4 -7
  43. package/src/core/promise-space.d.ts.map +1 -1
  44. package/src/core/promise-space.js +15 -16
  45. package/src/core/sim-behaviors.d.ts +3 -3
  46. package/src/core/sim-behaviors.d.ts.map +1 -1
  47. package/src/core/sim-behaviors.js +1 -1
  48. package/src/core/startWalletFactory.d.ts +23 -27
  49. package/src/core/startWalletFactory.d.ts.map +1 -1
  50. package/src/core/startWalletFactory.js +32 -18
  51. package/src/core/types-ambient.d.ts +538 -0
  52. package/src/core/types-ambient.js +1 -0
  53. package/src/core/utils.d.ts +47 -92
  54. package/src/core/utils.d.ts.map +1 -1
  55. package/src/core/utils.js +58 -50
  56. package/src/crc.d.ts +2 -6
  57. package/src/crc.d.ts.map +1 -1
  58. package/src/crc.js +5 -16
  59. package/src/ibc.d.ts +30 -35
  60. package/src/ibc.d.ts.map +1 -1
  61. package/src/ibc.js +740 -491
  62. package/src/lib-board.d.ts +49 -121
  63. package/src/lib-board.d.ts.map +1 -1
  64. package/src/lib-board.js +49 -50
  65. package/src/localchain.d.ts +195 -0
  66. package/src/localchain.d.ts.map +1 -0
  67. package/src/localchain.js +324 -0
  68. package/src/mintHolder.d.ts +6 -4
  69. package/src/mintHolder.d.ts.map +1 -1
  70. package/src/mintHolder.js +19 -21
  71. package/src/nameHub.d.ts +27 -5
  72. package/src/nameHub.d.ts.map +1 -1
  73. package/src/nameHub.js +33 -26
  74. package/src/priceAuthorityRegistry.d.ts +21 -0
  75. package/src/priceAuthorityRegistry.d.ts.map +1 -0
  76. package/src/priceAuthorityRegistry.js +220 -0
  77. package/src/proposals/kread-proposal.d.ts +15 -0
  78. package/src/proposals/kread-proposal.d.ts.map +1 -0
  79. package/src/proposals/kread-proposal.js +24 -0
  80. package/src/proposals/localchain-proposal.d.ts +62 -0
  81. package/src/proposals/localchain-proposal.d.ts.map +1 -0
  82. package/src/proposals/localchain-proposal.js +128 -0
  83. package/src/proposals/localchain-test.d.ts +25 -0
  84. package/src/proposals/localchain-test.d.ts.map +1 -0
  85. package/src/proposals/localchain-test.js +89 -0
  86. package/src/proposals/namesByAddress-fix-proposal.d.ts +22 -0
  87. package/src/proposals/namesByAddress-fix-proposal.d.ts.map +1 -0
  88. package/src/proposals/namesByAddress-fix-proposal.js +55 -0
  89. package/src/proposals/network-proposal.d.ts +23 -8
  90. package/src/proposals/network-proposal.d.ts.map +1 -1
  91. package/src/proposals/network-proposal.js +90 -65
  92. package/src/proposals/probeZcfBundle.d.ts +26 -0
  93. package/src/proposals/probeZcfBundle.d.ts.map +1 -0
  94. package/src/proposals/probeZcfBundle.js +72 -0
  95. package/src/proposals/restart-vats-proposal.d.ts +4 -2
  96. package/src/proposals/restart-vats-proposal.d.ts.map +1 -1
  97. package/src/proposals/restart-vats-proposal.js +20 -8
  98. package/src/proposals/transfer-proposal.d.ts +37 -0
  99. package/src/proposals/transfer-proposal.d.ts.map +1 -0
  100. package/src/proposals/transfer-proposal.js +115 -0
  101. package/src/proposals/upgrade-bank-proposal.d.ts +27 -0
  102. package/src/proposals/upgrade-bank-proposal.d.ts.map +1 -0
  103. package/src/proposals/upgrade-bank-proposal.js +42 -0
  104. package/src/proposals/upgrade-provisionPool-proposal.d.ts +27 -0
  105. package/src/proposals/upgrade-provisionPool-proposal.d.ts.map +1 -0
  106. package/src/proposals/upgrade-provisionPool-proposal.js +67 -0
  107. package/src/proposals/upgrade-zoe-proposal.d.ts +27 -0
  108. package/src/proposals/upgrade-zoe-proposal.d.ts.map +1 -0
  109. package/src/proposals/upgrade-zoe-proposal.js +42 -0
  110. package/src/proposals/vtransfer-echoer.d.ts +24 -0
  111. package/src/proposals/vtransfer-echoer.d.ts.map +1 -0
  112. package/src/proposals/vtransfer-echoer.js +42 -0
  113. package/src/proposals/zcf-only-proposal.d.ts +33 -0
  114. package/src/proposals/zcf-only-proposal.d.ts.map +1 -0
  115. package/src/proposals/zcf-only-proposal.js +36 -0
  116. package/src/proposals/zcf-proposal.d.ts +30 -0
  117. package/src/proposals/zcf-proposal.d.ts.map +1 -0
  118. package/src/proposals/zcf-proposal.js +50 -0
  119. package/src/repl.d.ts +3 -3
  120. package/src/repl.d.ts.map +1 -1
  121. package/src/repl.js +11 -5
  122. package/src/transfer.d.ts +111 -0
  123. package/src/transfer.d.ts.map +1 -0
  124. package/src/transfer.js +260 -0
  125. package/src/types.d.ts +237 -105
  126. package/src/types.js +1 -97
  127. package/src/vat-agoricNames.d.ts +4 -4
  128. package/src/vat-agoricNames.d.ts.map +1 -1
  129. package/src/vat-agoricNames.js +2 -4
  130. package/src/vat-bank.d.ts +217 -149
  131. package/src/vat-bank.d.ts.map +1 -1
  132. package/src/vat-bank.js +96 -68
  133. package/src/vat-board.d.ts +24 -24
  134. package/src/vat-board.d.ts.map +1 -1
  135. package/src/vat-board.js +0 -1
  136. package/src/vat-bridge.d.ts +9 -23
  137. package/src/vat-bridge.d.ts.map +1 -1
  138. package/src/vat-bridge.js +1 -2
  139. package/src/vat-ibc.d.ts +16 -4
  140. package/src/vat-ibc.d.ts.map +1 -1
  141. package/src/vat-ibc.js +20 -9
  142. package/src/vat-localchain.d.ts +37 -0
  143. package/src/vat-localchain.d.ts.map +1 -0
  144. package/src/vat-localchain.js +29 -0
  145. package/src/vat-mints.d.ts +31 -31
  146. package/src/vat-mints.d.ts.map +1 -1
  147. package/src/vat-mints.js +6 -7
  148. package/src/vat-network.d.ts +69 -1
  149. package/src/vat-network.d.ts.map +1 -1
  150. package/src/vat-network.js +45 -3
  151. package/src/vat-priceAuthority.d.ts +4 -4
  152. package/src/vat-priceAuthority.d.ts.map +1 -1
  153. package/src/vat-priceAuthority.js +1 -1
  154. package/src/vat-provisioning.d.ts +6 -6
  155. package/src/vat-provisioning.d.ts.map +1 -1
  156. package/src/vat-provisioning.js +15 -12
  157. package/src/vat-transfer.d.ts +81 -0
  158. package/src/vat-transfer.d.ts.map +1 -0
  159. package/src/vat-transfer.js +63 -0
  160. package/src/vat-zoe.d.ts +4 -1
  161. package/src/vat-zoe.d.ts.map +1 -1
  162. package/src/vat-zoe.js +10 -2
  163. package/src/virtual-purse.d.ts +46 -21
  164. package/src/virtual-purse.d.ts.map +1 -1
  165. package/src/virtual-purse.js +57 -46
  166. package/src/walletFlags.d.ts +2 -2
  167. package/src/walletFlags.js +1 -2
  168. package/tools/bank-utils.d.ts +20 -0
  169. package/tools/bank-utils.d.ts.map +1 -0
  170. package/tools/bank-utils.js +92 -0
  171. package/tools/board-utils.d.ts +39 -0
  172. package/tools/board-utils.d.ts.map +1 -0
  173. package/tools/board-utils.js +92 -0
  174. package/tools/boot-test-utils.d.ts +88 -0
  175. package/tools/boot-test-utils.d.ts.map +1 -0
  176. package/tools/boot-test-utils.js +157 -0
  177. package/tools/fake-bridge.d.ts +18 -0
  178. package/tools/fake-bridge.d.ts.map +1 -0
  179. package/tools/fake-bridge.js +289 -0
  180. package/tools/ibc-utils.d.ts +29 -0
  181. package/tools/ibc-utils.d.ts.map +1 -0
  182. package/tools/ibc-utils.js +115 -0
  183. package/decentral-core-config.json +0 -66
  184. package/decentral-demo-config.json +0 -155
  185. package/decentral-devnet-config.json +0 -216
  186. package/decentral-itest-vaults-config.json +0 -210
  187. package/decentral-main-vaults-config.json +0 -229
  188. package/decentral-test-vaults-config.json +0 -195
  189. package/demo-proposals.json +0 -5
  190. package/exported.js +0 -1
  191. package/jsconfig.build.json +0 -12
  192. package/jsconfig.json +0 -13
  193. package/src/core/types.d.ts +0 -764
  194. package/src/core/types.d.ts.map +0 -1
  195. package/src/core/types.js +0 -347
  196. package/src/provisionPool.d.ts +0 -164
  197. package/src/provisionPool.d.ts.map +0 -1
  198. package/src/provisionPool.js +0 -113
  199. package/src/provisionPoolKit.d.ts +0 -392
  200. package/src/provisionPoolKit.d.ts.map +0 -1
  201. package/src/provisionPoolKit.js +0 -464
  202. package/src/tokens.d.ts +0 -36
  203. package/src/tokens.d.ts.map +0 -1
  204. package/src/tokens.js +0 -37
  205. package/src/types.d.ts.map +0 -1
  206. package/src/vat-sharing.d.ts +0 -54
  207. package/src/vat-sharing.d.ts.map +0 -1
  208. package/src/vat-sharing.js +0 -14
package/src/ibc.js CHANGED
@@ -1,192 +1,110 @@
1
1
  // @ts-check
2
2
 
3
- import {
4
- dataToBase64,
5
- base64ToBytes,
6
- } from '@agoric/swingset-vat/src/vats/network/index.js';
7
- import { makeScalarMapStore, makeLegacyMap } from '@agoric/store';
8
- import { makePromiseKit } from '@endo/promise-kit';
9
3
  import { assert, details as X, Fail } from '@agoric/assert';
10
- import { Far } from '@endo/far';
4
+ import { E } from '@endo/far';
11
5
 
12
- import '@agoric/store/exported.js';
13
- import '@agoric/swingset-vat/src/vats/network/types.js';
6
+ import { byteSourceToBase64, base64ToBytes } from '@agoric/network';
14
7
 
15
- import { makeWithQueue } from '@agoric/internal/src/queue.js';
8
+ import {
9
+ localAddrToPortID,
10
+ decodeRemoteIbcAddress,
11
+ encodeLocalIbcAddress,
12
+ encodeRemoteIbcAddress,
13
+ } from '../tools/ibc-utils.js';
14
+
15
+ /**
16
+ * @import {LocalIbcAddress, RemoteIbcAddress} from '../tools/ibc-utils.js';
17
+ * @import {AttemptDescription} from '@agoric/network';
18
+ */
16
19
 
17
20
  // CAVEAT: IBC acks cannot be empty, as the Cosmos IAVL tree cannot represent
18
21
  // empty acknowledgements as distinct from unacknowledged packets.
19
22
  const DEFAULT_ACKNOWLEDGEMENT = '\x00';
20
23
 
21
- // Default timeout after 10 minutes.
22
- const DEFAULT_PACKET_TIMEOUT_NS = 10n * 60n * 1_000_000_000n;
24
+ // Default timeout after 60 minutes.
25
+ const DEFAULT_PACKET_TIMEOUT_NS = 60n * 60n * 1_000_000_000n;
23
26
 
24
27
  /**
25
- * @typedef {import('./types.js').BridgeHandler} BridgeHandler
28
+ * @import {Endpoint, Connection, ConnectionHandler, InboundAttempt, Bytes, ProtocolHandler, ProtocolImpl} from '@agoric/network';
29
+ * @import {BridgeHandler, ScopedBridgeManager, ConnectingInfo, IBCChannelID, IBCChannelOrdering, IBCEvent, IBCPacket, IBCPortID, IBCDowncallPacket, IBCDowncallMethod, IBCDowncall, IBCBridgeEvent} from './types.js';
30
+ * @import {Zone} from '@agoric/base-zone';
31
+ * @import {PromiseVow, Remote, VowKit, VowResolver, VowTools} from '@agoric/vow';
26
32
  */
27
33
 
28
- /**
29
- * @typedef {string} IBCPortID
30
- * @typedef {string} IBCChannelID
31
- * @typedef {string} IBCConnectionID
32
- */
34
+ /** @typedef {VowKit<AttemptDescription>} OnConnectP */
33
35
 
34
36
  /**
35
- * @typedef {object} IBCPacket
36
- * @property {Bytes} [data]
37
- * @property {IBCChannelID} source_channel
38
- * @property {IBCPortID} source_port
39
- * @property {IBCChannelID} destination_channel
40
- * @property {IBCPortID} destination_port
37
+ * @typedef {Omit<
38
+ * ConnectingInfo,
39
+ * 'counterparty' | 'channelID' | 'counterpartyVersion'
40
+ * > & {
41
+ * localAddr: Endpoint;
42
+ * onConnectP: OnConnectP;
43
+ * counterparty: { port_id: string };
44
+ * }} Outbound
41
45
  */
42
46
 
47
+ // FIXME(TS9006) remove 'any'
43
48
  /**
44
- * Create a handler for the IBC protocol, both from the network
45
- * and from the bridge.
46
- *
47
- * @param {typeof import('@endo/far').E} E
48
- * @param {(method: string, params: any) => Promise<any>} rawCallIBCDevice
49
- * @returns {ProtocolHandler & BridgeHandler} Protocol/Bridge handler
49
+ * @param {Zone} zone
50
+ * @returns {any}
50
51
  */
51
- export function makeIBCProtocolHandler(E, rawCallIBCDevice) {
52
- // Nonce for creating port identifiers.
53
- let lastPortID = 0;
54
-
55
- const callIBCDevice = (method, params) => {
56
- console.info('IBC downcall', method, params);
57
- return rawCallIBCDevice(method, params);
58
- };
59
- /**
60
- * @type {MapStore<string, Promise<Connection>>}
61
- */
62
- const channelKeyToConnP = makeScalarMapStore('CHANNEL:PORT');
63
-
64
- /**
65
- * @typedef {object} Counterparty
66
- * @property {string} port_id
67
- * @property {string} channel_id
68
- *
69
- * @typedef {object} ConnectingInfo
70
- * @property {'ORDERED'|'UNORDERED'} order
71
- * @property {string[]} connectionHops
72
- * @property {string} portID
73
- * @property {string} channelID
74
- * @property {Counterparty} counterparty
75
- * @property {string} version
76
- *
77
- * @typedef {PromiseRecord<AttemptDescription>} OnConnectP
78
- * @typedef {Omit<ConnectingInfo, 'counterparty' | 'channelID'> & {
79
- * localAddr: Endpoint, onConnectP: OnConnectP
80
- * counterparty: { port_id: string },
81
- * }} Outbound
82
- */
83
-
84
- /**
85
- * @type {LegacyMap<string, Array<Outbound>>}
86
- */
87
- // Legacy because it holds a mutable Javascript Array
88
- const srcPortToOutbounds = makeLegacyMap('SRC-PORT');
89
-
90
- /**
91
- * @type {MapStore<string, ConnectingInfo>}
92
- */
93
- const channelKeyToInfo = makeScalarMapStore('CHANNEL:PORT');
94
-
95
- /**
96
- * @type {MapStore<string, Promise<InboundAttempt>>}
97
- */
98
- const channelKeyToAttemptP = makeScalarMapStore('CHANNEL:PORT');
99
-
100
- /**
101
- * @type {LegacyMap<string, LegacyMap<number, PromiseRecord<Bytes>>>}
102
- */
103
- // Legacy because it holds a LegacyMap
104
- const channelKeyToSeqAck = makeLegacyMap('CHANNEL:PORT');
105
-
52
+ export const prepareIBCConnectionHandler = zone => {
106
53
  /**
107
- * Send a packet out via the IBC device.
108
- *
109
- * @param {IBCPacket} packet
110
- * @param {LegacyMap<number, PromiseRecord<Bytes>>} seqToAck
111
- * @param {bigint} [relativeTimeoutNs]
112
- */
113
- async function ibcSendPacket(
114
- packet,
115
- seqToAck,
116
- relativeTimeoutNs = DEFAULT_PACKET_TIMEOUT_NS,
117
- ) {
118
- // Make a kernel call to do the send.
119
- const fullPacket = await callIBCDevice('sendPacket', {
120
- packet,
121
- relativeTimeoutNs,
122
- });
123
-
124
- // Extract the actual sequence number from the return.
125
- const { sequence } = fullPacket;
126
-
127
- /**
128
- * @type {PromiseRecord<Bytes>}
129
- */
130
- const ackDeferred = makePromiseKit();
131
-
132
- // Register the ack resolver/rejector with this sequence number.
133
- seqToAck.init(sequence, ackDeferred);
134
- return ackDeferred.promise;
135
- }
136
-
137
- /**
138
- * @param {string} channelID
139
- * @param {string} portID
140
- * @param {string} rChannelID
141
- * @param {string} rPortID
142
- * @param {'ORDERED'|'UNORDERED'} order
54
+ * @param {IBCChannelID} channelID
55
+ * @param {IBCPortID} portID
56
+ * @param {IBCChannelID} rChannelID
57
+ * @param {IBCPortID} rPortID
58
+ * @param {IBCChannelOrdering} order
143
59
  * @returns {ConnectionHandler}
144
60
  */
145
- function makeIBCConnectionHandler(
146
- channelID,
147
- portID,
148
- rChannelID,
149
- rPortID,
150
- order,
151
- ) {
152
- const channelKey = `${channelID}:${portID}`;
153
- // Legacy because it holds a PromiseRecord
154
- const seqToAck = makeLegacyMap('SEQUENCE');
155
- channelKeyToSeqAck.init(channelKey, seqToAck);
156
-
61
+ const makeIBCConnectionHandler = zone.exoClass(
62
+ 'IBCConnectionHandler',
63
+ undefined,
157
64
  /**
158
- * @param {Connection} _conn
159
- * @param {Bytes} packetBytes
160
- * @param {ConnectionHandler} _handler
161
- * @param {object} root0
162
- * @param {bigint} [root0.relativeTimeoutNs]
163
- * @returns {Promise<Bytes>} Acknowledgement data
65
+ * @param {{
66
+ * protocolUtils: any;
67
+ * channelKeyToConnP: MapStore<
68
+ * string,
69
+ * import('@agoric/vow').Remote<Connection>
70
+ * >;
71
+ * channelKeyToSeqAck: MapStore<
72
+ * string,
73
+ * MapStore<number, import('@agoric/vow').VowKit<Bytes>>
74
+ * >;
75
+ * }} param0
76
+ * @param {{
77
+ * channelID: IBCChannelID;
78
+ * portID: string;
79
+ * rChannelID: IBCChannelID;
80
+ * rPortID: string;
81
+ * order: 'ORDERED' | 'UNORDERED';
82
+ * }} param1
164
83
  */
165
- let onReceive = async (
166
- _conn,
167
- packetBytes,
168
- _handler,
169
- { relativeTimeoutNs } = {},
84
+ (
85
+ { protocolUtils, channelKeyToConnP, channelKeyToSeqAck },
86
+ { channelID, portID, rChannelID, rPortID, order },
170
87
  ) => {
171
- // console.error(`Remote IBC Handler ${portID} ${channelID}`);
172
- const packet = {
173
- source_port: portID,
174
- source_channel: channelID,
175
- destination_port: rPortID,
176
- destination_channel: rChannelID,
177
- data: dataToBase64(packetBytes),
178
- };
179
- return ibcSendPacket(packet, seqToAck, relativeTimeoutNs);
180
- };
181
-
182
- if (order === 'ORDERED') {
183
- // We set up a queue on the receiver to enforce ordering.
184
- const withChannelReceiveQueue = makeWithQueue();
185
- onReceive = withChannelReceiveQueue(onReceive);
186
- }
88
+ const channelKey = `${channelID}:${portID}`;
89
+ const seqToAck = zone.detached().mapStore('seqToAck');
90
+ channelKeyToSeqAck.init(channelKey, seqToAck);
187
91
 
188
- return Far('IBCConnectionHandler', {
92
+ return {
93
+ protocolUtils,
94
+ channelID,
95
+ portID,
96
+ rChannelID,
97
+ rPortID,
98
+ order,
99
+ channelKeyToConnP,
100
+ channelKeyToSeqAck,
101
+ };
102
+ },
103
+ {
104
+ /** @type {Required<ConnectionHandler>['onOpen']} */
189
105
  async onOpen(conn, localAddr, remoteAddr, _handler) {
106
+ const { channelID, portID, channelKeyToConnP } = this.state;
107
+
190
108
  console.debug(
191
109
  'onOpen Remote IBC Connection',
192
110
  channelID,
@@ -194,194 +112,615 @@ export function makeIBCProtocolHandler(E, rawCallIBCDevice) {
194
112
  localAddr,
195
113
  remoteAddr,
196
114
  );
197
- const connP = E.resolve(conn);
198
- channelKeyToConnP.init(channelKey, connP);
115
+ const channelKey = `${channelID}:${portID}`;
116
+
117
+ channelKeyToConnP.init(channelKey, conn);
199
118
  },
200
- onReceive,
201
- async onClose(_conn, _reason, _handler) {
119
+ /** @type {Required<ConnectionHandler>['onReceive']} */
120
+ async onReceive(
121
+ _conn,
122
+ packetBytes,
123
+ _handler,
124
+ { relativeTimeoutNs } = {},
125
+ ) {
126
+ const { portID, channelID, rPortID, rChannelID } = this.state;
127
+ const { protocolUtils } = this.state;
128
+ // console.error(`Remote IBC Handler ${portID} ${channelID}`);
129
+ /** @type {IBCPacket} */
202
130
  const packet = {
203
131
  source_port: portID,
204
132
  source_channel: channelID,
133
+ destination_port: rPortID,
134
+ destination_channel: rChannelID,
135
+ data: byteSourceToBase64(packetBytes),
205
136
  };
206
- await callIBCDevice('startChannelCloseInit', { packet });
137
+ return protocolUtils.ibcSendPacket(packet, relativeTimeoutNs);
138
+ },
139
+ /** @type {Required<ConnectionHandler>['onClose']} */
140
+ async onClose() {
141
+ const { portID, channelID } = this.state;
142
+ const { protocolUtils, channelKeyToSeqAck } = this.state;
143
+
144
+ const packet = {
145
+ source_port: portID,
146
+ source_channel: channelID,
147
+ };
148
+ await protocolUtils.downcall('startChannelCloseInit', {
149
+ packet,
150
+ });
207
151
  const rejectReason = Error('Connection closed');
208
- for (const ackDeferred of seqToAck.values()) {
209
- ackDeferred.reject(rejectReason);
152
+ const channelKey = `${channelID}:${portID}`;
153
+
154
+ const seqToAck = channelKeyToSeqAck.get(channelKey);
155
+
156
+ for (const ackKit of seqToAck.values()) {
157
+ ackKit.resolver.reject(rejectReason);
210
158
  }
211
159
  channelKeyToSeqAck.delete(channelKey);
212
160
  },
213
- });
214
- }
161
+ },
162
+ );
215
163
 
216
- /**
217
- * @param {string} localAddr
218
- */
219
- const localAddrToPortID = localAddr => {
220
- const m = localAddr.match(/^\/ibc-port\/([-a-zA-Z0-9._+#[\]<>]+)$/);
221
- if (!m) {
222
- throw TypeError(
223
- `Invalid port specification ${localAddr}; expected "/ibc-port/PORT"`,
224
- );
225
- }
226
- return m[1];
227
- };
164
+ return makeIBCConnectionHandler;
165
+ };
228
166
 
229
- /**
230
- * @type {ProtocolImpl}
231
- */
232
- let protocolImpl;
167
+ /**
168
+ * @param {Zone} zone
169
+ * @param {VowTools} powers
170
+ */
171
+ export const prepareIBCProtocol = (zone, powers) => {
172
+ const { makeVowKit, watch } = powers;
173
+
174
+ const makeIBCConnectionHandler = prepareIBCConnectionHandler(zone);
233
175
 
234
176
  /**
235
- * @typedef {object} OutboundCircuitRecord
236
- * @property {IBCConnectionID} dst
237
- * @property {'ORDERED'|'UNORDERED'} order
238
- * @property {string} version
239
- * @property {IBCPacket} packet
240
- * @property {PromiseRecord<ConnectionHandler>} deferredHandler
177
+ * @typedef {object} IBCDevice
178
+ * @property {(method: string, args: object) => Promise<any>} downcall
241
179
  */
242
180
 
243
181
  /**
244
- * @type {LegacyMap<Port, Set<PromiseRecord<ConnectionHandler>>>}
182
+ * @type {WeakMapStore<
183
+ * IBCDevice,
184
+ * {
185
+ * protocolHandler: ProtocolHandler;
186
+ * bridgeHandler: BridgeHandler;
187
+ * }
188
+ * >} Map
189
+ * from IBC device to existing handler
245
190
  */
246
- // Legacy because it holds a raw JavaScript Set
247
- const portToPendingConns = makeLegacyMap('Port');
191
+ const ibcdevToKit = zone.weakMapStore('ibcdevToHandler');
192
+
193
+ const detached = zone.detached();
248
194
 
249
195
  /**
250
- * @type {ProtocolHandler}
196
+ * Create a handler for the IBC protocol, both from the network and from the
197
+ * bridge.
251
198
  */
252
- const protocol = Far('IBCProtocolHandler', {
253
- async onCreate(impl, _protocolHandler) {
254
- console.debug('IBC onCreate');
255
- protocolImpl = impl;
256
- },
257
- async onInstantiate() {
258
- // The IBC channel is not known until after handshake.
259
- return '';
260
- },
261
- async generatePortID(_localAddr, _protocolHandler) {
262
- lastPortID += 1;
263
- return `port-${lastPortID}`;
264
- },
265
- async onBind(port, localAddr, _protocolHandler) {
266
- const portID = localAddrToPortID(localAddr);
267
- portToPendingConns.init(port, new Set());
268
- const packet = {
269
- source_port: portID,
199
+ const makeIBCProtocolKit = zone.exoClassKit(
200
+ 'IBCProtocolHandler',
201
+ undefined,
202
+ /** @param {IBCDevice} ibcdev */
203
+ ibcdev => {
204
+ /** @type {MapStore<string, Remote<Connection>>} */
205
+ const channelKeyToConnP = detached.mapStore('channelKeyToConnP');
206
+
207
+ /** @type {MapStore<string, ConnectingInfo>} */
208
+ const channelKeyToInfo = detached.mapStore('channelKeyToInfo');
209
+
210
+ /** @type {MapStore<string, Remote<InboundAttempt>>} */
211
+ const channelKeyToAttempt = detached.mapStore('channelKeyToAttempt');
212
+
213
+ /** @type {MapStore<string, Outbound[]>} */
214
+ const srcPortToOutbounds = detached.mapStore('srcPortToOutbounds');
215
+
216
+ /** @type {MapStore<string, MapStore<number, VowKit<Bytes>>>} */
217
+ const channelKeyToSeqAck = detached.mapStore('channelKeyToSeqAck');
218
+
219
+ /** @type {MapStore<string, SetStore<VowResolver>>} */
220
+ const portToPendingConns = detached.mapStore('portToPendingConns');
221
+
222
+ return {
223
+ ibcdev,
224
+ channelKeyToConnP,
225
+ channelKeyToInfo,
226
+ channelKeyToAttempt,
227
+ srcPortToOutbounds,
228
+ channelKeyToSeqAck,
229
+ portToPendingConns,
230
+ lastPortID: 0n, // Nonce for creating port identifiers.
231
+ /** @type {Remote<ProtocolImpl> | null} */
232
+ protocolImpl: null,
270
233
  };
271
- return callIBCDevice('bindPort', { packet });
272
234
  },
273
- async onConnect(port, localAddr, remoteAddr, _chandler, _protocolHandler) {
274
- console.debug('IBC onConnect', localAddr, remoteAddr);
275
- const portID = localAddrToPortID(localAddr);
276
- const pendingConns = portToPendingConns.get(port);
277
-
278
- const match = remoteAddr.match(
279
- /^(\/ibc-hop\/[^/]+)*\/ibc-port\/([^/]+)\/(ordered|unordered)\/([^/]+)$/s,
280
- );
281
- if (!match) {
282
- throw TypeError(
283
- `Remote address ${remoteAddr} must be '(/ibc-hop/CONNECTION)*/ibc-port/PORT/(ordered|unordered)/VERSION'`,
284
- );
285
- }
286
-
287
- const hops = [];
288
- let h = match[1];
289
- while (h) {
290
- const m = h.match(/^\/ibc-hop\/([^/]+)/);
291
- if (!m) {
292
- throw Error(
293
- `internal: ${JSON.stringify(h)} did not begin with "/ibc-hop/XXX"`,
294
- );
295
- }
296
- h = h.substr(m[0].length);
297
- hops.push(m[1]);
298
- }
299
-
300
- // Generate a circuit.
301
- const rPortID = match[2];
302
- const order = match[3] === 'ordered' ? 'ORDERED' : 'UNORDERED';
303
- const version = match[4];
304
-
305
- const onConnectP = makePromiseKit();
306
-
307
- pendingConns.add(onConnectP);
308
- /** @type {Outbound[]} */
309
- let outbounds;
310
- if (srcPortToOutbounds.has(portID)) {
311
- outbounds = [...srcPortToOutbounds.get(portID)];
312
- } else {
313
- outbounds = [];
314
- srcPortToOutbounds.init(portID, outbounds);
315
- }
316
- outbounds.push({
317
- portID,
318
- counterparty: { port_id: rPortID },
319
- connectionHops: hops,
320
- order,
321
- version,
322
- onConnectP,
323
- localAddr,
324
- });
325
-
326
- // Initialise the channel, which automatic relayers should pick up.
327
- const packet = {
328
- source_port: portID,
329
- destination_port: rPortID,
330
- };
235
+ {
236
+ protocolHandler: {
237
+ /** @type {Required<ProtocolHandler>['onCreate']} */
238
+ async onCreate(impl) {
239
+ console.debug('IBC onCreate');
240
+ this.state.protocolImpl = impl;
241
+ },
242
+ /** @type {Required<ProtocolHandler>['onInstantiate']} */
243
+ async onInstantiate() {
244
+ // The IBC channel is not known until after handshake.
245
+ return '';
246
+ },
247
+ /** @type {Required<ProtocolHandler>['generatePortID']} */
248
+ async generatePortID() {
249
+ this.state.lastPortID += 1n;
250
+ return `port-${this.state.lastPortID}`;
251
+ },
252
+ /** @type {Required<ProtocolHandler>['onBind']} */
253
+ async onBind(_port, localAddr) {
254
+ const { util } = this.facets;
255
+ const { portToPendingConns } = this.state;
256
+
257
+ // @ts-expect-error may not be LocalIbcAddress
258
+ const portID = localAddrToPortID(localAddr);
259
+ portToPendingConns.init(portID, detached.setStore('pendingConns'));
260
+ const packet = {
261
+ source_port: portID,
262
+ };
263
+ return util.downcall('bindPort', { packet });
264
+ },
265
+ /** @type {Required<ProtocolHandler>['onConnect']} */
266
+ async onConnect(_port, localAddr, remoteAddr) {
267
+ const { util } = this.facets;
268
+ const { portToPendingConns, srcPortToOutbounds } = this.state;
269
+
270
+ console.debug('IBC onConnect', localAddr, remoteAddr);
271
+ // @ts-expect-error may not be LocalIbcAddress
272
+ const portID = localAddrToPortID(localAddr);
273
+ const pendingConns = portToPendingConns.get(portID);
274
+
275
+ const { rPortID, hops, order, version } =
276
+ decodeRemoteIbcAddress(remoteAddr);
277
+
278
+ const kit = makeVowKit();
279
+
280
+ pendingConns.add(kit.resolver);
281
+ /** @type {Outbound} */
282
+ const ob = {
283
+ portID,
284
+ counterparty: { port_id: rPortID },
285
+ connectionHops: hops,
286
+ order,
287
+ version,
288
+ onConnectP: kit,
289
+ localAddr,
290
+ };
291
+ if (srcPortToOutbounds.has(portID)) {
292
+ const outbounds = srcPortToOutbounds.get(portID);
293
+ srcPortToOutbounds.set(portID, harden([...outbounds, ob]));
294
+ } else {
295
+ srcPortToOutbounds.init(portID, harden([ob]));
296
+ }
331
297
 
332
- await callIBCDevice('startChannelOpenInit', {
333
- packet,
334
- order,
335
- hops,
336
- version,
337
- });
298
+ // Initialise the channel, which a listening relayer should pick up.
299
+ /** @type {IBCDowncallPacket<'startChannelOpenInit'>} */
300
+ const packet = {
301
+ source_port: portID,
302
+ destination_port: rPortID,
303
+ };
338
304
 
339
- return onConnectP.promise;
340
- },
341
- async onListen(_port, localAddr, _listenHandler) {
342
- console.debug('IBC onListen', localAddr);
343
- },
344
- async onListenRemove(_port, localAddr, _listenHandler) {
345
- console.debug('IBC onListenRemove', localAddr);
346
- },
347
- async onRevoke(port, localAddr, _protocolHandler) {
348
- console.debug('IBC onRevoke', localAddr);
349
- const pendingConns = portToPendingConns.get(port);
350
- portToPendingConns.delete(port);
351
- const revoked = Error(`Port ${localAddr} revoked`);
352
- for (const onConnectP of pendingConns.values()) {
353
- onConnectP.reject(revoked);
354
- }
355
- },
356
- });
357
-
358
- return Far('IBCProtocolHandler', {
359
- ...protocol,
360
- async fromBridge(obj) {
361
- console.info('IBC fromBridge', obj);
362
- switch (obj.event) {
363
- case 'channelOpenTry': {
364
- // They're (more or less politely) asking if we are listening, so make an attempt.
305
+ await util.downcall('startChannelOpenInit', {
306
+ packet,
307
+ order,
308
+ hops,
309
+ version,
310
+ });
311
+
312
+ return kit.vow;
313
+ },
314
+ /** @type {Required<ProtocolHandler>['onListen']} */
315
+ async onListen(_port, localAddr) {
316
+ console.debug('IBC onListen', localAddr);
317
+ },
318
+ /** @type {Required<ProtocolHandler>['onListenRemove']} */
319
+ async onListenRemove(_port, localAddr) {
320
+ console.debug('IBC onListenRemove', localAddr);
321
+ },
322
+ /** @type {Required<ProtocolHandler>['onRevoke']} */
323
+ async onRevoke(_port, localAddr) {
324
+ const { portToPendingConns } = this.state;
325
+ console.debug('IBC onRevoke', localAddr);
326
+ // @ts-expect-error may not be LocalIbcAddress
327
+ const portID = localAddrToPortID(localAddr);
328
+
329
+ const pendingConns = portToPendingConns.get(portID);
330
+ portToPendingConns.delete(portID);
331
+ const revoked = Error(`Port ${localAddr} revoked`);
332
+ for (const resolver of pendingConns.values()) {
333
+ resolver.reject(revoked);
334
+ }
335
+ },
336
+ },
337
+ bridgeHandler: {
338
+ /** @param {IBCEvent<IBCBridgeEvent>} obj */
339
+ async fromBridge(obj) {
340
+ const {
341
+ protocolImpl,
342
+ channelKeyToAttempt,
343
+ channelKeyToInfo,
344
+ srcPortToOutbounds,
345
+ channelKeyToConnP,
346
+ channelKeyToSeqAck,
347
+ } = this.state;
348
+
349
+ const { util } = this.facets;
350
+
351
+ console.info('IBC fromBridge', obj);
352
+ await null;
353
+ switch (obj.event) {
354
+ case 'channelOpenInit': {
355
+ const {
356
+ channelID,
357
+ portID,
358
+ counterparty: { port_id: rPortID, channel_id: rChannelID },
359
+ connectionHops: hops,
360
+ order,
361
+ version,
362
+ asyncVersions,
363
+ } = /** @type {IBCEvent<'channelOpenInit'>} */ (obj);
364
+ if (!asyncVersions) {
365
+ // Synchronous versions have already been negotiated.
366
+ break;
367
+ }
368
+
369
+ // We have async version negotiation, so we must call back before the
370
+ // channel can make progress.
371
+ // We just use the provided version without failing.
372
+ await util.downcall('initOpenExecuted', {
373
+ packet: {
374
+ source_port: portID,
375
+ source_channel: channelID,
376
+ destination_port: rPortID,
377
+ destination_channel: rChannelID,
378
+ },
379
+ order,
380
+ version,
381
+ hops,
382
+ });
383
+ break;
384
+ }
385
+ case 'channelOpenTry': {
386
+ // They're (more or less politely) asking if we are listening, so make an attempt.
387
+ const {
388
+ portID,
389
+ counterparty: { port_id: rPortID, channel_id: rChannelID },
390
+ connectionHops: hops,
391
+ order,
392
+ version,
393
+ counterpartyVersion: rVersion,
394
+ } = /** @type {IBCEvent<'channelOpenTry'>} */ (obj);
395
+
396
+ const localAddr = encodeLocalIbcAddress(portID, order, version);
397
+ const remoteAddr = encodeRemoteIbcAddress(
398
+ hops,
399
+ rPortID,
400
+ order,
401
+ rVersion,
402
+ rChannelID,
403
+ );
404
+
405
+ // See if we allow an inbound attempt for this address pair (without
406
+ // rejecting).
407
+ // const attempt = await this.state.protocolImpl
408
+ // ?.inbound(localAddr, remoteAddr)
409
+ // .then(
410
+ // ack => console.info('Manual packet', ack, 'acked:', ack),
411
+ // e => console.warn('Manual packet', e, 'failed:', e),
412
+ // );
413
+
414
+ assert(protocolImpl);
415
+
416
+ return watch(
417
+ E(protocolImpl).inbound(localAddr, remoteAddr),
418
+ this.facets.protocolImplInboundWatcher,
419
+ {
420
+ obj,
421
+ rPortID,
422
+ rChannelID,
423
+ hops,
424
+ },
425
+ );
426
+ }
427
+
428
+ case 'channelOpenAck': {
429
+ // Complete the pending outbound connection.
430
+ const {
431
+ portID,
432
+ channelID,
433
+ counterparty: { port_id: rPortID, channel_id: rChannelID },
434
+ counterpartyVersion: rVersion,
435
+ connectionHops: rHops,
436
+ } = /** @type {IBCEvent<'channelOpenAck'>} */ (obj);
437
+
438
+ const outbounds = this.state.srcPortToOutbounds.has(portID)
439
+ ? [...this.state.srcPortToOutbounds.get(portID)]
440
+ : [];
441
+ const oidx = outbounds.findIndex(
442
+ ({
443
+ counterparty: { port_id: iPortID },
444
+ connectionHops: iHops,
445
+ }) => {
446
+ if (iPortID !== rPortID) {
447
+ return false;
448
+ }
449
+ if (iHops.length !== rHops.length) {
450
+ return false;
451
+ }
452
+ for (let i = 0; i < iHops.length; i += 1) {
453
+ if (iHops[i] !== rHops[i]) {
454
+ return false;
455
+ }
456
+ }
457
+ return true;
458
+ },
459
+ );
460
+ oidx >= 0 || Fail`${portID}: did not expect channelOpenAck`;
461
+ const { onConnectP, localAddr, ...chanInfo } = outbounds[oidx];
462
+ outbounds.splice(oidx, 1);
463
+ if (outbounds.length === 0) {
464
+ srcPortToOutbounds.delete(portID);
465
+ } else {
466
+ srcPortToOutbounds.set(portID, harden(outbounds));
467
+ }
468
+
469
+ // Finish the outbound connection.
470
+ const remoteAddress = encodeRemoteIbcAddress(
471
+ rHops,
472
+ rPortID,
473
+ chanInfo.order,
474
+ rVersion,
475
+ rChannelID,
476
+ );
477
+ const localAddress = `${localAddr}/${chanInfo.order.toLowerCase()}/${rVersion}/ibc-channel/${channelID}`;
478
+ const rchandler = makeIBCConnectionHandler(
479
+ {
480
+ protocolUtils: util,
481
+ channelKeyToConnP,
482
+ channelKeyToSeqAck,
483
+ },
484
+ {
485
+ channelID,
486
+ portID,
487
+ rChannelID,
488
+ rPortID,
489
+ order: chanInfo.order,
490
+ },
491
+ );
492
+ onConnectP.resolver.resolve({
493
+ localAddress,
494
+ remoteAddress,
495
+ handler: rchandler,
496
+ });
497
+ break;
498
+ }
499
+
500
+ case 'channelOpenConfirm': {
501
+ const { portID, channelID } =
502
+ /** @type {IBCEvent<'channelOpenConfirm'>} */ (obj);
503
+
504
+ const channelKey = `${channelID}:${portID}`;
505
+ channelKeyToAttempt.has(channelKey) ||
506
+ Fail`${channelKey}: did not expect channelOpenConfirm`;
507
+ const attemptP = channelKeyToAttempt.get(channelKey);
508
+ channelKeyToAttempt.delete(channelKey);
509
+
510
+ // We have the information from our inbound connection, so complete it.
511
+ const {
512
+ order,
513
+ counterparty: { port_id: rPortID, channel_id: rChannelID },
514
+ } = channelKeyToInfo.get(channelKey);
515
+ channelKeyToInfo.delete(channelKey);
516
+
517
+ // Accept the attempt.
518
+ const rchandler = makeIBCConnectionHandler(
519
+ {
520
+ protocolUtils: util,
521
+ channelKeyToConnP,
522
+ channelKeyToSeqAck,
523
+ },
524
+ {
525
+ channelID,
526
+ portID,
527
+ rChannelID,
528
+ rPortID,
529
+ order,
530
+ },
531
+ );
532
+ const localAddr = await E(attemptP).getLocalAddress();
533
+ void E(attemptP).accept({
534
+ localAddress: `${localAddr}/ibc-channel/${channelID}`,
535
+ handler: rchandler,
536
+ });
537
+ break;
538
+ }
539
+
540
+ case 'receivePacket': {
541
+ const { packet } = /** @type {IBCEvent<'receivePacket'>} */ (obj);
542
+ const {
543
+ data: data64,
544
+ destination_port: portID,
545
+ destination_channel: channelID,
546
+ } = packet;
547
+ const channelKey = `${channelID}:${portID}`;
548
+ const conn = channelKeyToConnP.get(channelKey);
549
+ const data = base64ToBytes(data64);
550
+
551
+ return watch(E(conn).send(data), this.facets.ackWatcher, {
552
+ packet,
553
+ });
554
+ }
555
+
556
+ case 'acknowledgementPacket': {
557
+ const { packet, acknowledgement } =
558
+ /** @type {IBCEvent<'acknowledgementPacket'>} */ (obj);
559
+ const {
560
+ sequence,
561
+ source_channel: channelID,
562
+ source_port: portID,
563
+ } = packet;
564
+ if (sequence === undefined)
565
+ throw TypeError('acknowledgementPacket without sequence');
566
+ const ackKit = util.findAckKit(channelID, portID, sequence);
567
+ ackKit.resolver.resolve(base64ToBytes(acknowledgement));
568
+ break;
569
+ }
570
+
571
+ case 'timeoutPacket': {
572
+ const { packet } = /** @type {IBCEvent<'timeoutPacket'>} */ (obj);
573
+ const {
574
+ sequence,
575
+ source_channel: channelID,
576
+ source_port: portID,
577
+ } = packet;
578
+ if (sequence === undefined)
579
+ throw TypeError('timeoutPacket without sequence');
580
+
581
+ const ackKit = util.findAckKit(channelID, portID, sequence);
582
+ ackKit.resolver.reject(Error(`Packet timed out`));
583
+ break;
584
+ }
585
+
586
+ case 'channelCloseInit':
587
+ case 'channelCloseConfirm': {
588
+ const { portID, channelID } =
589
+ // could be either but that complicates line wrapping
590
+ /** @type {IBCEvent<'channelCloseInit'>} */ (obj);
591
+ const channelKey = `${channelID}:${portID}`;
592
+ if (channelKeyToConnP.has(channelKey)) {
593
+ const conn = channelKeyToConnP.get(channelKey);
594
+ channelKeyToConnP.delete(channelKey);
595
+ void E(conn).close();
596
+ }
597
+ break;
598
+ }
599
+
600
+ case 'sendPacket': {
601
+ const { packet, relativeTimeoutNs } =
602
+ /** @type {IBCEvent<'sendPacket'>} */ (obj);
603
+ util.ibcSendPacket(packet, relativeTimeoutNs).then(
604
+ ack => console.info('Manual packet', packet, 'acked:', ack),
605
+ e => console.warn('Manual packet', packet, 'failed:', e),
606
+ );
607
+ break;
608
+ }
609
+
610
+ default:
611
+ console.error('Unexpected IBC_EVENT', obj.event);
612
+ assert.fail(X`unrecognized method ${obj.event}`, TypeError);
613
+ }
614
+ },
615
+ },
616
+ util: {
617
+ /**
618
+ * @template {IBCDowncallMethod} M
619
+ * @param {M} method
620
+ * @param {IBCDowncall<M>} args
621
+ */
622
+ downcall(method, args) {
623
+ return E(this.state.ibcdev).downcall(method, args);
624
+ },
625
+ /**
626
+ * Send a packet out via the IBC device.
627
+ *
628
+ * @param {IBCPacket} packet
629
+ * @param {bigint} [relativeTimeoutNs]
630
+ * @returns {PromiseVow<Bytes>} Acknowledgement data
631
+ */
632
+ async ibcSendPacket(
633
+ packet,
634
+ relativeTimeoutNs = DEFAULT_PACKET_TIMEOUT_NS,
635
+ ) {
636
+ const { util } = this.facets;
637
+ // Make a kernel call to do the send.
638
+ const fullPacket = await util.downcall('sendPacket', {
639
+ packet,
640
+ relativeTimeoutNs,
641
+ });
642
+
643
+ // Extract the actual sequence number from the return.
644
+ const {
645
+ sequence,
646
+ source_channel: channelID,
647
+ source_port: portID,
648
+ } = fullPacket;
649
+
650
+ /** @type {VowKit<Bytes>} */
651
+ const { vow } = util.findAckKit(channelID, portID, sequence);
652
+ return vow;
653
+ },
654
+
655
+ /**
656
+ * @param {IBCChannelID} channelID
657
+ * @param {IBCPortID} portID
658
+ * @param {number} sequence
659
+ */
660
+ findAckKit(channelID, portID, sequence) {
661
+ const { channelKeyToSeqAck } = this.state;
662
+ const channelKey = `${channelID}:${portID}`;
663
+ const seqToAck = channelKeyToSeqAck.get(channelKey);
664
+ if (seqToAck.has(sequence)) {
665
+ return seqToAck.get(sequence);
666
+ }
667
+ const kit = makeVowKit();
668
+ seqToAck.init(sequence, kit);
669
+ return kit;
670
+ },
671
+ },
672
+ ackWatcher: {
673
+ /**
674
+ * @param {string} ack
675
+ * @param {{ packet: any }} watcherContext
676
+ */
677
+ onFulfilled(ack, watcherContext) {
678
+ const { packet } = watcherContext;
679
+
680
+ const realAck = ack || DEFAULT_ACKNOWLEDGEMENT;
681
+ const ack64 = byteSourceToBase64(realAck);
682
+ this.facets.util
683
+ .downcall('receiveExecuted', {
684
+ packet,
685
+ ack: ack64,
686
+ })
687
+ .catch(e => this.facets.ackWatcher.onRejected(e));
688
+ },
689
+ onRejected(e) {
690
+ console.log(e);
691
+ },
692
+ },
693
+ protocolImplAttemptWatcher: {
694
+ /**
695
+ * @param {string} attemptedLocal
696
+ * @param {{
697
+ * channelID: IBCChannelID;
698
+ * portID: string;
699
+ * attempt: any;
700
+ * obj: any;
701
+ * asyncVersions: any;
702
+ * rPortID: string;
703
+ * rChannelID: IBCChannelID;
704
+ * order: IBCChannelOrdering;
705
+ * hops: any;
706
+ * version: string;
707
+ * }} watcherContext
708
+ */
709
+ onFulfilled(attemptedLocal, watcherContext) {
365
710
  const {
366
711
  channelID,
367
712
  portID,
368
- counterparty: { port_id: rPortID, channel_id: rChannelID },
369
- connectionHops: hops,
713
+ attempt,
714
+ obj,
715
+ asyncVersions,
716
+ rPortID,
717
+ rChannelID,
370
718
  order,
719
+ hops,
371
720
  version,
372
- counterpartyVersion: rVersion,
373
- } = obj;
374
-
375
- const localAddr = `/ibc-port/${portID}/${order.toLowerCase()}/${version}`;
376
- const ibcHops = hops.map(hop => `/ibc-hop/${hop}`).join('/');
377
- const remoteAddr = `${ibcHops}/ibc-port/${rPortID}/${order.toLowerCase()}/${rVersion}/ibc-channel/${rChannelID}`;
378
-
379
- // See if we allow an inbound attempt for this address pair (without
380
- // rejecting).
381
- const attemptP = E(protocolImpl).inbound(localAddr, remoteAddr);
721
+ } = watcherContext;
722
+ const { channelKeyToAttempt, channelKeyToInfo } = this.state;
382
723
 
383
- // Tell what version string we negotiated.
384
- const attemptedLocal = await E(attemptP).getLocalAddress();
385
724
  const match = attemptedLocal.match(
386
725
  // Match: ... /ORDER/VERSION ...
387
726
  new RegExp('^(/[^/]+/[^/]+)*/(ordered|unordered)/([^/]+)(/|$)'),
@@ -393,193 +732,103 @@ export function makeIBCProtocolHandler(E, rawCallIBCDevice) {
393
732
  `${channelKey}: cannot determine version from attempted local address ${attemptedLocal}`,
394
733
  );
395
734
  }
396
- const negotiatedVersion = match[3];
397
735
 
398
- channelKeyToAttemptP.init(channelKey, attemptP);
736
+ channelKeyToAttempt.init(channelKey, attempt);
399
737
  channelKeyToInfo.init(channelKey, obj);
400
738
 
739
+ const negotiatedVersion = match[3];
740
+
401
741
  try {
402
- if (negotiatedVersion !== version) {
403
- // Too late; the relayer gave us a version we didn't like.
742
+ if (asyncVersions) {
743
+ // We have async version negotiation, so we must call back now.
744
+ return this.facets.util.downcall('tryOpenExecuted', {
745
+ packet: {
746
+ source_port: rPortID,
747
+ source_channel: rChannelID,
748
+ destination_port: portID,
749
+ destination_channel: channelID,
750
+ },
751
+ order,
752
+ version: negotiatedVersion,
753
+ hops,
754
+ });
755
+ } else if (negotiatedVersion !== version) {
756
+ // Too late; the other side gave us a version we didn't like.
404
757
  throw Error(
405
- `${channelKey}: negotiated version was ${negotiatedVersion}; rejecting ${version}`,
758
+ `${channelKey}: async negotiated version was ${negotiatedVersion} but synchronous version was ${version}`,
406
759
  );
407
760
  }
408
761
  } catch (e) {
409
762
  // Clean up after our failed attempt.
410
- channelKeyToAttemptP.delete(channelKey);
763
+ channelKeyToAttempt.delete(channelKey);
411
764
  channelKeyToInfo.delete(channelKey);
412
- void E(attemptP).close();
765
+ void E(attempt).close();
413
766
  throw e;
414
767
  }
415
- break;
416
- }
768
+ },
769
+ },
770
+ protocolImplInboundWatcher: {
771
+ /**
772
+ * @param {any} attempt
773
+ * @param {Record<string, any>} watchContext
774
+ */
775
+ onFulfilled(attempt, watchContext) {
776
+ const { obj } = watchContext;
417
777
 
418
- case 'channelOpenAck': {
419
- // Complete the pending outbound connection.
420
- const {
421
- portID,
422
- channelID,
423
- counterparty: { port_id: rPortID, channel_id: rChannelID },
424
- counterpartyVersion: rVersion,
425
- connectionHops: rHops,
426
- } = obj;
427
- const outbounds = srcPortToOutbounds.has(portID)
428
- ? srcPortToOutbounds.get(portID)
429
- : [];
430
- const oidx = outbounds.findIndex(
431
- ({ counterparty: { port_id: iPortID }, connectionHops: iHops }) => {
432
- if (iPortID !== rPortID) {
433
- return false;
434
- }
435
- if (iHops.length !== rHops.length) {
436
- return false;
437
- }
438
- for (let i = 0; i < iHops.length; i += 1) {
439
- if (iHops[i] !== rHops[i]) {
440
- return false;
441
- }
442
- }
443
- return true;
778
+ // Tell what version string we negotiated.
779
+ return watch(
780
+ E(attempt).getLocalAddress(),
781
+ this.facets.protocolImplAttemptWatcher,
782
+ {
783
+ ...obj,
784
+ ...watchContext,
785
+ attempt,
444
786
  },
445
787
  );
446
- oidx >= 0 || Fail`${portID}: did not expect channelOpenAck`;
447
- const { onConnectP, localAddr, ...chanInfo } = outbounds[oidx];
448
- outbounds.splice(oidx, 1);
449
- if (outbounds.length === 0) {
450
- srcPortToOutbounds.delete(portID);
451
- }
452
-
453
- // Finish the outbound connection.
454
- const ibcHops = rHops.map(hop => `/ibc-hop/${hop}`).join('/');
455
- const remoteAddress = `${ibcHops}/ibc-port/${rPortID}/${chanInfo.order.toLowerCase()}/${rVersion}/ibc-channel/${rChannelID}`;
456
- const localAddress = `${localAddr}/ibc-channel/${channelID}`;
457
- const rchandler = makeIBCConnectionHandler(
458
- channelID,
459
- portID,
460
- rChannelID,
461
- rPortID,
462
- chanInfo.order,
463
- );
464
- onConnectP.resolve({
465
- localAddress,
466
- remoteAddress,
467
- handler: rchandler,
468
- });
469
- break;
470
- }
471
-
472
- case 'channelOpenConfirm': {
473
- const { portID, channelID } = obj;
474
- const channelKey = `${channelID}:${portID}`;
475
- channelKeyToAttemptP.has(channelKey) ||
476
- Fail`${channelKey}: did not expect channelOpenConfirm`;
477
- const attemptP = channelKeyToAttemptP.get(channelKey);
478
- channelKeyToAttemptP.delete(channelKey);
479
-
480
- // We have the information from our inbound connection, so complete it.
481
- const {
482
- order,
483
- counterparty: { port_id: rPortID, channel_id: rChannelID },
484
- } = channelKeyToInfo.get(channelKey);
485
- channelKeyToInfo.delete(channelKey);
486
-
487
- // Accept the attempt.
488
- const rchandler = makeIBCConnectionHandler(
489
- channelID,
490
- portID,
491
- rChannelID,
492
- rPortID,
493
- order,
494
- );
495
- const localAddr = await E(attemptP).getLocalAddress();
496
- void E(attemptP).accept({
497
- localAddress: `${localAddr}/ibc-channel/${channelID}`,
498
- handler: rchandler,
499
- });
500
- break;
501
- }
502
-
503
- case 'receivePacket': {
504
- const { packet } = obj;
505
- const {
506
- data: data64,
507
- destination_port: portID,
508
- destination_channel: channelID,
509
- } = packet;
510
- const channelKey = `${channelID}:${portID}`;
511
- const connP = channelKeyToConnP.get(channelKey);
512
- const data = base64ToBytes(data64);
513
-
514
- await E(connP)
515
- .send(data)
516
- .then(ack => {
517
- const realAck = ack || DEFAULT_ACKNOWLEDGEMENT;
518
- const ack64 = dataToBase64(realAck);
519
- return callIBCDevice('receiveExecuted', { packet, ack: ack64 });
520
- })
521
- .catch(e => console.error(e));
522
- break;
523
- }
524
-
525
- case 'acknowledgementPacket': {
526
- const { packet, acknowledgement } = obj;
527
- const {
528
- sequence,
529
- source_channel: channelID,
530
- source_port: portID,
531
- } = packet;
532
- const channelKey = `${channelID}:${portID}`;
533
- const seqToAck = channelKeyToSeqAck.get(channelKey);
534
- const ackDeferred = seqToAck.get(sequence);
535
- ackDeferred.resolve(base64ToBytes(acknowledgement));
536
- seqToAck.delete(sequence);
537
- break;
538
- }
539
-
540
- case 'timeoutPacket': {
541
- const { packet } = obj;
542
- const {
543
- sequence,
544
- source_channel: channelID,
545
- source_port: portID,
546
- } = packet;
547
- const channelKey = `${channelID}:${portID}`;
548
- const seqToAck = channelKeyToSeqAck.get(channelKey);
549
- const ackDeferred = seqToAck.get(sequence);
550
- ackDeferred.reject(Error(`Packet timed out`));
551
- seqToAck.delete(sequence);
552
- break;
553
- }
554
-
555
- case 'channelCloseInit':
556
- case 'channelCloseConfirm': {
557
- const { portID, channelID } = obj;
558
- const channelKey = `${channelID}:${portID}`;
559
- if (channelKeyToConnP.has(channelKey)) {
560
- const connP = channelKeyToConnP.get(channelKey);
561
- channelKeyToConnP.delete(channelKey);
562
- void E(connP).close();
563
- }
564
- break;
565
- }
788
+ },
789
+ },
790
+ },
791
+ );
566
792
 
567
- case 'sendPacket': {
568
- const { packet, relativeTimeoutNs } = obj;
569
- const { source_port: portID, source_channel: channelID } = packet;
570
- const channelKey = `${channelID}:${portID}`;
571
- const seqToAck = channelKeyToSeqAck.get(channelKey);
572
- ibcSendPacket(packet, seqToAck, relativeTimeoutNs).then(
573
- ack => console.info('Manual packet', packet, 'acked:', ack),
574
- e => console.warn('Manual packet', packet, 'failed:', e),
575
- );
576
- break;
577
- }
793
+ /** @param {IBCDevice} ibcdev */
794
+ const makeIBCProtocolHandlerKit = ibcdev => {
795
+ const { protocolHandler, bridgeHandler } = makeIBCProtocolKit(ibcdev);
796
+ return harden({ protocolHandler, bridgeHandler });
797
+ };
578
798
 
579
- default:
580
- console.error('Unexpected IBC_EVENT', obj.event);
581
- assert.fail(X`unrecognized method ${obj.event}`, TypeError);
582
- }
799
+ /** @param {IBCDevice} ibcdev */
800
+ const provideIBCProtocolHandlerKit = ibcdev => {
801
+ if (ibcdevToKit.has(ibcdev)) {
802
+ return ibcdevToKit.get(ibcdev);
803
+ }
804
+ const kit = makeIBCProtocolHandlerKit(ibcdev);
805
+ ibcdevToKit.init(ibcdev, kit);
806
+ return kit;
807
+ };
808
+ return provideIBCProtocolHandlerKit;
809
+ };
810
+ harden(prepareIBCProtocol);
811
+
812
+ /** @param {Zone} zone */
813
+ export const prepareCallbacks = zone => {
814
+ return zone.exoClass(
815
+ 'callbacks',
816
+ undefined,
817
+ /** @param {ScopedBridgeManager<'dibc'>} dibcBridgeManager */
818
+ dibcBridgeManager => ({ dibcBridgeManager }),
819
+ {
820
+ /**
821
+ * @param {string} method
822
+ * @param {any} obj
823
+ */
824
+ downcall(method, obj) {
825
+ const { dibcBridgeManager } = this.state;
826
+ return E(dibcBridgeManager).toBridge({
827
+ ...obj,
828
+ type: 'IBC_METHOD',
829
+ method,
830
+ });
831
+ },
583
832
  },
584
- });
585
- }
833
+ );
834
+ };