|
| 1 | +/* |
| 2 | + * Organization of keys in the key value store: |
| 3 | + * |
| 4 | + * Definitions |
| 5 | + * NN ::= some decimal integer |
| 6 | + * CAPDATA ::= capdata encoded structure value |
| 7 | + * JSON(${xx}) ::= JSON encoding of ${xx} |
| 8 | + * |
| 9 | + * ${koid} ::= ko${NN} // kernel object ID |
| 10 | + * ${kpid} ::= kp${NN} // kernel promise ID |
| 11 | + * ${kref} ::= ${koid} | ${kpid} // kernel reference |
| 12 | + * ${dir} ::= + | - // direction (for remote and vat references) |
| 13 | + * ${roid} ::= ro${dir}${NN} // remote object ID |
| 14 | + * ${rpid} ::= rp${dir}${NN} // remote promise ID |
| 15 | + * ${rref} ::= ${roid} | ${rpid} // remote reference |
| 16 | + * ${void} ::= o${dir}${NN} // vat object ID |
| 17 | + * ${vpid} ::= p${dir}${NN} // vat promise ID |
| 18 | + * ${vref} ::= ${void} | ${vpid} // vat reference |
| 19 | + * ${eref} ::= ${vref} | ${rref} // external reference |
| 20 | + * ${vatid} ::= v${NN} // vat ID |
| 21 | + * ${remid} ::= r${NN} // remote ID |
| 22 | + * ${endid} ::= ${vatid} | ${remid} // endpoint ID |
| 23 | + * ${queueName} ::= run | ${kpid} |
| 24 | + * |
| 25 | + * Queues |
| 26 | + * queue.${queueName}.head = NN // queue head index |
| 27 | + * queue.${queueName}.tail = NN // queue tail index |
| 28 | + * queue.${queueName}.${NN} = JSON(CAPDATA) // queue entry #NN |
| 29 | + * |
| 30 | + * Kernel objects |
| 31 | + * ${koid}.refCount = NN // reference count |
| 32 | + * ${koid}.owner = ${vatid} // owner (where the object is) |
| 33 | + * |
| 34 | + * Kernel promises |
| 35 | + * ${kpid}.refCount = NN // reference count |
| 36 | + * ${kpid}.state = unresolved | fulfilled | rejected // current state of settlement |
| 37 | + * ${kpid}.subscribers = JSON([${endid}]) // array of who is waiting for settlement |
| 38 | + * ${kpid}.decider = ${endid} // who decides on settlement |
| 39 | + * ${kpid}.value = JSON(CAPDATA) // value settled to, if settled |
| 40 | + * |
| 41 | + * C-lists |
| 42 | + * cle.${endid}.${eref} = ${kref} // ERef->KRef mapping |
| 43 | + * clk.${endid}.${kref} = ${eref} // KRef->ERef mapping |
| 44 | + * |
| 45 | + * Vat bookkeeping |
| 46 | + * e.nextObjectId.${endid} = NN // allocation counter for imported object ERefs |
| 47 | + * e.nextPromiseId.${endid} = NN // allocation counter for imported promise ERefs |
| 48 | + * vatConfig.${vatid} = JSON(CONFIG) // vat's configuration object |
| 49 | + * |
| 50 | + * Kernel bookkeeping |
| 51 | + * initialized = true // if set, indicates the store has been initialized |
| 52 | + * nextVatId = NN // allocation counter for vat IDs |
| 53 | + * nextRemoteId = NN // allocation counter for remote IDs |
| 54 | + * k.nextObjectId = NN // allocation counter for object KRefs |
| 55 | + * k.nextPromiseId = NN // allocation counter for promise KRefs |
| 56 | + */ |
| 57 | + |
| 58 | +import type { KernelDatabase, KVStore, VatStore } from '@ocap/store'; |
| 59 | + |
| 60 | +import { getBaseMethods } from './methods/base.ts'; |
| 61 | +import { getCListMethods } from './methods/clist.ts'; |
| 62 | +import { getGCMethods } from './methods/gc.ts'; |
| 63 | +import { getIdMethods } from './methods/id.ts'; |
| 64 | +import { getObjectMethods } from './methods/object.ts'; |
| 65 | +import { getPromiseMethods } from './methods/promise.ts'; |
| 66 | +import { getQueueMethods } from './methods/queue.ts'; |
| 67 | +import { getRefCountMethods } from './methods/refcount.ts'; |
| 68 | +import { getVatMethods } from './methods/vat.ts'; |
| 69 | +import type { StoreContext } from './types.ts'; |
| 70 | +import type { KRef, VatId } from '../types.ts'; |
| 71 | + |
| 72 | +/** |
| 73 | + * Create a new KernelStore object wrapped around a raw kernel database. The |
| 74 | + * resulting object provides a variety of operations for accessing various |
| 75 | + * kernel-relevent persistent data structure abstractions on their own terms, |
| 76 | + * without burdening the kernel with the particular details of how they are |
| 77 | + * represented in storage. It is our hope that these operations may be later |
| 78 | + * reimplemented on top of a more sophisticated database layer that can realize |
| 79 | + * them more directly (and thus, one hopes, more efficiently) without requiring |
| 80 | + * the kernel itself to be any the wiser. |
| 81 | + * |
| 82 | + * @param kdb - The kernel database this store is based on. |
| 83 | + * @returns A KernelStore object that maps various persistent kernel data |
| 84 | + * structures onto `kdb`. |
| 85 | + */ |
| 86 | +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type |
| 87 | +export function makeKernelStore(kdb: KernelDatabase) { |
| 88 | + // Initialize core state |
| 89 | + |
| 90 | + /** KV store in which all the kernel's own state is kept. */ |
| 91 | + const kv: KVStore = kdb.kernelKVStore; |
| 92 | + |
| 93 | + const { provideCachedStoredValue, provideStoredQueue } = getBaseMethods(kv); |
| 94 | + |
| 95 | + const context: StoreContext = { |
| 96 | + kv, |
| 97 | + /** The kernel's run queue. */ |
| 98 | + runQueue: provideStoredQueue('run', true), |
| 99 | + /** Cache of the run queue's current length */ |
| 100 | + runQueueLengthCache: -1, |
| 101 | + /** Counter for allocating kernel object IDs */ |
| 102 | + nextObjectId: provideCachedStoredValue('nextObjectId', '1'), |
| 103 | + /** Counter for allocating kernel promise IDs */ |
| 104 | + nextPromiseId: provideCachedStoredValue('nextPromiseId', '1'), |
| 105 | + /** Counter for allocating VatIDs */ |
| 106 | + nextVatId: provideCachedStoredValue('nextVatId', '1'), |
| 107 | + /** Counter for allocating RemoteIDs */ |
| 108 | + nextRemoteId: provideCachedStoredValue('nextRemoteId', '1'), |
| 109 | + // As refcounts are decremented, we accumulate a set of krefs for which |
| 110 | + // action might need to be taken: |
| 111 | + // * promises which are now resolved and unreferenced can be deleted |
| 112 | + // * objects which are no longer reachable: export can be dropped |
| 113 | + // * objects which are no longer recognizable: export can be retired |
| 114 | + // This set is ephemeral: it lives in RAM, grows as deliveries and syscalls |
| 115 | + // cause decrefs, and will be harvested by processRefcounts(). This needs to be |
| 116 | + // called in the same transaction window as the syscalls/etc which prompted |
| 117 | + // the change, else removals might be lost (not performed during the next |
| 118 | + // replay). |
| 119 | + maybeFreeKrefs: new Set<KRef>(), |
| 120 | + // Garbage collection |
| 121 | + gcActions: provideCachedStoredValue('gcActions', '[]'), |
| 122 | + reapQueue: provideCachedStoredValue('reapQueue', '[]'), |
| 123 | + }; |
| 124 | + |
| 125 | + const id = getIdMethods(context); |
| 126 | + const refCount = getRefCountMethods(context); |
| 127 | + const object = getObjectMethods(context); |
| 128 | + const promise = getPromiseMethods(context); |
| 129 | + const gc = getGCMethods(context); |
| 130 | + const cList = getCListMethods(context); |
| 131 | + const queue = getQueueMethods(context); |
| 132 | + const vat = getVatMethods(context); |
| 133 | + |
| 134 | + /** |
| 135 | + * Create a new VatStore for a vat. |
| 136 | + * |
| 137 | + * @param vatID - The vat for which this is being done. |
| 138 | + * |
| 139 | + * @returns a a VatStore object for the given vat. |
| 140 | + */ |
| 141 | + function makeVatStore(vatID: string): VatStore { |
| 142 | + return kdb.makeVatStore(vatID); |
| 143 | + } |
| 144 | + |
| 145 | + /** |
| 146 | + * Delete all persistent state associated with a vat. |
| 147 | + * |
| 148 | + * @param vatId - The vat whose state is to be deleted. |
| 149 | + */ |
| 150 | + function deleteVat(vatId: VatId): void { |
| 151 | + vat.deleteEndpoint(vatId); |
| 152 | + vat.deleteVatConfig(vatId); |
| 153 | + kdb.deleteVatStore(vatId); |
| 154 | + } |
| 155 | + |
| 156 | + /** |
| 157 | + * Reset the kernel's persistent state and reset all counters. |
| 158 | + */ |
| 159 | + function reset(): void { |
| 160 | + kdb.clear(); |
| 161 | + context.maybeFreeKrefs.clear(); |
| 162 | + context.runQueue = provideStoredQueue('run', true); |
| 163 | + context.gcActions = provideCachedStoredValue('gcActions', '[]'); |
| 164 | + context.reapQueue = provideCachedStoredValue('reapQueue', '[]'); |
| 165 | + context.nextObjectId = provideCachedStoredValue('nextObjectId', '1'); |
| 166 | + context.nextPromiseId = provideCachedStoredValue('nextPromiseId', '1'); |
| 167 | + context.nextVatId = provideCachedStoredValue('nextVatId', '1'); |
| 168 | + context.nextRemoteId = provideCachedStoredValue('nextRemoteId', '1'); |
| 169 | + } |
| 170 | + |
| 171 | + /** |
| 172 | + * Delete everything from the database. |
| 173 | + */ |
| 174 | + function clear(): void { |
| 175 | + kdb.clear(); |
| 176 | + } |
| 177 | + |
| 178 | + return harden({ |
| 179 | + ...id, |
| 180 | + ...queue, |
| 181 | + ...refCount, |
| 182 | + ...object, |
| 183 | + ...promise, |
| 184 | + ...gc, |
| 185 | + ...cList, |
| 186 | + ...vat, |
| 187 | + makeVatStore, |
| 188 | + deleteVat, |
| 189 | + clear, |
| 190 | + reset, |
| 191 | + kv, |
| 192 | + }); |
| 193 | +} |
| 194 | + |
| 195 | +export type KernelStore = ReturnType<typeof makeKernelStore>; |
0 commit comments