Skip to content

Commit

Permalink
feat: end-to-end dIBC across chains
Browse files Browse the repository at this point in the history
c = home.ibcport~.connect('/ibc-hop/ibconelink/ibc-port/echo/ordered/echo-2', home.agent.text('echo'))
... follow the instructions
c~.send('foobar')
  • Loading branch information
michaelfig committed May 1, 2020
1 parent e778b72 commit 151ff3f
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 194 deletions.
106 changes: 53 additions & 53 deletions packages/SwingSet/src/vats/network/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const harden = /** @type {<T>(x: T) => T} */ (rawHarden);
* @property {(port: Port, localAddr: Endpoint, p: ProtocolHandler) => Promise<void>} onBind A port will be bound
* @property {(port: Port, localAddr: Endpoint, listenHandler: ListenHandler, p: ProtocolHandler) => Promise<void>} onListen A port was listening
* @property {(port: Port, localAddr: Endpoint, listenHandler: ListenHandler, p: ProtocolHandler) => Promise<void>} onListenRemove A port listener has been reset
* @property {(port: Port, localAddr: Endpoint, remote: Endpoint, p: ProtocolHandler) => Promise<ConnectionHandler|undefined>} onConnect A port initiates an outbound connection
* @property {(port: Port, localAddr: Endpoint, remote: Endpoint, c: ConnectionHandler, p: ProtocolHandler) => Promise<ConnectionHandler|undefined>} onConnect A port initiates an outbound connection
* @property {(port: Port, localAddr: Endpoint, p: ProtocolHandler) => Promise<void>} onRevoke The port is being completely destroyed
*
* @typedef {Object} ProtocolImpl Things the protocol can do for us
Expand Down Expand Up @@ -227,6 +227,26 @@ export function crossoverConnection(
return [conn0, conn1];
}

/**
* Get the list of prefixes from longest to shortest.
* @param {string} addr
* @param {string} [sep='/']
*/
export function getPrefixes(addr, sep = '/') {
const parts = addr.split(sep);

/**
* @type {string[]}
*/
const ret = [];
for (let i = parts.length; i > 0; i -= 1) {
// Try most specific match.
const prefix = parts.slice(0, i).join(sep);
ret.push(prefix);
}
return ret;
}

/**
* Create a protocol that has a handler.
*
Expand Down Expand Up @@ -284,12 +304,19 @@ export function makeNetworkProtocol(protocolHandler, E = defaultE) {
const localAddr =
/** @type {string} */
(await E(port).getLocalAddress());

const ret = getPrefixes(remoteAddr, '/');
if (await protocolImpl.isListening(ret)) {
return protocolImpl.inbound(ret, remoteAddr, localAddr, lchandler);
}

const rchandler =
/** @type {ConnectionHandler} */
(await E(protocolHandler).onConnect(
port,
localAddr,
remoteAddr,
lchandler,
protocolHandler,
));

Expand Down Expand Up @@ -365,16 +392,24 @@ export function makeNetworkProtocol(protocolHandler, E = defaultE) {
throw TypeError(`listenHandler is not defined`);
}
if (listening.has(localAddr)) {
throw Error(`Port ${localAddr} is already listening`);
const [lport, lhandler] = listening.get(localAddr);
if (lhandler === listenHandler) {
return;
}
listening.set(localAddr, [port, listenHandler]);
E(lhandler)
.onRemove(lport, lhandler)
.catch(rethrowUnlessMissing);
} else {
listening.init(localAddr, [port, listenHandler]);
}

