From 421b9d432e26670f223518acbaf7d9bd55d63ca3 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Sun, 11 Apr 2021 18:05:26 -0600 Subject: [PATCH] fix: fully implement onInbound for unique connection ID --- packages/SwingSet/src/vats/network/network.js | 52 +++++++++++-------- packages/SwingSet/src/vats/network/types.js | 3 +- .../cosmic-swingset/lib/ag-solo/vats/ibc.js | 25 ++++++++- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/packages/SwingSet/src/vats/network/network.js b/packages/SwingSet/src/vats/network/network.js index 6e7d77a9f50..3deeb93cf2b 100644 --- a/packages/SwingSet/src/vats/network/network.js +++ b/packages/SwingSet/src/vats/network/network.js @@ -385,10 +385,16 @@ export function makeNetworkProtocol(protocolHandler) { const [port, listener] = listening.get(listenPrefix); let localAddr; try { - // See if we have a listener that's willing to receive this connection. + // See if our protocol is willing to receive this connection. // eslint-disable-next-line no-await-in-loop - const localSuffix = await E(listener) - .onInbound(port, listenPrefix, remoteAddr, listener) + const localSuffix = await E(protocolHandler) + .onInbound( + port, + listenPrefix, + remoteAddr, + listener, + protocolHandler, + ) .catch(rethrowUnlessMissing); localAddr = localSuffix ? `${listenPrefix}/${localSuffix}` @@ -508,7 +514,7 @@ export function makeEchoConnectionHandler() { } return bytes; }, - async onClose(_connection, _connectionHandler) { + async onClose(_connection, _reason, _connectionHandler) { if (closed) { throw closed; } @@ -517,18 +523,29 @@ export function makeEchoConnectionHandler() { }); } +export function makeNonceMaker(prefix = '', suffix = '') { + let nonce = 0; + return async () => { + nonce += 1; + return `${prefix}${nonce}${suffix}`; + }; +} + /** * Create a protocol handler that just connects to itself. * + * @param {ProtocolHandler['onInbound']} [onInbound] * @returns {ProtocolHandler} The localhost handler */ -export function makeLoopbackProtocolHandler() { +export function makeLoopbackProtocolHandler( + onInbound = makeNonceMaker('nonce/'), +) { /** * @type {Store} */ const listeners = makeStore('localAddr'); - let nonce = 0; + const makePortID = makeNonceMaker('port'); return Far('ProtocolHandler', { // eslint-disable-next-line no-empty-function @@ -536,31 +553,24 @@ export function makeLoopbackProtocolHandler() { // TODO }, async generatePortID(_protocolHandler) { - nonce += 1; - return `port${nonce}`; + return makePortID(); }, async onBind(_port, _localAddr, _protocolHandler) { // TODO: Maybe handle a bind? }, - async onConnect(_port, localAddr, remoteAddr, _chandler, _protocolHandler) { + async onConnect(_port, localAddr, remoteAddr, _chandler, protocolHandler) { const [lport, lhandler] = listeners.get(remoteAddr); - // console.log(`looking up onAccept in`, lhandler); - const remoteSuffix = - /** @type {Endpoint} */ - (await E(lhandler) - .onInbound(lport, remoteAddr, localAddr, lhandler) - .catch(e => rethrowUnlessMissing(e))); - - if (remoteSuffix) { - remoteAddr = `${remoteAddr}/${remoteSuffix}`; - } - const rchandler = /** @type {ConnectionHandler} */ (await E(lhandler).onAccept(lport, remoteAddr, localAddr, lhandler)); // console.log(`rchandler is`, rchandler); - return [remoteAddr, rchandler]; + const remoteSuffix = await E(protocolHandler) + .onInbound(lport, remoteAddr, localAddr, lhandler, protocolHandler) + .catch(rethrowUnlessMissing); + const ra = remoteSuffix ? `${remoteAddr}/${remoteSuffix}` : remoteAddr; + return [ra, rchandler]; }, + onInbound, async onListen(port, localAddr, listenHandler, _protocolHandler) { // TODO: Implement other listener replacement policies. if (listeners.has(localAddr)) { diff --git a/packages/SwingSet/src/vats/network/types.js b/packages/SwingSet/src/vats/network/types.js index 0f4cdbaf78c..c3040542ddd 100644 --- a/packages/SwingSet/src/vats/network/types.js +++ b/packages/SwingSet/src/vats/network/types.js @@ -30,7 +30,6 @@ /** * @typedef {Object} ListenHandler A handler for incoming connections * @property {(port: Port, l: ListenHandler) => Promise} [onListen] The listener has been registered - * @property {(port: Port, listenAddr: Endpoint, remoteAddr: Endpoint, l: ListenHandler) => Promise} [onInbound] Return metadata for inbound connection attempt * @property {(port: Port, localAddr: Endpoint, remoteAddr: Endpoint, l: ListenHandler) => Promise} onAccept A new connection is incoming * @property {(port: Port, localAddr: Endpoint, remoteAddr: Endpoint, l: ListenHandler) => Promise} onReject The connection was rejected * @property {(port: Port, rej: any, l: ListenHandler) => Promise} [onError] There was an error while listening @@ -61,6 +60,8 @@ * @property {(port: Port, localAddr: Endpoint, p: ProtocolHandler) => Promise} onBind A port will be bound * @property {(port: Port, localAddr: Endpoint, listenHandler: ListenHandler, p: ProtocolHandler) => Promise} onListen A port was listening * @property {(port: Port, localAddr: Endpoint, listenHandler: ListenHandler, p: ProtocolHandler) => Promise} onListenRemove A port listener has been reset + * @property {(port: Port, listenAddr: Endpoint, remoteAddr: Endpoint, l: ListenHandler, p: ProtocolHandler) => Promise} [onInbound] Return suffix for + * inbound connection attempt * @property {(port: Port, localAddr: Endpoint, remote: Endpoint, c: ConnectionHandler, p: ProtocolHandler) => Promise<[Endpoint, ConnectionHandler]>} onConnect A port initiates an outbound connection * @property {(port: Port, localAddr: Endpoint, p: ProtocolHandler) => Promise} onRevoke The port is being completely destroyed * diff --git a/packages/cosmic-swingset/lib/ag-solo/vats/ibc.js b/packages/cosmic-swingset/lib/ag-solo/vats/ibc.js index 022486501be..01aa3029380 100644 --- a/packages/cosmic-swingset/lib/ag-solo/vats/ibc.js +++ b/packages/cosmic-swingset/lib/ag-solo/vats/ibc.js @@ -242,6 +242,9 @@ export function makeIBCProtocolHandler(E, rawCallIBCDevice) { */ const portToPendingConns = makeStore('Port'); + /** @type {Store} */ + const remoteAddrToLocalSuffix = makeStore(); + /** * @type {ProtocolHandler} */ @@ -263,6 +266,17 @@ export function makeIBCProtocolHandler(E, rawCallIBCDevice) { }; return callIBCDevice('bindPort', { packet }); }, + async onInbound( + _port, + _listenAddr, + remoteAddr, + _lhandler, + _protocolHandler, + ) { + // we can take advantage of the fact that remoteAddrs are unique (they + // have their own channelID). + return remoteAddrToLocalSuffix.get(remoteAddr); + }, async onConnect(port, localAddr, remoteAddr, chandler, _protocolHandler) { console.debug('IBC onConnect', localAddr, remoteAddr); const portID = localAddrToPortID(localAddr); @@ -451,11 +465,18 @@ EOF const ibcHops = hops.map(hop => `/ibc-hop/${hop}`).join('/'); const remoteAddr = `${ibcHops}/ibc-port/${rPortID}/${order.toLowerCase()}/${rVersion}/ibc-channel/${rChannelID}`; - // See if we allow an inbound attempt for this address pair (without rejecting). + // See if we allow an inbound attempt for this address pair (without + // rejecting). + remoteAddrToLocalSuffix.init(remoteAddr, `ibc-channel/${channelID}`); const attemptP = E(protocolImpl).inbound(localAddr, remoteAddr); // Tell what version string we negotiated. - const attemptedLocal = await E(attemptP).getLocalAddress(); + const attemptedLocal = await E(attemptP) + .getLocalAddress() + .finally(() => { + // No matter what, delete the temporary mapping. + remoteAddrToLocalSuffix.delete(remoteAddr); + }); const match = attemptedLocal.match( // Match: ... /ORDER/VERSION ... new RegExp('^(/[^/]+/[^/]+)*/(ordered|unordered)/([^/]+)(/|$)'),