generated from MetaMask/metamask-module-template
-
Notifications
You must be signed in to change notification settings - Fork 6
feat(extension): Database access PoC, with pseudo-kernel in a web worker #100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
3bd0608
Database access PoC, with pseudo-kernel in a web worker
FUDCo cfda2f3
chore: @#($*&)@#$& lint
FUDCo 73fb980
chore: #$@#)#&! yarn.lock
FUDCo 1b44a1d
chore: CI doesn't like anticipated dependencies
FUDCo a04ab9f
chore: #$@#)#&! yarn.lock again
FUDCo 6593c6e
chore: another little bit of lint-related cleanup
FUDCo 9b64764
chore: more review cleanups
FUDCo 7d90a3e
chore: further cleanup from review
FUDCo 7ea16ef
chore: TypeScript improvements, sort of
FUDCo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,228 @@ | ||
| import './endoify.js'; | ||
| import type { Command } from '@ocap/utils'; | ||
| import { CommandMethod } from '@ocap/utils'; | ||
| import type { Database } from '@sqlite.org/sqlite-wasm'; | ||
| import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; | ||
|
|
||
| main().catch(console.error); | ||
|
|
||
| type Queue<Type> = Type[]; | ||
|
|
||
| type VatId = `v${number}`; | ||
| type RemoteId = `r${number}`; | ||
| type EndpointId = VatId | RemoteId; | ||
|
|
||
| type RefTypeTag = 'o' | 'p'; | ||
| type RefDirectionTag = '+' | '-'; | ||
| type InnerKRef = `${RefTypeTag}${number}`; | ||
| type InnerERef = `${RefTypeTag}${RefDirectionTag}${number}`; | ||
|
|
||
| type KRef = `k${InnerKRef}`; | ||
| type VRef = `v${InnerERef}`; | ||
| type RRef = `r${InnerERef}`; | ||
| type ERef = VRef | RRef; | ||
|
|
||
| type CapData = { | ||
| body: string; | ||
| slots: string[]; | ||
| }; | ||
|
|
||
| type Message = { | ||
| target: ERef | KRef; | ||
| method: string; | ||
| params: CapData; | ||
| }; | ||
|
|
||
| // Per-endpoint persistent state | ||
| type EndpointState<IdType> = { | ||
| name: string; | ||
| id: IdType; | ||
| nextExportObjectIdCounter: number; | ||
| nextExportPromiseIdCounter: number; | ||
| eRefToKRef: Map<ERef, KRef>; | ||
| kRefToERef: Map<KRef, ERef>; | ||
| }; | ||
|
|
||
| type VatState = { | ||
| messagePort: MessagePort; | ||
| state: EndpointState<VatId>; | ||
| source: string; | ||
| kvTable: Map<string, string>; | ||
| }; | ||
|
|
||
| type RemoteState = { | ||
| state: EndpointState<RemoteId>; | ||
| connectToURL: string; | ||
| // more here about maintaining connection... | ||
| }; | ||
|
|
||
| // Kernel persistent state | ||
| type KernelObject = { | ||
| owner: EndpointId; | ||
| reachableCount: number; | ||
| recognizableCount: number; | ||
| }; | ||
|
|
||
| type PromiseState = 'unresolved' | 'fulfilled' | 'rejected'; | ||
|
|
||
| type KernelPromise = { | ||
| decider: EndpointId; | ||
| state: PromiseState; | ||
| referenceCount: number; | ||
| messageQueue: Queue<Message>; | ||
| value: undefined | CapData; | ||
| }; | ||
|
|
||
| // export temporarily to shut up lint whinges about unusedness | ||
| export type KernelState = { | ||
| runQueue: Queue<Message>; | ||
| nextVatIdCounter: number; | ||
| vats: Map<VatId, VatState>; | ||
| nextRemoteIdCounter: number; | ||
| remotes: Map<RemoteId, RemoteState>; | ||
| nextKernelObjectIdCounter: number; | ||
| kernelObjects: Map<KRef, KernelObject>; | ||
| nextKernePromiseIdCounter: number; | ||
| kernelPromises: Map<KRef, KernelPromise>; | ||
| }; | ||
|
|
||
| /** | ||
| * Ensure that SQLite is initialized. | ||
| * | ||
| * @returns The SQLite database object. | ||
| */ | ||
| async function initDB(): Promise<Database> { | ||
| const sqlite3 = await sqlite3InitModule(); | ||
| if (sqlite3.oo1.OpfsDb) { | ||
| return new sqlite3.oo1.OpfsDb('/testdb.sqlite', 'cwt'); | ||
| } | ||
| console.warn(`OPFS not enabled, database will be ephemeral`); | ||
| return new sqlite3.oo1.DB('/testdb.sqlite', 'cwt'); | ||
| } | ||
|
|
||
| /** | ||
| * The main function for the offscreen script. | ||
| */ | ||
| async function main(): Promise<void> { | ||
| const db = await initDB(); | ||
| db.exec(` | ||
| CREATE TABLE IF NOT EXISTS kv ( | ||
| key TEXT, | ||
| value TEXT, | ||
| PRIMARY KEY(key) | ||
| ) | ||
| `); | ||
|
|
||
| const sqlKVGet = db.prepare(` | ||
| SELECT value | ||
| FROM kv | ||
| WHERE key = ? | ||
| `); | ||
|
|
||
| /** | ||
| * Exercise reading from the database. | ||
| * | ||
| * @param key - A key to fetch. | ||
| * @returns The value at that key. | ||
| */ | ||
| function kvGet(key: string): string { | ||
| sqlKVGet.bind([key]); | ||
| if (sqlKVGet.step()) { | ||
| const result = sqlKVGet.getString(0); | ||
| if (result) { | ||
| sqlKVGet.reset(); | ||
| console.log(`kernel get '${key}' as '${result}'`); | ||
| return result; | ||
| } | ||
| } | ||
| sqlKVGet.reset(); | ||
| throw Error(`no record matching key '${key}'`); | ||
| } | ||
|
|
||
| const sqlKVSet = db.prepare(` | ||
| INSERT INTO kv (key, value) | ||
| VALUES (?, ?) | ||
| ON CONFLICT DO UPDATE SET value = excluded.value | ||
| `); | ||
|
|
||
| /** | ||
| * Exercise writing to the database. | ||
| * | ||
| * @param key - A key to assign. | ||
| * @param value - The value to assign to it. | ||
| */ | ||
| function kvSet(key: string, value: string): void { | ||
| console.log(`kernel set '${key}' to '${value}'`); | ||
| sqlKVSet.bind([key, value]); | ||
| sqlKVSet.step(); | ||
| sqlKVSet.reset(); | ||
| } | ||
|
|
||
| // Handle messages from the console service worker | ||
| onmessage = async (event) => { | ||
| const message = event.data; | ||
| const { method, params } = message as Command; | ||
| console.log('received message: ', method, params); | ||
| switch (method) { | ||
| case CommandMethod.Evaluate: | ||
| reply(CommandMethod.Evaluate, await evaluate(params)); | ||
| break; | ||
| case CommandMethod.CapTpCall: { | ||
| reply( | ||
| CommandMethod.CapTpCall, | ||
| 'Error: CapTpCall not implemented here (yet)', | ||
| ); | ||
| break; | ||
| } | ||
| case CommandMethod.CapTpInit: | ||
| reply( | ||
| CommandMethod.CapTpInit, | ||
| 'Error: CapTpInit not implemented here (yet)', | ||
| ); | ||
| break; | ||
| case CommandMethod.Ping: | ||
| reply(CommandMethod.Ping, 'pong'); | ||
| break; | ||
| case CommandMethod.KVSet: { | ||
| const { key, value } = params; | ||
| kvSet(key, value); | ||
| reply(CommandMethod.KVSet, `~~~ set "${key}" to "${value}" ~~~`); | ||
| break; | ||
| } | ||
| case CommandMethod.KVGet: { | ||
| try { | ||
| const result = kvGet(params); | ||
| reply(CommandMethod.KVGet, result); | ||
| } catch (problem) { | ||
| reply(CommandMethod.KVGet, problem as string); // cast is a lie, it really is an Error | ||
| } | ||
| break; | ||
| } | ||
| default: | ||
| console.error( | ||
| // eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
| `kernel received unexpected method in message: "${method}"`, | ||
rekmarks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ); | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * Reply to the background script. | ||
| * | ||
| * @param method - The message method. | ||
| * @param params - The message params. | ||
| */ | ||
| function reply(method: CommandMethod, params?: string): void { | ||
| postMessage({ method, params }); | ||
| } | ||
|
|
||
| /** | ||
| * Evaluate a string in the default iframe. | ||
| * | ||
| * @param _source - The source string to evaluate. | ||
| * @returns The result of the evaluation, or an error message. | ||
| */ | ||
| async function evaluate(_source: string): Promise<string> { | ||
| return `Error: evaluate not implemented here (yet)`; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should have an issue to add a lint rule requiring such hygiene.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! Agoric had just such a lint rule, so we can grab the requisite eslint voodoo from the
agoric-sdkrepo.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issues welcome 😄