Skip to content

Commit

Permalink
ScopedBridgeManager utils (Agoric#9396)
Browse files Browse the repository at this point in the history
refs: Agoric#9063
refs: Agoric#9207 


## Description

As part of Agoric#9063 I'm trying to improve the testing situation. One
challenge is faking the bridges.

This PR factors out some test utils, with the eventual goal of providing
fixtures in each test for the bridge comms.

Along the way I realized that one of the tests had the wrong bridge, and
that we could have avoided that if the scoped bridges were typed by
their bridgeId. So this also does that. @michaelfig I'd like your take
on this.

### Security Considerations

<!-- Does this change introduce new assumptions or dependencies that, if
violated, could introduce security vulnerabilities? How does this PR
change the boundaries between mutually-suspicious components? What new
authorities are introduced by this change, perhaps by new API calls?
-->

### Scaling Considerations

<!-- Does this change require or encourage significant increase in
consumption of CPU cycles, RAM, on-chain storage, message exchanges, or
other scarce resources? If so, can that be prevented or mitigated? -->

### Documentation Considerations

<!-- Give our docs folks some hints about what needs to be described to
downstream users.

Backwards compatibility: what happens to existing data or deployments
when this code is shipped? Do we need to instruct users to do something
to upgrade their saved data? If there is no upgrade path possible, how
bad will that be for users?

-->

### Testing Considerations

<!-- Every PR should of course come with tests of its own functionality.
What additional tests are still needed beyond those unit tests? How does
this affect CI, other test automation, or the testnet?
-->

### Upgrade Considerations

<!-- What aspects of this PR are relevant to upgrading live production
systems, and how should they be addressed? -->
  • Loading branch information
mergify[bot] authored May 23, 2024
2 parents 0f12662 + caba319 commit 90906b1
Show file tree
Hide file tree
Showing 27 changed files with 191 additions and 249 deletions.
60 changes: 0 additions & 60 deletions packages/boot/test/bootstrapTests/ibcBridgeMock.js

This file was deleted.

27 changes: 16 additions & 11 deletions packages/boot/test/bootstrapTests/net-ibc-upgrade.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/** @file upgrade network / IBC vat at many points in state machine */
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import { createRequire } from 'module';
import type { TestFn } from 'ava';
import type { BridgeHandler } from '@agoric/vats';
import { BridgeId } from '@agoric/internal';
import type { BridgeHandler } from '@agoric/vats';
import { makeFakeIbcBridge } from '@agoric/vats/tools/fake-bridge.js';
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import { makeNodeBundleCache } from '@endo/bundle-source/cache.js';
// import { E } from '@endo/eventual-send';
import { V as E } from '@agoric/vow/vat.js';
import { makeBridge } from './ibcBridgeMock.js';
import type { TestFn } from 'ava';
import { createRequire } from 'module';

import type { Baggage } from '@agoric/swingset-liveslots';
import { M, makeScalarBigMapStore } from '@agoric/vat-data';
import { makeDurableZone } from '@agoric/zone/durable.js';
import { makeSwingsetTestKit } from '../../tools/supports.ts';

const { entries, assign } = Object;
Expand All @@ -22,7 +24,13 @@ const asset = {

export const makeTestContext = async t => {
console.time('DefaultTestContext');
const { bridgeHandler, events } = makeBridge(t);

const baggage = makeScalarBigMapStore('baggage', {
keyShape: M.string(),
durable: true,
}) as Baggage;
const zone = makeDurableZone(baggage);

const bundleDir = 'bundles/vaults';
const bundleCache = await makeNodeBundleCache(
bundleDir,
Expand All @@ -31,9 +39,6 @@ export const makeTestContext = async t => {
);
const swingsetTestKit = await makeSwingsetTestKit(t.log, bundleDir, {
configSpecifier: PLATFORM_CONFIG,
bridgeHandlers: {
[BridgeId.DIBC]: obj => bridgeHandler.toBridge(obj),
},
});
console.timeLog('DefaultTestContext', 'swingsetTestKit');

Expand Down
2 changes: 1 addition & 1 deletion packages/boot/test/bootstrapTests/vats-restart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ test.serial('make IBC callbacks before upgrade', async t => {
const vatStore = await EV.vat('bootstrap').consumeItem('vatStore');
const { root: ibc } = await EV(vatStore).get('ibc');
t.log('E(ibc).makeCallbacks(m1)');
const dummyBridgeManager = null as unknown as ScopedBridgeManager;
const dummyBridgeManager = null as unknown as ScopedBridgeManager<any>;
const callbacks = await EV(ibc).makeCallbacks(dummyBridgeManager);
t.truthy(callbacks);
t.context.shared.ibcCallbacks = callbacks;
Expand Down
5 changes: 0 additions & 5 deletions packages/boot/tools/supports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ export const matchIter = (t: AvaT, iter, valueRef) => {
* @param [options.profileVats]
* @param [options.debugVats]
* @param [options.defaultManagerType]
* @param [options.bridgeHandlers]
*/
export const makeSwingsetTestKit = async (
log: (..._: any[]) => void,
Expand All @@ -262,7 +261,6 @@ export const makeSwingsetTestKit = async (
profileVats = [] as string[],
debugVats = [] as string[],
defaultManagerType = 'local' as ManagerType,
bridgeHandlers = {} as Record<string, (obj: any) => unknown>,
} = {},
) => {
console.time('makeBaseSwingsetTestKit');
Expand Down Expand Up @@ -305,9 +303,6 @@ export const makeSwingsetTestKit = async (
}
outboundMessages.get(bridgeId).push(obj);

if (bridgeId in bridgeHandlers) {
return bridgeHandlers[bridgeId](obj);
}
switch (bridgeId) {
case BridgeId.BANK: {
trace(
Expand Down
5 changes: 5 additions & 0 deletions packages/inter-protocol/test/smartWallet/contexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ export const makeDefaultTestContext = async (t, makeSpace) => {
'anyAddress',
);
const bridgeManager = await consume.bridgeManager;
/**
* @type {| undefined
* | import('@agoric/vats').ScopedBridgeManager<'wallet'>}
*/
// @ts-expect-error XXX EProxy
const walletBridgeManager = await (bridgeManager &&
E(bridgeManager).register(BridgeId.WALLET));
const walletFactory = await E(zoe).startInstance(
Expand Down
5 changes: 3 additions & 2 deletions packages/internal/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/**
* Event source ids used by the bridge device.
*/
export const BridgeId = {
export const BridgeId = /** @type {const} */ ({
BANK: 'bank',
CORE: 'core',
DIBC: 'dibc',
Expand All @@ -24,8 +24,9 @@ export const BridgeId = {
VLOCALCHAIN: 'vlocalchain',
VTRANSFER: 'vtransfer',
WALLET: 'wallet',
};
});
harden(BridgeId);
/** @typedef {(typeof BridgeId)[keyof typeof BridgeId]} BridgeIdValue */

export const CosmosInitKeyToBridgeId = {
vbankPort: BridgeId.BANK,
Expand Down
6 changes: 3 additions & 3 deletions packages/orchestration/test/examples/swapExample.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { makeHeapZone } from '@agoric/zone';
import { prepareLocalChainTools } from '@agoric/vats/src/localchain.js';
import { buildRootObject as buildBankVatRoot } from '@agoric/vats/src/vat-bank.js';
import { withAmountUtils } from '@agoric/zoe/tools/test-utils.js';
import { makeBridge } from '../supports.js';
import { makeFakeLocalchainBridge } from '../supports.js';

const dirname = path.dirname(new URL(import.meta.url).pathname);

Expand All @@ -34,11 +34,11 @@ test('start', async t => {

await E(bankManager).addAsset('uist', 'IST', 'Inter Stable Token', issuerKit);

const fakeBridgeKit = makeBridge(t);
const localchainBridge = makeFakeLocalchainBridge(zone);

const localchain = makeLocalChain({
bankManager,
system: fakeBridgeKit.bridgeHandler,
system: localchainBridge,
});

const storage = makeFakeStorageKit('mockChainStorageRoot', {
Expand Down
6 changes: 3 additions & 3 deletions packages/orchestration/test/examples/unbondExample.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { withAmountUtils } from '@agoric/zoe/tools/test-utils.js';
import { makeHeapZone } from '@agoric/zone';
import { E } from '@endo/far';
import path from 'path';
import { makeBridge } from '../supports.js';
import { makeFakeLocalchainBridge } from '../supports.js';

const dirname = path.dirname(new URL(import.meta.url).pathname);

Expand All @@ -34,11 +34,11 @@ test('start', async t => {

await E(bankManager).addAsset('uist', 'IST', 'Inter Stable Token', issuerKit);

const fakeBridgeKit = makeBridge(t);
const localchainBridge = makeFakeLocalchainBridge(zone);

const localchain = makeLocalChain({
bankManager,
system: fakeBridgeKit.bridgeHandler,
system: localchainBridge,
});

const storage = makeFakeStorageKit('mockChainStorageRoot', {
Expand Down
67 changes: 1 addition & 66 deletions packages/orchestration/test/supports.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1 @@
import { Fail } from '@agoric/assert';
import type { ScopedBridgeManager } from '@agoric/vats';

import {
makePinnedHistoryTopic,
prepareDurablePublishKit,
subscribeEach,
} from '@agoric/notifier';
import type { Baggage } from '@agoric/swingset-liveslots';
import { M, makeScalarBigMapStore } from '@agoric/vat-data';
import { makeDurableZone } from '@agoric/zone/durable.js';
import type { ExecutionContext } from 'ava';

// TODO DRY with ibcBridgeMock.js in package boot
export const makeBridge = (
t: ExecutionContext,
baggage = makeScalarBigMapStore('baggage', {
keyShape: M.string(),
durable: true,
}) as Baggage,
zone = makeDurableZone(baggage),
) => {
const makeDurablePublishKit = prepareDurablePublishKit(
baggage,
'DurablePublishKit',
);

const { subscriber, publisher } = makeDurablePublishKit();

const pinnedHistoryTopic = makePinnedHistoryTopic(subscriber);
const events = subscribeEach(pinnedHistoryTopic)[Symbol.asyncIterator]();

let hndlr;

const bridgeHandler: ScopedBridgeManager = zone.exo(
'IBC Bridge Manager',
undefined,
{
toBridge: async obj => {
const { method, type, ...params } = obj;
publisher.publish([method, params]);
console.info('toBridge', type, method, params);
switch (type) {
case 'VLOCALCHAIN_ALLOCATE_ADDRESS':
return 'agoric1fixme';
default:
Fail`unknown type ${type}`;
}
return undefined;
},
fromBridge: async obj => {
console.info('fromBridge', obj);
},
initHandler: h => {
if (hndlr) throw Error('already init');
hndlr = h;
},
setHandler: h => {
if (!hndlr) throw Error('must init first');
hndlr = h;
},
},
);

return { bridgeHandler, events };
};
export { makeFakeLocalchainBridge } from '@agoric/vats/tools/fake-bridge.js';
2 changes: 1 addition & 1 deletion packages/smart-wallet/src/walletFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const makeAssetRegistry = assetPublisher => {
* @param {ZCF<SmartWalletContractTerms>} zcf
* @param {{
* storageNode: ERef<StorageNode>,
* walletBridgeManager?: ERef<import('@agoric/vats').ScopedBridgeManager>,
* walletBridgeManager?: ERef<import('@agoric/vats').ScopedBridgeManager<'wallet'>>,
* walletReviver?: ERef<WalletReviver>,
* }} privateArgs
* @param {import('@agoric/vat-data').Baggage} baggage
Expand Down
2 changes: 2 additions & 0 deletions packages/smart-wallet/test/contexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const makeDefaultTestContext = async (t, makeSpace) => {
'anyAddress',
);
const bridgeManager = await consume.bridgeManager;
/** @type {import('@agoric/vats').ScopedBridgeManager<'wallet'>} */
// @ts-expect-error XXX generics through EProxy
const walletBridgeManager = await (bridgeManager &&
E(bridgeManager).register(BridgeId.WALLET));
const walletFactory = await E(zoe).startInstance(
Expand Down
4 changes: 4 additions & 0 deletions packages/smart-wallet/test/supports.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,13 @@ export const subscriptionKey = subscription => {

/** @returns {import('@agoric/vats').BridgeManager} */
const makeFakeBridgeManager = () =>
// @ts-expect-error XXX generics puzzle: could be instantiated with a different subtype of constraint
Far('fakeBridgeManager', {
register(bridgeId, handler) {
return Far('scopedBridgeManager', {
getBridgeId() {
return bridgeId;
},
fromBridge(_obj) {
assert.fail(`expected fromBridge`);
},
Expand Down
2 changes: 2 additions & 0 deletions packages/vats/src/core/basic-behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,8 @@ export const addBankAssets = async ({
const assetAdmin = E(agoricNamesAdmin).lookupAdmin('vbankAsset');

const bridgeManager = await bridgeManagerP;
/** @type {import('../types.js').ScopedBridgeManager<'bank'> | undefined} */
// @ts-expect-error XXX EProxy
const bankBridgeManager =
bridgeManager && E(bridgeManager).register(BridgeId.BANK);
const bankMgr = await E(E(loadCriticalVat)('bank')).makeBankManager(
Expand Down
2 changes: 2 additions & 0 deletions packages/vats/src/core/chain-behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ export const makeChainStorage = async ({
return;
}

/** @type {import('../types.js').ScopedBridgeManager<'storage'>} */
// @ts-expect-error XXX EProxy
const storageBridgeManager = E(bridgeManager).register(BRIDGE_ID.STORAGE);
storageBridgeManagerP.resolve(storageBridgeManager);

Expand Down
4 changes: 2 additions & 2 deletions packages/vats/src/core/startWalletFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ const publishRevivableWalletState = async (
* >['creatorFacet'];
* adminFacet: AdminFacet;
* };
* walletBridgeManager: import('../types.js').ScopedBridgeManager;
* provisionWalletBridgeManager: import('../types.js').ScopedBridgeManager;
* walletBridgeManager: import('../types.js').ScopedBridgeManager<'wallet'>;
* provisionWalletBridgeManager: import('../types.js').ScopedBridgeManager<'provisionWallet'>;
* }>} powers
* @param {{
* options?: {
Expand Down
4 changes: 3 additions & 1 deletion packages/vats/src/core/types-ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,9 @@ type ChainBootstrapSpaceT = {
provisionWalletBridgeManager:
| import('../types.js').ScopedBridgeManager
| undefined;
storageBridgeManager: import('../types.js').ScopedBridgeManager | undefined;
storageBridgeManager:
| import('../types.js').ScopedBridgeManager<'storage'>
| undefined;
/**
* Convienence function for starting a contract (ungoverned) and saving its
* facets (including adminFacet)
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/src/ibc.js
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ export const prepareCallbacks = zone => {
return zone.exoClass(
'callbacks',
undefined,
/** @param {ScopedBridgeManager} dibcBridgeManager */
/** @param {ScopedBridgeManager<'dibc'>} dibcBridgeManager */
dibcBridgeManager => ({ dibcBridgeManager }),
{
/**
Expand Down
4 changes: 2 additions & 2 deletions packages/vats/src/localchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ const { Fail } = assert;

/**
* @typedef {{
* system: ScopedBridgeManager;
* system: ScopedBridgeManager<'vlocalchain'>;
* bank: Bank;
* }} AccountPowers
*/

/**
* @typedef {{
* system: ScopedBridgeManager;
* system: ScopedBridgeManager<'vlocalchain'>;
* bankManager: BankManager;
* }} LocalChainPowers
*/
Expand Down
Loading

0 comments on commit 90906b1

Please sign in to comment.