Skip to content

Commit

Permalink
refactor(daemon): Synchronize host makeUnconfined() (merge #2124)
Browse files Browse the repository at this point in the history
Progresses: #2086

Synchronizes the host's `makeUnconfined()` per #2086. Refactoring `daemon.js` in support of this goal fixed one bug while revealing another.

In particular, #2074 is progressed by enabling indirect cancellation of caplets via their workers. The issue is not resolved since indirect cancellation of caplets via their caplet dependencies still does not work as intended. A new, failing regression test has been added for this specific case.

The revealed bug is #2021, which we believed to be fixed by #2092. Rather than fixing the bug, that PR concealed it by always creating a new incarnation of `eval` formula workers, even if they already existed. The regression test for #2021 has been marked as failing, and we will have to find a different solution for it.
  • Loading branch information
rekmarks authored Mar 9, 2024
2 parents eb900a7 + a239f10 commit 96e54b9
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 59 deletions.
164 changes: 120 additions & 44 deletions packages/daemon/src/daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -1007,41 +1007,38 @@ const makeDaemonCore = async (
);
};

/** @type {import('./types.js').DaemonCore['incarnateGuest']} */
const incarnateGuest = async (hostFormulaIdentifier, deferredTasks) => {
/**
* Helper for callers of `incarnateNumberedGuest`.
* @param {string} hostFormulaIdentifier - The formula identifier of the host.
*/
const incarnateGuestDependencies = async hostFormulaIdentifier =>
harden({
guestFormulaNumber: await randomHex512(),
hostHandleFormulaIdentifier: (
await incarnateNumberedHandle(
await randomHex512(),
hostFormulaIdentifier,
)
).formulaIdentifier,
storeFormulaIdentifier: (
await incarnateNumberedPetStore(await randomHex512())
).formulaIdentifier,
workerFormulaIdentifier: (
await incarnateNumberedWorker(await randomHex512())
).formulaIdentifier,
});

/**
*
* @param {ReturnType<any>} identifiers
*/
const incarnateNumberedGuest = identifiers => {
const {
guestFormulaNumber,
hostHandleFormulaIdentifier,
storeFormulaIdentifier,
workerFormulaIdentifier,
} = await formulaGraphMutex.enqueue(async () => {
const formulaNumber = await randomHex512();
const hostHandle = await incarnateNumberedHandle(
await randomHex512(),
hostFormulaIdentifier,
);
const storeIncarnation = await incarnateNumberedPetStore(
await randomHex512(),
);
const workerIncarnation = await incarnateNumberedWorker(
await randomHex512(),
);

await deferredTasks.execute({
guestFormulaIdentifier: serializeFormulaIdentifier({
type: 'guest',
number: formulaNumber,
node: ownNodeIdentifier,
}),
});

return harden({
guestFormulaNumber: formulaNumber,
hostHandleFormulaIdentifier: hostHandle.formulaIdentifier,
storeFormulaIdentifier: storeIncarnation.formulaIdentifier,
workerFormulaIdentifier: workerIncarnation.formulaIdentifier,
});
});
} = identifiers;

/** @type {import('./types.js').GuestFormula} */
const formula = {
Expand All @@ -1055,6 +1052,44 @@ const makeDaemonCore = async (
);
};

/** @type {import('./types.js').DaemonCore['incarnateGuest']} */
const incarnateGuest = async (hostFormulaIdentifier, deferredTasks) => {
return incarnateNumberedGuest(
await formulaGraphMutex.enqueue(async () => {
const identifiers = await incarnateGuestDependencies(
hostFormulaIdentifier,
);

await deferredTasks.execute({
guestFormulaIdentifier: serializeFormulaIdentifier({
type: 'guest',
number: identifiers.guestFormulaNumber,
node: ownNodeIdentifier,
}),
});

return identifiers;
}),
);
};

/**
* @param {string} [specifiedWorkerFormulaIdentifier]
*/
const provideWorkerFormulaIdentifier =
async specifiedWorkerFormulaIdentifier => {
await null;
if (typeof specifiedWorkerFormulaIdentifier === 'string') {
return specifiedWorkerFormulaIdentifier;
}

const workerFormulaNumber = await randomHex512();
const workerIncarnation = await incarnateNumberedWorker(
workerFormulaNumber,
);
return workerIncarnation.formulaIdentifier;
};

/** @type {import('./types.js').DaemonCore['incarnateEval']} */
const incarnateEval = async (
nameHubFormulaIdentifier,
Expand All @@ -1067,22 +1102,19 @@ const makeDaemonCore = async (
const {
workerFormulaIdentifier,
endowmentFormulaIdentifiers,
evalFormulaIdentifier,
evalFormulaNumber,
} = await formulaGraphMutex.enqueue(async () => {
const ownFormulaNumber = await randomHex512();
const ownFormulaIdentifier = serializeFormulaIdentifier({
type: 'eval',
number: ownFormulaNumber,
node: ownNodeIdentifier,
});
const workerFormulaNumber = await (specifiedWorkerFormulaIdentifier
? parseFormulaIdentifier(specifiedWorkerFormulaIdentifier).number
: randomHex512());

const identifiers = harden({
workerFormulaIdentifier: (
await incarnateNumberedWorker(workerFormulaNumber)
).formulaIdentifier,
workerFormulaIdentifier: await provideWorkerFormulaIdentifier(
specifiedWorkerFormulaIdentifier,
),
endowmentFormulaIdentifiers: await Promise.all(
endowmentFormulaIdsOrPaths.map(async formulaIdOrPath => {
if (typeof formulaIdOrPath === 'string') {
Expand All @@ -1102,15 +1134,13 @@ const makeDaemonCore = async (
}),
),
evalFormulaIdentifier: ownFormulaIdentifier,
evalFormulaNumber: ownFormulaNumber,
});

await deferredTasks.execute(identifiers);
return identifiers;
});

const { number: evalFormulaNumber } = parseFormulaIdentifier(
evalFormulaIdentifier,
);
/** @type {import('./types.js').EvalFormula} */
const formula = {
type: 'eval',
Expand Down Expand Up @@ -1151,18 +1181,64 @@ const makeDaemonCore = async (
);
};

/**
* @param {string} hostFormulaIdentifier
* @param {string} [specifiedPowersFormulaIdentifier]
*/
const providePowersFormulaIdentifier = async (
hostFormulaIdentifier,
specifiedPowersFormulaIdentifier,
) => {
await null;
if (typeof specifiedPowersFormulaIdentifier === 'string') {
return specifiedPowersFormulaIdentifier;
}

const guestIncarnationData = await incarnateGuestDependencies(
hostFormulaIdentifier,
);
const guestIncarnation = await incarnateNumberedGuest(guestIncarnationData);
return guestIncarnation.formulaIdentifier;
};

/** @type {import('./types.js').DaemonCore['incarnateUnconfined']} */
const incarnateUnconfined = async (
workerFormulaIdentifier,
powersFormulaIdentifiers,
hostFormulaIdentifier,
specifier,
deferredTasks,
specifiedWorkerFormulaIdentifier,
specifiedPowersFormulaIdentifier,
) => {
const formulaNumber = await randomHex512();
const {
powersFormulaIdentifier,
unconfinedFormulaNumber: formulaNumber,
workerFormulaIdentifier,
} = await formulaGraphMutex.enqueue(async () => {
const ownFormulaNumber = await randomHex512();
const identifiers = harden({
powersFormulaIdentifier: await providePowersFormulaIdentifier(
hostFormulaIdentifier,
specifiedPowersFormulaIdentifier,
),
unconfinedFormulaIdentifier: serializeFormulaIdentifier({
type: 'make-unconfined',
number: ownFormulaNumber,
node: ownNodeIdentifier,
}),
unconfinedFormulaNumber: ownFormulaNumber,
workerFormulaIdentifier: await provideWorkerFormulaIdentifier(
specifiedWorkerFormulaIdentifier,
),
});
await deferredTasks.execute(identifiers);
return identifiers;
});

/** @type {import('./types.js').MakeUnconfinedFormula} */
const formula = {
type: 'make-unconfined',
worker: workerFormulaIdentifier,
powers: powersFormulaIdentifiers,
powers: powersFormulaIdentifier,
specifier,
};
return /** @type {import('./types.js').IncarnateResult<unknown>} */ (
Expand Down
47 changes: 39 additions & 8 deletions packages/daemon/src/host.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import { parseFormulaIdentifier } from './formula-identifier.js';

const { quote: q } = assert;

/** @param {string} name */
const assertPowersName = name => {
['NONE', 'SELF', 'ENDO'].includes(name) || assertPetName(name);
};

/**
* @param {object} args
* @param {import('./types.js').DaemonCore['provideValueForFormulaIdentifier']} args.provideValueForFormulaIdentifier
Expand Down Expand Up @@ -259,7 +264,6 @@ export const makeHostMaker = ({
deferTask(identifiers =>
petStore.write(workerName, identifiers.workerFormulaIdentifier),
);
return undefined;
}
return workerFormulaIdentifier;
};
Expand All @@ -283,6 +287,21 @@ export const makeHostMaker = ({
return guestFormulaIdentifier;
};

/**
* @param {string | 'NONE' | 'SELF' | 'ENDO'} partyName
* @param {import('./types.js').DeferredTasks<{ powersFormulaIdentifier: string }>['push']} deferTask
* @returns {string | undefined}
*/
const providePowersFormulaIdentifierSync = (partyName, deferTask) => {
const powersFormulaIdentifier = petStore.identifyLocal(partyName);
if (powersFormulaIdentifier === undefined) {
deferTask(identifiers =>
petStore.write(partyName, identifiers.powersFormulaIdentifier),
);
}
return powersFormulaIdentifier;
};

/**
* @param {string | 'MAIN' | 'NEW'} workerName
* @param {string} source
Expand Down Expand Up @@ -356,24 +375,36 @@ export const makeHostMaker = ({
powersName,
resultName,
) => {
const workerFormulaIdentifier = await provideWorkerFormulaIdentifier(
assertPowersName(powersName);

/** @type {import('./types.js').DeferredTasks<import('./types.js').MakeUnconfinedDeferredTaskParams>} */
const tasks = makeDeferredTasks();

const workerFormulaIdentifier = provideWorkerFormulaIdentifierSync(
workerName,
tasks.push,
);

const powersFormulaIdentifier = await providePowersFormulaIdentifier(
const powersFormulaIdentifier = providePowersFormulaIdentifierSync(
powersName,
tasks.push,
);

if (resultName !== undefined) {
tasks.push(identifiers =>
petStore.write(resultName, identifiers.unconfinedFormulaIdentifier),
);
}

// Behold, recursion:
// eslint-disable-next-line no-use-before-define
const { formulaIdentifier, value } = await incarnateUnconfined(
const { value } = await incarnateUnconfined(
hostFormulaIdentifier,
specifier,
tasks,
workerFormulaIdentifier,
powersFormulaIdentifier,
specifier,
);
if (resultName !== undefined) {
await petStore.write(resultName, formulaIdentifier);
}
return value;
};

Expand Down
12 changes: 10 additions & 2 deletions packages/daemon/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ type MakeUnconfinedFormula = {
// TODO formula slots
};

export type MakeUnconfinedDeferredTaskParams = {
powersFormulaIdentifier: string;
unconfinedFormulaIdentifier: string;
workerFormulaIdentifier: string;
};

type MakeBundleFormula = {
type: 'make-bundle';
worker: string;
Expand Down Expand Up @@ -771,9 +777,11 @@ export interface DaemonCore {
specifiedWorkerFormulaIdentifier?: string,
) => IncarnateResult<unknown>;
incarnateUnconfined: (
workerFormulaIdentifier: string,
powersFormulaIdentifier: string,
hostFormulaIdentifier: string,
specifier: string,
deferredTasks: DeferredTasks<MakeUnconfinedDeferredTaskParams>,
specifiedWorkerFormulaIdentifier?: string,
specifiedPowersFormulaIdentifier?: string,
) => IncarnateResult<unknown>;
incarnateBundler: (
powersFormulaIdentifier: string,
Expand Down
15 changes: 15 additions & 0 deletions packages/daemon/test/doubler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { E, Far } from '@endo/far';

export const make = powers => {
const counter = E(powers).request(
'HOST',
'a counter, suitable for doubling',
'my-counter',
);
return Far('Doubler', {
async incr() {
const n = await E(counter).incr();
return n * 2;
},
});
};
Loading

0 comments on commit 96e54b9

Please sign in to comment.