Skip to content

Commit

Permalink
feat(vats): Cosmos chain core bootstrap with ag-solo client
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Jan 26, 2022
1 parent 877f30a commit a4ab506
Show file tree
Hide file tree
Showing 10 changed files with 307 additions and 122 deletions.
50 changes: 9 additions & 41 deletions packages/vats/src/core/basic-behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { E, Far } from '@agoric/far';
import { makeIssuerKit } from '@agoric/ertp';

import { makeStore } from '@agoric/store';

import { makeNameHubKit } from '../nameHub.js';
import { BLD_ISSUER_ENTRY } from '../issuers.js';

import { feeIssuerConfig, collectNameAdmins, shared } from './utils';
import { feeIssuerConfig, collectNameAdmins, shared } from './utils.js';

const { keys } = Object;

Expand All @@ -17,7 +18,7 @@ const { keys } = Object;

/**
* @param {{
* vatPowers: { D: EProxy }, // D type is approximate
* vatPowers: { D: DProxy }, // D type is approximate
* vats: { vattp: VattpVat },
* devices: { mailbox: MailboxDevice },
* }} powers
Expand Down Expand Up @@ -68,7 +69,7 @@ harden(makeVatsFromBundles);
* vatAdminSvc: ERef<VatAdminSvc>,
* loadVat: ERef<VatLoader<ZoeVat>>,
* nameAdmins: ERef<Store<NameHub, NameAdmin>>,
* client: ERef<ClientConfig>
* client: ERef<ClientManager>
* },
* produce: { zoe: Producer<ZoeService>, feeMintAccess: Producer<FeeMintAccess> },
* }} powers
Expand Down Expand Up @@ -106,7 +107,7 @@ harden(buildZoe);
* TODO: rename this to getBoard?
*
* @param {{
* consume: { loadVat: ERef<VatLoader<BoardVat>>, client: ERef<ClientConfig> },
* consume: { loadVat: ERef<VatLoader<BoardVat>>, client: ERef<ClientManager> },
* produce: { board: Producer<ERef<Board>> },
* }} powers
* @typedef {ERef<ReturnType<import('../vat-board.js').buildRootObject>>} BoardVat
Expand All @@ -125,7 +126,7 @@ harden(makeBoard);

/**
* @param {{
* consume: { client: ERef<ClientConfig> },
* consume: { client: ERef<ClientManager> },
* produce: {
* agoricNames: Producer<NameHub>,
* agoricNamesAdmin: Producer<NameAdmin>,
Expand All @@ -138,8 +139,6 @@ export const makeAddressNameHubs = async ({ consume: { client }, produce }) => {
nameHub: agoricNames,
nameAdmin: agoricNamesAdmin,
} = makeNameHubKit();
produce.agoricNames.resolve(agoricNames);
produce.agoricNamesAdmin.resolve(agoricNamesAdmin);

const {
nameHub: namesByAddress,
Expand All @@ -166,6 +165,8 @@ export const makeAddressNameHubs = async ({ consume: { client }, produce }) => {
),
);
produce.nameAdmins.resolve(nameAdmins);
produce.agoricNames.resolve(agoricNames);
produce.agoricNamesAdmin.resolve(agoricNamesAdmin);

const perAddress = address => {
// Create a name hub for this address.
Expand Down Expand Up @@ -196,7 +197,7 @@ harden(makeAddressNameHubs);
* @param {{
* consume: {
* loadVat: ERef<VatLoader<BankVat>>,
* client: ERef<ClientConfig>,
* client: ERef<ClientManager>,
* bridgeManager: import('../bridge.js').BridgeManager,
* },
* produce: { bankManager: Producer<unknown> },
Expand Down Expand Up @@ -245,36 +246,3 @@ export const makeBLDKit = async ({
]);
};
harden(makeBLDKit);

/**
* @param {{
* devices: { timer: unknown },
* vats: { timer: TimerVat },
* produce: { chainTimerService: Producer<ERef<TimerService>> }
* }} powers
*/
export const startTimerService = async ({
devices: { timer: timerDevice },
vats: { timer: timerVat },
produce: { chainTimerService },
}) => {
chainTimerService.resolve(E(timerVat).createTimerService(timerDevice));
};
harden(startTimerService);

