Skip to content

Commit

Permalink
feat: chain-hub resumable
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Jun 20, 2024
1 parent f928bdd commit 5e0643b
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 69 deletions.
6 changes: 4 additions & 2 deletions packages/orchestration/src/examples/sendAnywhere.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { withdrawFromSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js'
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
import { E } from '@endo/far';
import { M, mustMatch } from '@endo/patterns';

import { V } from '@agoric/vow/vat.js';
import { AmountShape } from '@agoric/ertp';
import { CosmosChainInfoShape } from '../typeGuards.js';
import { provideOrchestration } from '../utils/start-helper.js';
Expand Down Expand Up @@ -134,7 +134,9 @@ export const start = async (zcf, privateArgs, baggage) => {
*/
async addChain(chainInfo, connectionInfo) {
const chainKey = `${chainInfo.chainId}-${(nonce += 1n)}`;
const agoricChainInfo = await chainHub.getChainInfo('agoric');
// when() because chainHub methods return vows. If this were inside
// orchestrate() the membrane would wrap/unwrap automatically.
const agoricChainInfo = await V.when(chainHub.getChainInfo('agoric'));
chainHub.registerChain(chainKey, chainInfo);
chainHub.registerConnection(
agoricChainInfo.chainId,
Expand Down
106 changes: 58 additions & 48 deletions packages/orchestration/src/exos/chain-hub.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { VowShape } from '@agoric/vow';
import { allVows, watch } from '@agoric/vow/vat.js';
import { makeHeapZone } from '@agoric/zone';
import { E } from '@endo/far';
import { M } from '@endo/patterns';
Expand All @@ -7,6 +9,7 @@ const { Fail } = assert;

/**
* @import {NameHub} from '@agoric/vats';
* @import {Vow} from '@agoric/vow';
* @import {CosmosChainInfo, IBCConnectionInfo} from '../cosmos-api.js';
* @import {ChainInfo, KnownChains} from '../chain-info.js';
* @import {Remote} from '@agoric/internal';
Expand Down Expand Up @@ -67,15 +70,14 @@ const ChainIdArgShape = M.or(

const ChainHubI = M.interface('ChainHub', {
registerChain: M.call(M.string(), CosmosChainInfoShape).returns(),
getChainInfo: M.callWhen(M.string()).returns(CosmosChainInfoShape),
registerConnection: M.callWhen(
getChainInfo: M.call(M.string()).returns(VowShape),
registerConnection: M.call(
M.string(),
M.string(),
IBCConnectionInfoShape,
).returns(),
getConnectionInfo: M.callWhen(ChainIdArgShape, ChainIdArgShape).returns(
IBCConnectionInfoShape,
),
getConnectionInfo: M.call(ChainIdArgShape, ChainIdArgShape).returns(VowShape),
getChainsAndConnection: M.call(M.string(), M.string()).returns(VowShape),
});

/**
Expand Down Expand Up @@ -120,22 +122,25 @@ export const makeChainHub = (agoricNames, zone = makeHeapZone()) => {
/**
* @template {string} K
* @param {K} chainName
* @returns {Promise<ActualChainInfo<K>>}
* @returns {Vow<ActualChainInfo<K>>}
*/
async getChainInfo(chainName) {
getChainInfo(chainName) {
// Either from registerChain or memoized remote lookup()
if (chainInfos.has(chainName)) {
// @ts-expect-error cast
return chainInfos.get(chainName);
return /** @type {Vow<ActualChainInfo<K>>} */ (
watch(chainInfos.get(chainName))
);
}

const chainInfo = await E(agoricNames)
.lookup(CHAIN_KEY, chainName)
.catch(_cause => {
return watch(E(agoricNames).lookup(CHAIN_KEY, chainName), {
onFulfilled: chainInfo => {
chainInfos.init(chainName, chainInfo);
return chainInfo;
},
onRejected: _cause => {
throw assert.error(`chain not found:${chainName}`);
});
chainInfos.init(chainName, chainInfo);
return chainInfo;
},
});
},
/**
* @param {string} chainId1
Expand All @@ -150,50 +155,55 @@ export const makeChainHub = (agoricNames, zone = makeHeapZone()) => {
/**
* @param {string | { chainId: string }} chain1
* @param {string | { chainId: string }} chain2
* @returns {Promise<IBCConnectionInfo>}
* @returns {Vow<IBCConnectionInfo>}
*/
async getConnectionInfo(chain1, chain2) {
getConnectionInfo(chain1, chain2) {
const chainId1 = typeof chain1 === 'string' ? chain1 : chain1.chainId;
const chainId2 = typeof chain2 === 'string' ? chain2 : chain2.chainId;
const key = connectionKey(chainId1, chainId2);
if (connectionInfos.has(key)) {
return connectionInfos.get(key);
return watch(connectionInfos.get(key));
}

const connectionInfo = await E(agoricNames)
.lookup(CONNECTIONS_KEY, key)
.catch(_cause => {
return watch(E(agoricNames).lookup(CONNECTIONS_KEY, key), {
onFulfilled: connectionInfo => {
connectionInfos.init(key, connectionInfo);
return connectionInfo;
},
onRejected: _cause => {
throw assert.error(`connection not found: ${chainId1}<->${chainId2}`);
});
connectionInfos.init(key, connectionInfo);
return connectionInfo;
},
});
},

/**
* @template {string} C1
* @template {string} C2
* @param {C1} chainName1
* @param {C2} chainName2
* @returns {Vow<
* [ActualChainInfo<C1>, ActualChainInfo<C2>, IBCConnectionInfo]
* >}
*/
getChainsAndConnection(chainName1, chainName2) {
return watch(
allVows([
chainHub.getChainInfo(chainName1),
chainHub.getChainInfo(chainName2),
]),
{
onFulfilled: ([chain1, chain2]) => {
return watch(chainHub.getConnectionInfo(chain2, chain1), {
onFulfilled: connectionInfo => {
return [chain1, chain2, connectionInfo];
},
});
},
},
);
},
});

return chainHub;
};
/** @typedef {ReturnType<typeof makeChainHub>} ChainHub */

/**
* @template {string} C1
* @template {string} C2
* @param {ChainHub} chainHub
* @param {C1} chainName1
* @param {C2} chainName2
* @returns {Promise<
* [ActualChainInfo<C1>, ActualChainInfo<C2>, IBCConnectionInfo]
* >}
*/
export const getChainsAndConnection = async (
chainHub,
chainName1,
chainName2,
) => {
const [chain1, chain2] = await Promise.all([
chainHub.getChainInfo(chainName1),
chainHub.getChainInfo(chainName2),
]);
const connectionInfo = await chainHub.getConnectionInfo(chain2, chain1);

return [chain1, chain2, connectionInfo];
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { typedJson } from '@agoric/cosmic-proto/vatsafe';
import { AmountShape, PaymentShape } from '@agoric/ertp';
import { makeTracer } from '@agoric/internal';
import { M } from '@agoric/vat-data';
import { VowShape } from '@agoric/vow';
import { V } from '@agoric/vow/vat.js';
import { TopicsRecordShape } from '@agoric/zoe/src/contractSupport/index.js';
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
Expand Down Expand Up @@ -89,7 +90,7 @@ export const prepareLocalOrchestrationAccountKit = (
getChainInfoWatcher: M.interface('getChainInfoWatcher', {
onFulfilled: M.call(M.record()) // agoric chain info
.optional({ destination: ChainAddressShape }) // empty context
.returns(M.promise()), // transfer channel
.returns(VowShape), // transfer channel
}),
getTimeoutTimestampWatcher: M.interface('getTimeoutTimestampWatcher', {
onFulfilled: M.call(M.bigint())
Expand Down
13 changes: 7 additions & 6 deletions packages/orchestration/src/exos/orchestrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import { makeTracer } from '@agoric/internal';
import { E } from '@endo/far';
import { M } from '@endo/patterns';
import {
ChainInfoShape,
LocalChainAccountShape,
DenomShape,
BrandInfoShape,
ChainInfoShape,
DenomAmountShape,
DenomShape,
LocalChainAccountShape,
} from '../typeGuards.js';
import { getChainsAndConnection } from './chain-hub.js';

/**
* @import {Zone} from '@agoric/base-zone';
Expand Down Expand Up @@ -78,7 +77,7 @@ export const prepareOrchestratorKit = (
makeRemoteChainFacadeWatcher: M.interface(
'makeRemoteChainFacadeWatcher',
{
onFulfilled: M.call(M.arrayOf(M.record()))
onFulfilled: M.call(M.any())
.optional(M.arrayOf(M.undefined()))
.returns(M.any()), // FIXME narrow
},
Expand Down Expand Up @@ -115,16 +114,18 @@ export const prepareOrchestratorKit = (
/** @type {Orchestrator['getChain']} */
getChain(name) {
if (name === 'agoric') {
// XXX when() until membrane
return when(
watch(
chainHub.getChainInfo('agoric'),
this.facets.makeLocalChainFacadeWatcher,
),
);
}
// XXX when() until membrane
return when(
watch(
getChainsAndConnection(chainHub, 'agoric', name),
chainHub.getChainsAndConnection('agoric', name),
this.facets.makeRemoteChainFacadeWatcher,
),
);
Expand Down
9 changes: 4 additions & 5 deletions packages/orchestration/src/proposals/start-stakeAtom.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { makeTracer } from '@agoric/internal';
import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js';
import { E } from '@endo/far';
import { getChainsAndConnection, makeChainHub } from '../exos/chain-hub.js';
import { V } from '@agoric/vow/vat.js';
import { makeChainHub } from '../exos/chain-hub.js';

/**
* @import {IBCConnectionID} from '@agoric/vats';
Expand Down Expand Up @@ -46,10 +47,8 @@ export const startStakeAtom = async ({

const chainHub = makeChainHub(await agoricNames);

const [_, cosmoshub, connectionInfo] = await getChainsAndConnection(
chainHub,
'agoric',
'cosmoshub',
const [_, cosmoshub, connectionInfo] = await V.when(
chainHub.getChainsAndConnection('agoric', 'cosmoshub'),
);

/** @type {StartUpgradableOpts<StakeIcaSF>} */
Expand Down
9 changes: 4 additions & 5 deletions packages/orchestration/src/proposals/start-stakeOsmo.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { makeTracer } from '@agoric/internal';
import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js';
import { V } from '@agoric/vow/vat.js';
import { E } from '@endo/far';
import { getChainsAndConnection, makeChainHub } from '../exos/chain-hub.js';
import { makeChainHub } from '../exos/chain-hub.js';

/**
* @import {IBCConnectionID} from '@agoric/vats';
Expand Down Expand Up @@ -47,10 +48,8 @@ export const startStakeOsmo = async ({

const chainHub = makeChainHub(await agoricNames);

const [_, osmosis, connectionInfo] = await getChainsAndConnection(
chainHub,
'agoric',
'osmosis',
const [_, osmosis, connectionInfo] = await V.when(
chainHub.getChainsAndConnection('agoric', 'osmosis'),
);

/** @type {StartUpgradableOpts<StakeIcaSF>} */
Expand Down
8 changes: 6 additions & 2 deletions packages/orchestration/test/exos/chain-hub.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import test from '@endo/ses-ava/prepare-endo.js';

import { makeNameHubKit } from '@agoric/vats';
import { V } from '@agoric/vow/vat.js';
import { makeChainHub } from '../../src/exos/chain-hub.js';

const connection = {
Expand Down Expand Up @@ -37,8 +38,11 @@ test('getConnectionInfo', async t => {

// Look up by string or info object
t.deepEqual(
await chainHub.getConnectionInfo(aChain.chainId, bChain.chainId),
await V.when(chainHub.getConnectionInfo(aChain.chainId, bChain.chainId)),
connection,
);
t.deepEqual(
await V.when(chainHub.getConnectionInfo(aChain, bChain)),
connection,
);
t.deepEqual(await chainHub.getConnectionInfo(aChain, bChain), connection);
});

0 comments on commit 5e0643b

Please sign in to comment.