@metamask/ocap-kernel
Version:
OCap kernel core components
213 lines • 9.74 kB
JavaScript
/*
* Organization of keys in the key value store:
*
* Definitions
* NN ::= some decimal integer
* CAPDATA ::= capdata encoded structure value
* JSON(${xx}) ::= JSON encoding of ${xx}
*
* ${koid} ::= ko${NN} // kernel object ID
* ${kpid} ::= kp${NN} // kernel promise ID
* ${kref} ::= ${koid} | ${kpid} // kernel reference
* ${dir} ::= + | - // direction (for remote and vat references)
* ${roid} ::= ro${dir}${NN} // remote object ID
* ${rpid} ::= rp${dir}${NN} // remote promise ID
* ${rref} ::= ${roid} | ${rpid} // remote reference
* ${void} ::= o${dir}${NN} // vat object ID
* ${vpid} ::= p${dir}${NN} // vat promise ID
* ${vref} ::= ${void} | ${vpid} // vat reference
* ${eref} ::= ${vref} | ${rref} // external reference
* ${vatid} ::= v${NN} // vat ID
* ${remid} ::= r${NN} // remote ID
* ${endid} ::= ${vatid} | ${remid} // endpoint ID
* ${queueName} ::= run | ${kpid}
*
* Queues
* queue.${queueName}.head = NN // queue head index
* queue.${queueName}.tail = NN // queue tail index
* queue.${queueName}.${NN} = JSON(CAPDATA) // queue entry #NN
*
* Kernel objects
* ${koid}.refCount = NN // reference count
* ${koid}.owner = ${vatid} // owner (where the object is)
*
* Kernel promises
* ${kpid}.refCount = NN // reference count
* ${kpid}.state = unresolved | fulfilled | rejected // current state of settlement
* ${kpid}.subscribers = JSON([${endid}]) // array of who is waiting for settlement
* ${kpid}.decider = ${endid} // who decides on settlement
* ${kpid}.value = JSON(CAPDATA) // value settled to, if settled
*
* C-lists
* cle.${endid}.${eref} = ${kref} // ERef->KRef mapping
* clk.${endid}.${kref} = ${eref} // KRef->ERef mapping
*
* Vat bookkeeping
* e.nextObjectId.${endid} = NN // allocation counter for imported object ERefs
* e.nextPromiseId.${endid} = NN // allocation counter for imported promise ERefs
* vatConfig.${vatid} = JSON(CONFIG) // vat's configuration object
*
* Kernel bookkeeping
* initialized = true // if set, indicates the store has been initialized
* nextVatId = NN // allocation counter for vat IDs
* nextRemoteId = NN // allocation counter for remote IDs
* k.nextObjectId = NN // allocation counter for object KRefs
* k.nextPromiseId = NN // allocation counter for promise KRefs
*/
import { Logger } from "@metamask/logger";
import { getBaseMethods } from "./methods/base.mjs";
import { getCListMethods } from "./methods/clist.mjs";
import { getCrankMethods } from "./methods/crank.mjs";
import { getGCMethods } from "./methods/gc.mjs";
import { getIdMethods } from "./methods/id.mjs";
import { getObjectMethods } from "./methods/object.mjs";
import { getPinMethods } from "./methods/pinned.mjs";
import { getPromiseMethods } from "./methods/promise.mjs";
import { getQueueMethods } from "./methods/queue.mjs";
import { getReachableMethods } from "./methods/reachable.mjs";
import { getRefCountMethods } from "./methods/refcount.mjs";
import { getRevocationMethods } from "./methods/revocation.mjs";
import { getSubclusterMethods } from "./methods/subclusters.mjs";
import { getTranslators } from "./methods/translators.mjs";
import { getVatMethods } from "./methods/vat.mjs";
/**
* Create a new KernelStore object wrapped around a raw kernel database. The
* resulting object provides a variety of operations for accessing various
* kernel-relevent persistent data structure abstractions on their own terms,
* without burdening the kernel with the particular details of how they are
* represented in storage. It is our hope that these operations may be later
* reimplemented on top of a more sophisticated database layer that can realize
* them more directly (and thus, one hopes, more efficiently) without requiring
* the kernel itself to be any the wiser.
*
* @param kdb - The kernel database this store is based on.
* @param logger - The logger to use.
* @returns A KernelStore object that maps various persistent kernel data
* structures onto `kdb`.
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function makeKernelStore(kdb, logger) {
// Initialize core state
/** KV store in which all the kernel's own state is kept. */
const kv = kdb.kernelKVStore;
const { provideCachedStoredValue, provideStoredQueue } = getBaseMethods(kv);
const context = {
kv,
/** The kernel's run queue. */
runQueue: provideStoredQueue('run', true),
/** Cache of the run queue's current length */
runQueueLengthCache: -1,
/** Counter for allocating kernel object IDs */
nextObjectId: provideCachedStoredValue('nextObjectId', '1'),
/** Counter for allocating kernel promise IDs */
nextPromiseId: provideCachedStoredValue('nextPromiseId', '1'),
/** Counter for allocating VatIDs */
nextVatId: provideCachedStoredValue('nextVatId', '1'),
/** Counter for allocating RemoteIDs */
nextRemoteId: provideCachedStoredValue('nextRemoteId', '1'),
// As refcounts are decremented, we accumulate a set of krefs for which
// action might need to be taken:
// * promises which are now resolved and unreferenced can be deleted
// * objects which are no longer reachable: export can be dropped
// * objects which are no longer recognizable: export can be retired
// This set is ephemeral: it lives in RAM, grows as deliveries and syscalls
// cause decrefs, and will be harvested by processRefcounts(). This needs to be
// called in the same transaction window as the syscalls/etc which prompted
// the change, else removals might be lost (not performed during the next
// replay).
maybeFreeKrefs: new Set(),
// Garbage collection
gcActions: provideCachedStoredValue('gcActions', '[]'),
reapQueue: provideCachedStoredValue('reapQueue', '[]'),
terminatedVats: provideCachedStoredValue('vats.terminated', '[]'),
inCrank: false,
savepoints: [],
// Subclusters
subclusters: provideCachedStoredValue('subclusters', '[]'),
nextSubclusterId: provideCachedStoredValue('nextSubclusterId', '1'),
vatToSubclusterMap: provideCachedStoredValue('vatToSubclusterMap', '{}'),
// Logging
logger: logger ?? new Logger({ tags: ['kernel-store'] }),
};
const id = getIdMethods(context);
const refCount = getRefCountMethods(context);
const object = getObjectMethods(context);
const promise = getPromiseMethods(context);
const revocation = getRevocationMethods(context);
const gc = getGCMethods(context);
const cList = getCListMethods(context);
const queue = getQueueMethods(context);
const vat = getVatMethods(context);
const reachable = getReachableMethods(context);
const translators = getTranslators(context);
const pinned = getPinMethods(context);
const crank = getCrankMethods(context, kdb);
const subclusters = getSubclusterMethods(context);
/**
* Create a new VatStore for a vat.
*
* @param vatID - The vat for which this is being done.
*
* @returns a a VatStore object for the given vat.
*/
function makeVatStore(vatID) {
return kdb.makeVatStore(vatID);
}
/**
* Delete all persistent state associated with a vat.
*
* @param vatId - The vat whose state is to be deleted.
*/
function deleteVat(vatId) {
vat.deleteVatConfig(vatId);
kdb.deleteVatStore(vatId);
subclusters.removeVatFromSubcluster(vatId);
}
/**
* Reset the kernel's persistent state and reset all counters.
*/
function reset() {
kdb.clear();
context.maybeFreeKrefs.clear();
context.runQueue = provideStoredQueue('run', true);
context.gcActions = provideCachedStoredValue('gcActions', '[]');
context.reapQueue = provideCachedStoredValue('reapQueue', '[]');
context.terminatedVats = provideCachedStoredValue('vats.terminated', '[]');
context.nextObjectId = provideCachedStoredValue('nextObjectId', '1');
context.nextPromiseId = provideCachedStoredValue('nextPromiseId', '1');
context.nextVatId = provideCachedStoredValue('nextVatId', '1');
context.nextRemoteId = provideCachedStoredValue('nextRemoteId', '1');
context.subclusters = provideCachedStoredValue('subclusters', '[]');
context.nextSubclusterId = provideCachedStoredValue('nextSubclusterId', '1');
context.vatToSubclusterMap = provideCachedStoredValue('vatToSubclusterMap', '{}');
crank.releaseAllSavepoints();
}
/**
* Delete everything from the database.
*/
function clear() {
kdb.clear();
}
return harden({
...id,
...queue,
...refCount,
...object,
...promise,
...revocation,
...gc,
...reachable,
...cList,
...vat,
...translators,
...pinned,
...crank,
...subclusters,
makeVatStore,
deleteVat,
clear,
reset,
kv,
});
}
//# sourceMappingURL=index.mjs.map