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