/**
* @param {{
* consume: {
* loadVat: ERef<VatLoader<ProvisioningVat>>,
* chainBundler: ERef<ChainBundler>,
* },
* vats: { comms: CommsVatRoot, vattp: VattpVat },
* }} powers
* @typedef {ERef<ReturnType<import('../vat-provisioning.js').buildRootObject>>} ProvisioningVat
*/
export const registerProvisioner = async ({
consume: { chainBundler, loadVat },
vats: { comms, vattp },
}) => {
await E(E(loadVat)('provisioning')).register(chainBundler, comms, vattp);
};
1 change: 1 addition & 0 deletions packages/vats/src/core/behaviors.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @ts-check
export * from './basic-behaviors.js';
export * from './chain-behaviors.js';
export * from './econ-behaviors.js';
export * from './sim-behaviors.js';
34 changes: 29 additions & 5 deletions packages/vats/src/core/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {

import * as behaviors from './behaviors.js';

const { entries, fromEntries } = Object;
const { entries, fromEntries, keys } = Object;
const { details: X, quote: q } = assert;

// Choose a manifest based on runtime configured argv.ROLE.
Expand All @@ -26,13 +26,15 @@ const roleToManifest = new Map([
const makePromiseSpace = () => {
/** @type {Map<string, PromiseRecord<unknown>>} */
const state = new Map();
const remaining = new Set();

const findOrCreateKit = name => {
let kit = state.get(name);
if (!kit) {
// console.info(name, ': new Promise');
console.info(`${name}: new Promise`);
kit = makePromiseKit();
state.set(name, kit);
remaining.add(name);
}
return kit;
};
Expand All @@ -53,8 +55,19 @@ const makePromiseSpace = () => {
{
get: (_target, name) => {
assert.typeof(name, 'string');
const { resolve } = findOrCreateKit(name);
// promise.then(() => console.info(name, ': resolve'));
const { resolve, promise } = findOrCreateKit(name);
// promise.then(
// () => console.info(name, ': resolve'),
// e => console.info(name, ': reject', e),
// );
promise.finally(() => {
remaining.delete(name);
console.info(
name,
'settled; remaining:',
[...remaining.keys()].sort(),
);
});
// Note: repeated resolves() are noops.
return harden({ resolve });
},
Expand All @@ -75,14 +88,25 @@ const extract = (template, specimen) => {
if (typeof specimen !== 'object' || specimen === null) {
assert.fail(X`object template requires object specimen, not ${specimen}`);
}
return harden(
const target = harden(
fromEntries(
entries(template).map(([propName, subTemplate]) => [
propName,
extract(subTemplate, specimen[propName]),
]),
),
);
return new Proxy(target, {
get: (t, propName) => {
if (typeof propName !== 'symbol') {
assert(
propName in t,
X`${propName} not permitted, only ${keys(template)}`,
);
}
return t[propName];
},
});
} else {
assert.fail(X`unexpected template: ${q(template)}`);
}
Expand Down
182 changes: 182 additions & 0 deletions packages/vats/src/core/chain-behaviors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { E, Far } from '@agoric/far';
import { deeplyFulfilled } from '@agoric/marshal';
import {
makeAsyncIterableFromNotifier,
makeNotifierKit,
makeSubscriptionKit,
observeIteration,
} from '@agoric/notifier';

import { makeBridgeManager as makeBridgeManagerKit } from '../bridge.js';

import { callProperties } from './utils.js';

const { details: X } = assert;

/**
* @param {{
* consume: {
* loadVat: ERef<VatLoader<ProvisioningVat>>,
* clientCreator: ERef<ClientCreator>,
* },
* produce: { provisioning: Producer<unknown> },
* vats: { comms: CommsVatRoot, vattp: VattpVat },
* }} powers
* @typedef {ERef<ReturnType<import('../vat-provisioning.js').buildRootObject>>} ProvisioningVat
*/
export const makeProvisioner = async ({
consume: { clientCreator, loadVat },
vats: { comms, vattp },
produce: { provisioning },
}) => {
const provisionerVat = E(loadVat)('provisioning');
await E(provisionerVat).register(clientCreator, comms, vattp);
provisioning.resolve(provisionerVat);
};
harden(makeProvisioner);

/**
* @param {{
* consume: { provisioning: ProvisioningVat, bridgeManager: ERef<OptionalBridgeManager> },
* }} powers
*/
export const bridgeProvisioner = async ({
consume: { provisioning, bridgeManager: bridgeManagerP },
}) => {
const bridgeManager = await bridgeManagerP;
if (!bridgeManager) {
return;
}

// Register a provisioning handler over the bridge.
const handler = Far('provisioningHandler', {
async fromBridge(_srcID, obj) {
switch (obj.type) {
case 'PLEASE_PROVISION': {
const { nickname, address, powerFlags } = obj;
return E(provisioning)
.pleaseProvision(nickname, address, powerFlags)
.catch(e =>
console.error(`Error provisioning ${nickname} ${address}:`, e),
);
}
default:
assert.fail(X`Unrecognized request ${obj.type}`);
}
},
});
await E(bridgeManager).register('provision', handler);
};
harden(bridgeProvisioner);

/**
*
* @param {{
* produce: { client: Producer<ClientManager>, clientCreator: Producer<ClientCreator> }
* }} param0
*/
export const makeClientManager = async ({
produce: { client, clientCreator: clientCreatorP },
}) => {
// Create a subscription of chain configurations.
const { subscription, publication } = makeSubscriptionKit();

// Cache the latest full property maker state.
let cachedPropertyMakers = {};

/** @type {ClientManager} */
const clientManager = Far('chainClientManager', {
assignBundle: newPropertyMakers => {
// Write the property makers to the cache, and update the subscription.
cachedPropertyMakers = { ...cachedPropertyMakers, ...newPropertyMakers };
publication.updateState(newPropertyMakers);
},
});

/** @type {ClientCreator} */
const clientCreator = Far('clientCreator', {
createUserBundle: (nickname, clientAddress, powerFlags) => {
const c = E(clientCreator).createClientFacet(
nickname,
clientAddress,
powerFlags,
);
return E(c).getChainBundle();
},
createClientFacet: async (_nickname, clientAddress, _powerFlags) => {
let clientHome = {};

const makeUpdatedConfiguration = (newPropertyMakers = {}) => {
// Specialize the property makers with the client address.
const newProperties = callProperties(newPropertyMakers, clientAddress);
clientHome = { ...clientHome, ...newProperties };
const config = harden({ clientAddress, clientHome });
return deeplyFulfilled(config);
};

// Publish new configurations.
const { notifier, updater } = makeNotifierKit(
makeUpdatedConfiguration(cachedPropertyMakers),
);
const it = makeAsyncIterableFromNotifier(notifier);

/** @type {ClientFacet} */
const clientFacet = Far('chainProvisioner', {
getChainBundle: () => clientHome,
getConfiguration: () => it,
});

observeIteration(subscription, {
updateState(newPropertyMakers) {
updater.updateState(makeUpdatedConfiguration(newPropertyMakers));
},
});

return clientFacet;
},
});

clientCreatorP.resolve(clientCreator);
client.resolve(clientManager);
};
harden(makeClientManager);

/**
* @param {{
* devices: { timer: unknown },
* vats: { timer: TimerVat },
* produce: { chainTimerService: Producer<ERef<TimerService>> }
* }} powers
*/
export const startTimerService = async ({
devices: { timer: timerDevice },
vats: { timer: timerVat },
produce: { chainTimerService },
}) => {
chainTimerService.resolve(E(timerVat).createTimerService(timerDevice));
};
harden(startTimerService);

/**
* TODO: Make a Powers type we pass everywhere, which has just a subset as permitted.
*
* @param {{
* devices: { bridge: Device<import('../bridge.js').BridgeDevice> },
* vatPowers: { D: DProxy },
* produce: { bridgeManager: Producer<OptionalBridgeManager> },
* }} powers
*/
export const makeBridgeManager = async ({
devices: { bridge },
vatPowers: { D },
produce: { bridgeManager },
}) => {
const myBridge = bridge ? makeBridgeManagerKit(E, D, bridge) : undefined;
if (!myBridge) {
console.warn(
'Running without a bridge device; this is not an actual chain.',
);
}
bridgeManager.resolve(myBridge);
};
harden(makeBridgeManager);
4 changes: 2 additions & 2 deletions packages/vats/src/core/econ-behaviors.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @ts-check

import { E } from '@agoric/far';
import { makeRatio } from '@agoric/zoe/src/contractSupport';
import { makeRatio } from '@agoric/zoe/src/contractSupport/index.js';

import { installOnChain as installVaultFactoryOnChain } from '@agoric/run-protocol/bundles/install-on-chain.js';
import { bootstrapAttestation } from '@agoric/zoe/src/contracts/attestation/bootstrapAttestation.js';
Expand Down Expand Up @@ -68,7 +68,7 @@ harden(startVaultFactory);
* consume: {
* agoricNames: ERef<NameHub>,
* bridgeManager: ERef<import('../bridge.js').BridgeManager>,
* client: ERef<ClientConfig>,
* client: ERef<ClientManager>,
* nameAdmins: ERef<Store<NameHub, NameAdmin>>,
* zoe: ERef<ZoeService>,
* }
Expand Down
Loading

0 comments on commit a4ab506

Please sign in to comment.