diff --git a/packages/cosmic-swingset/src/computron-counter.js b/packages/cosmic-swingset/src/computron-counter.js new file mode 100644 index 00000000000..4db024ca7a0 --- /dev/null +++ b/packages/cosmic-swingset/src/computron-counter.js @@ -0,0 +1,115 @@ +// @ts-check + +import { assert } from '@endo/errors'; +import { + BeansPerBlockComputeLimit, + BeansPerVatCreation, + BeansPerXsnapComputron, +} from './sim-params.js'; + +const { hasOwn } = Object; + +/** + * @typedef {object} BeansPerUnit + * @property {bigint} blockComputeLimit + * @property {bigint} vatCreation + * @property {bigint} xsnapComputron + */ + +/** + * Return a stateful run policy that supports two phases: first allow + * non-cleanup work (presumably deliveries) until an overrideable computron + * budget is exhausted, then (iff no work was done and at least one vat cleanup + * budget field is positive) a cleanup phase that allows cleanup work (and + * presumably nothing else) until one of those fields is exhausted. + * https://github.com/Agoric/agoric-sdk/issues/8928#issuecomment-2053357870 + * + * @param {object} params + * @param {BeansPerUnit} params.beansPerUnit + * @param {import('@agoric/swingset-vat').CleanupBudget} [params.vatCleanupBudget] + * @param {boolean} [ignoreBlockLimit] + * @returns {import('./launch-chain.js').ChainRunPolicy} + */ +export function computronCounter( + { beansPerUnit, vatCleanupBudget }, + ignoreBlockLimit = false, +) { + const { + [BeansPerBlockComputeLimit]: blockComputeLimit, + [BeansPerVatCreation]: vatCreation, + [BeansPerXsnapComputron]: xsnapComputron, + } = beansPerUnit; + assert.typeof(blockComputeLimit, 'bigint'); + assert.typeof(vatCreation, 'bigint'); + assert.typeof(xsnapComputron, 'bigint'); + + let totalBeans = 0n; + const shouldRun = () => ignoreBlockLimit || totalBeans < blockComputeLimit; + + const remainingCleanups = { default: Infinity, ...vatCleanupBudget }; + const defaultCleanupBudget = remainingCleanups.default; + let cleanupStarted = false; + let cleanupDone = false; + const cleanupPossible = + Object.values(remainingCleanups).length > 0 + ? Object.values(remainingCleanups).some(n => n > 0) + : defaultCleanupBudget > 0; + if (!cleanupPossible) cleanupDone = true; + /** @type {() => (false | import('@agoric/swingset-vat').CleanupBudget)} */ + const allowCleanup = () => + cleanupStarted && !cleanupDone && { ...remainingCleanups }; + const startCleanup = () => { + assert(!cleanupStarted); + cleanupStarted = true; + return totalBeans === 0n && !cleanupDone; + }; + const didCleanup = details => { + for (const [phase, count] of Object.entries(details.cleanups)) { + if (phase === 'total') continue; + if (!hasOwn(remainingCleanups, phase)) { + // TODO: log unknown phases? + remainingCleanups[phase] = defaultCleanupBudget; + } + remainingCleanups[phase] -= count; + if (remainingCleanups[phase] <= 0) cleanupDone = true; + } + // We return true to allow processing of any BOYD/GC prompted by cleanup, + // even if cleanup as such is now done. + return true; + }; + + const policy = harden({ + vatCreated() { + totalBeans += vatCreation; + return shouldRun(); + }, + crankComplete(details = {}) { + assert.typeof(details, 'object'); + if (details.computrons) { + assert.typeof(details.computrons, 'bigint'); + + // TODO: xsnapComputron should not be assumed here. + // Instead, SwingSet should describe the computron model it uses. + totalBeans += details.computrons * xsnapComputron; + } + return shouldRun(); + }, + crankFailed() { + const failedComputrons = 1000000n; // who knows, 1M is as good as anything + totalBeans += failedComputrons * xsnapComputron; + return shouldRun(); + }, + emptyCrank() { + return shouldRun(); + }, + allowCleanup, + didCleanup, + + shouldRun, + remainingBeans: () => + ignoreBlockLimit ? undefined : blockComputeLimit - totalBeans, + totalBeans: () => totalBeans, + startCleanup, + }); + return policy; +} diff --git a/packages/cosmic-swingset/src/launch-chain.js b/packages/cosmic-swingset/src/launch-chain.js index 9500a380a12..a483fecdb20 100644 --- a/packages/cosmic-swingset/src/launch-chain.js +++ b/packages/cosmic-swingset/src/launch-chain.js @@ -43,17 +43,11 @@ import { makeSlogCallbacks, } from './kernel-stats.js'; -import { - BeansPerBlockComputeLimit, - BeansPerVatCreation, - BeansPerXsnapComputron, -} from './sim-params.js'; import { parseParams } from './params.js'; import { makeQueue, makeQueueStorageMock } from './helpers/make-queue.js'; import { exportStorage } from './export-storage.js'; import { parseLocatedJson } from './helpers/json.js'; - -const { hasOwn } = Object; +import { computronCounter } from './computron-counter.js'; /** @import { BlockInfo } from '@agoric/internal/src/chain-utils.js' */ /** @import { Mailbox, RunPolicy, SwingSetConfig } from '@agoric/swingset-vat' */ @@ -282,111 +276,6 @@ export async function buildSwingset( * }} ChainRunPolicy */ -/** - * @typedef {object} BeansPerUnit - * @property {bigint} blockComputeLimit - * @property {bigint} vatCreation - * @property {bigint} xsnapComputron - */ - -/** - * Return a stateful run policy that supports two phases: first allow - * non-cleanup work (presumably deliveries) until an overrideable computron - * budget is exhausted, then (iff no work was done and at least one vat cleanup - * budget field is positive) a cleanup phase that allows cleanup work (and - * presumably nothing else) until one of those fields is exhausted. - * https://github.com/Agoric/agoric-sdk/issues/8928#issuecomment-2053357870 - * - * @param {object} params - * @param {BeansPerUnit} params.beansPerUnit - * @param {import('@agoric/swingset-vat').CleanupBudget} [params.vatCleanupBudget] - * @param {boolean} [ignoreBlockLimit] - * @returns {ChainRunPolicy} - */ -function computronCounter( - { beansPerUnit, vatCleanupBudget }, - ignoreBlockLimit = false, -) { - const { - [BeansPerBlockComputeLimit]: blockComputeLimit, - [BeansPerVatCreation]: vatCreation, - [BeansPerXsnapComputron]: xsnapComputron, - } = beansPerUnit; - assert.typeof(blockComputeLimit, 'bigint'); - assert.typeof(vatCreation, 'bigint'); - assert.typeof(xsnapComputron, 'bigint'); - - let totalBeans = 0n; - const shouldRun = () => ignoreBlockLimit || totalBeans < blockComputeLimit; - - const remainingCleanups = { default: Infinity, ...vatCleanupBudget }; - const defaultCleanupBudget = remainingCleanups.default; - let cleanupStarted = false; - let cleanupDone = false; - const cleanupPossible = - Object.values(remainingCleanups).length > 0 - ? Object.values(remainingCleanups).some(n => n > 0) - : defaultCleanupBudget > 0; - if (!cleanupPossible) cleanupDone = true; - /** @type {() => (false | import('@agoric/swingset-vat').CleanupBudget)} */ - const allowCleanup = () => - cleanupStarted && !cleanupDone && { ...remainingCleanups }; - const startCleanup = () => { - assert(!cleanupStarted); - cleanupStarted = true; - return totalBeans === 0n && !cleanupDone; - }; - const didCleanup = details => { - for (const [phase, count] of Object.entries(details.cleanups)) { - if (phase === 'total') continue; - if (!hasOwn(remainingCleanups, phase)) { - // TODO: log unknown phases? - remainingCleanups[phase] = defaultCleanupBudget; - } - remainingCleanups[phase] -= count; - if (remainingCleanups[phase] <= 0) cleanupDone = true; - } - // We return true to allow processing of any BOYD/GC prompted by cleanup, - // even if cleanup as such is now done. - return true; - }; - - const policy = harden({ - vatCreated() { - totalBeans += vatCreation; - return shouldRun(); - }, - crankComplete(details = {}) { - assert.typeof(details, 'object'); - if (details.computrons) { - assert.typeof(details.computrons, 'bigint'); - - // TODO: xsnapComputron should not be assumed here. - // Instead, SwingSet should describe the computron model it uses. - totalBeans += details.computrons * xsnapComputron; - } - return shouldRun(); - }, - crankFailed() { - const failedComputrons = 1000000n; // who knows, 1M is as good as anything - totalBeans += failedComputrons * xsnapComputron; - return shouldRun(); - }, - emptyCrank() { - return shouldRun(); - }, - allowCleanup, - didCleanup, - - shouldRun, - remainingBeans: () => - ignoreBlockLimit ? undefined : blockComputeLimit - totalBeans, - totalBeans: () => totalBeans, - startCleanup, - }); - return policy; -} - /** * @template [T=unknown] * @typedef {object} LaunchOptions