await E(protocolHandler).onListen(
port,
localAddr,
listenHandler,
protocolHandler,
);
// TODO: Handle the race between revoke() and open connections.
listening.init(localAddr, [port, listenHandler]);
await E(listenHandler)
.onListen(port, listenHandler)
.catch(rethrowUnlessMissing);
Expand All @@ -386,13 +421,13 @@ export function makeNetworkProtocol(protocolHandler, E = defaultE) {
if (listening.get(localAddr)[1] !== listenHandler) {
throw Error(`Port ${localAddr} handler to remove is not listening`);
}
listening.delete(localAddr);
await E(protocolHandler).onListenRemove(
port,
localAddr,
listenHandler,
protocolHandler,
);
listening.delete(localAddr);
await E(listenHandler)
.onRemove(port, listenHandler)
.catch(rethrowUnlessMissing);
Expand Down Expand Up @@ -489,8 +524,8 @@ export function makeLoopbackProtocolHandler(E = defaultE) {

return harden({
// eslint-disable-next-line no-empty-function
async onCreate(_protocol, _protocolHandler) {
// TODO: Maybe do something on creation?
async onCreate(_impl, _protocolHandler) {
// TODO
},
async generatePortID(_protocolHandler) {
nonce += 1;
Expand All @@ -499,7 +534,7 @@ export function makeLoopbackProtocolHandler(E = defaultE) {
async onBind(_port, _localAddr, _protocolHandler) {
// TODO: Maybe handle a bind?
},
async onConnect(_port, localAddr, remoteAddr, _protocolHandler) {
async onConnect(_port, localAddr, remoteAddr, _chandler, _protocolHandler) {
if (!listeners.has(remoteAddr)) {
return undefined;
}
Expand All @@ -512,7 +547,16 @@ export function makeLoopbackProtocolHandler(E = defaultE) {
return rport;
},
async onListen(port, localAddr, listenHandler, _protocolHandler) {
listeners.init(localAddr, [port, listenHandler]);
// TODO: Implement other listener replacement policies.
if (listeners.has(localAddr)) {
const lhandler = listeners.get(localAddr)[1];
if (lhandler !== listenHandler) {
// Last-one-wins.
listeners.set(localAddr, [port, listenHandler]);
}
} else {
listeners.init(localAddr, [port, listenHandler]);
}
},
async onListenRemove(port, localAddr, listenHandler, _protocolHandler) {
const [lport, lhandler] = listeners.get(localAddr);
Expand All @@ -529,47 +573,3 @@ export function makeLoopbackProtocolHandler(E = defaultE) {
},
});
}

/**
* Create a protocol that combines the subprotocol
* with a loopback protocol.
*
* @param {ProtocolHandler} subi the handler to delegate non-loopback connections
* @returns {ProtocolHandler} the extended protocol
*/
export function extendLoopbackProtocolHandler(subi) {
const loopback = makeLoopbackProtocolHandler();
return harden({
async onCreate(impl, _protocolHandler) {
await subi.onCreate(impl, subi);
await loopback.onCreate(impl, loopback);
},
async generatePortID(localAddr, _protocolHandler) {
return subi.generatePortID(localAddr, subi);
},
async onBind(port, localAddr, _protocolHandler) {
await subi.onBind(port, localAddr, subi);
await loopback.onBind(port, localAddr, loopback);
},
async onConnect(port, localAddr, remoteAddr, _protocolHandler) {
// Try loopback connection first.
const c = await loopback.onConnect(port, localAddr, remoteAddr, loopback);
if (c) {
return c;
}
return subi.onConnect(port, localAddr, remoteAddr, subi);
},
async onListen(port, localAddr, listenHandler) {
await subi.onListen(port, localAddr, listenHandler, subi);
await loopback.onListen(port, localAddr, listenHandler, loopback);
},
async onListenRemove(port, localAddr, listenHandler) {
await subi.onListenRemove(port, localAddr, listenHandler, subi);
await loopback.onListenRemove(port, localAddr, listenHandler, loopback);
},
async onRevoke(port, localAddr) {
await subi.onRevoke(port, localAddr, subi);
await loopback.onRevoke(port, localAddr, loopback);
},
});
}
14 changes: 9 additions & 5 deletions packages/SwingSet/src/vats/network/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function makeRouter(sep = '/') {
const prefixToRoute = makeStore('prefix');
return harden({
getRoutes(addr) {
const parts = addr.split('/');
const parts = addr.split(sep);
/**
* @type {[string, any][]}
*/
Expand Down Expand Up @@ -84,13 +84,17 @@ export function makeRouterProtocol(sep = '/', E = defaultE) {
const protocols = makeStore('prefix');
const protocolHandlers = makeStore('prefix');

function registerProtocolHandler(prefix, protocolHandler) {
function registerProtocolHandler(paths, protocolHandler) {
const protocol = makeNetworkProtocol(protocolHandler);
router.register(prefix, protocol);
protocols.init(prefix, protocol);
protocolHandlers.init(prefix, protocolHandler);
for (const prefix of paths) {
router.register(prefix, protocol);
protocols.init(prefix, protocol);
protocolHandlers.init(prefix, protocolHandler);
}
}

// FIXME: Buggy.
// Needs to account for multiple paths.
function unregisterProtocolHandler(prefix, protocolHandler) {
const ph = protocolHandlers.get(prefix);
if (ph !== protocolHandler) {
Expand Down
20 changes: 11 additions & 9 deletions packages/cosmic-swingset/lib/ag-solo/html/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ function run() {
function linesToHTML(lines) {
return lines
.split('\n')
.map(l =>
l
.replace('&', '&amp;')
.replace('<', '&lt;')
.replace(/\t/g, ' ')
.replace(/\s/g, '&nbsp;'),
.map(
l =>
l
.replace('&', '&amp;') // quote ampersands
.replace('<', '&lt;') // quote html
.replace(/\t/g, ' ') // expand tabs
.replace(/ /g, '&nbsp;') // convert spaces
.replace(/&nbsp;&nbsp;/g, '&nbsp; '), // allow multispace to break
)
.join('<br />');
}
Expand All @@ -65,7 +67,7 @@ function run() {
label.textContent = `${kind}[${histnum}]`;
const content = document.createElement('div');
content.id = `${kind}-${histnum}`;
content.textContent = `${value}`;
content.innerHTML = linesToHTML(`${value}`);
row.appendChild(label);
row.appendChild(content);
h.append(row);
Expand Down Expand Up @@ -102,9 +104,9 @@ function run() {
const h1 = document.getElementById(`history-${histnum}`);
const m1 = document.getElementById(`msg-command-${histnum}`);
const m2 = document.getElementById(`msg-history-${histnum}`);
c.textContent = `${command}`;
c.innerHTML = linesToHTML(`${command}`);
m1.innerHTML = linesToHTML(`${consoles.command}`);
h1.textContent = `${result}`;
h1.innerHTML = linesToHTML(`${result}`);
m2.innerHTML = linesToHTML(`${consoles.display}`);
} else {
addHistoryEntry(histnum, command, result, consoles);
Expand Down
12 changes: 7 additions & 5 deletions packages/cosmic-swingset/lib/ag-solo/vats/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,11 @@ export default function setup(syscall, state, helpers) {
// Every vat has a loopback device.
ps.push(
E(vats.network).registerProtocolHandler(
'/local',
['/local'],
makeLoopbackProtocolHandler(),
),
);
// FIXME: Temporarily disable IBC access until we have stability.
if (false && bridgeMgr) {
if (bridgeMgr) {
// We have access to the bridge, and therefore IBC.
const callbacks = harden({
downcall(method, obj) {
Expand All @@ -159,12 +158,15 @@ export default function setup(syscall, state, helpers) {
);
bridgeMgr.register('dibc', ibcHandler);
ps.push(
E(vats.network).registerProtocolHandler('/ibc-port', ibcHandler),
E(vats.network).registerProtocolHandler(
['/ibc-port', '/ibc-hop'],
ibcHandler,
),
);
} else {
const loHandler = makeLoopbackProtocolHandler(E);
ps.push(
E(vats.network).registerProtocolHandler('/ibc-port', loHandler),
E(vats.network).registerProtocolHandler(['/ibc-port'], loHandler),
);
}
await Promise.all(ps);
Expand Down
Loading

0 comments on commit 151ff3f

Please sign in to comment.