Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/ocap-kernel/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Deserialize CapData rejections in `Kernel.queueMessage` so vat errors surface as plain `Error` objects to all callers ([#928](https://github.com/MetaMask/ocap-kernel/pull/928))
- Detect peer restart across receiver state loss so the receiving kernel no longer silently drops a restarted peer's `seq=1` messages ([#948](https://github.com/MetaMask/ocap-kernel/pull/948))
- Persist the peer's last-observed incarnation and compare it on every successful handshake; on a detected restart, clear the peer's c-list contributions and reject the promises it was deciding before the new incarnation reuses any erefs
- Accept liveslots-allocated durable, virtual, and faceted vrefs (e.g. `o+d10/1`, `o+v3/4:0`) in `isVRef` / `insistERef` / `EndpointMessageStruct` validation ([#949](https://github.com/MetaMask/ocap-kernel/pull/949))
- Previously the regex only matched plain `[op][+-]N`, so any vat using `defineDurableKind` failed outgoing-send validation and persisted-slot reads

## [0.7.0]

Expand Down
37 changes: 28 additions & 9 deletions packages/ocap-kernel/src/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,12 +602,21 @@ describe('insistKRef', () => {
});

describe('isVRef', () => {
it.each(['o+0', 'o-1', 'p+42', 'p-0', 'o+123456789'])(
'returns true for valid VRef %s',
(value) => {
expect(isVRef(value)).toBe(true);
},
);
it.each([
'o+0',
'o-1',
'p+42',
'p-0',
'o+123456789',
// Vat-allocated durable / virtual objects (liveslots `defineDurableKind`
// and friends). See parseVatSlot in @agoric/swingset-liveslots.
'o+d10/1',
'o+v3/4',
'o+d10/1:0',
'o+v3/4:7',
])('returns true for valid VRef %s', (value) => {
expect(isVRef(value)).toBe(true);
});

it.each([
{ name: 'missing sign', value: 'o1' },
Expand All @@ -616,6 +625,16 @@ describe('isVRef', () => {
{ name: 'non-digit suffix', value: 'o+1abc' },
{ name: 'kernel ref', value: 'ko1' },
{ name: 'remote ref', value: 'ro+1' },
// Durability marker only valid on `o+`. `o-` is kernel-allocated;
// promises and devices never carry a durability/subid suffix.
{ name: 'durable on import', value: 'o-d10/1' },
{ name: 'durable on promise', value: 'p+d10/1' },
// Subid grammar requires a durability marker.
{ name: 'subid without marker', value: 'o+10/1' },
// Facet requires a subid.
{ name: 'facet without subid', value: 'o+d10:0' },
// Device refs are not supported by this kernel.
{ name: 'device ref', value: 'd+0' },
{ name: 'number', value: 123 },
{ name: 'null', value: null },
])('returns false for $name', ({ value }) => {
Expand All @@ -624,7 +643,7 @@ describe('isVRef', () => {
});

describe('insistVRef', () => {
it.each(['o+0', 'p-1', 'o+42'])(
it.each(['o+0', 'p-1', 'o+42', 'o+d10/1', 'o+v3/4:7'])(
'does not throw for valid VRef %s',
(value) => {
expect(() => insistVRef(value)).not.toThrow();
Expand Down Expand Up @@ -680,7 +699,7 @@ describe('insistRRef', () => {
});

describe('isERef', () => {
it.each(['o+0', 'p-1', 'ro+1', 'rp-2'])(
it.each(['o+0', 'p-1', 'ro+1', 'rp-2', 'o+d10/1', 'o+v3/4:7'])(
'returns true for valid ERef %s',
(value) => {
expect(isERef(value)).toBe(true);
Expand All @@ -698,7 +717,7 @@ describe('isERef', () => {
});

describe('insistERef', () => {
it.each(['o+0', 'p-1', 'ro+1', 'rp-2'])(
it.each(['o+0', 'p-1', 'ro+1', 'rp-2', 'o+d10/1', 'o+v3/4:7'])(
'does not throw for valid ERef %s',
(value) => {
expect(() => insistERef(value)).not.toThrow();
Expand Down
21 changes: 18 additions & 3 deletions packages/ocap-kernel/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,22 @@ export type KRef = string & { readonly [KRefBrand]: never };

declare const VRefBrand: unique symbol;
/**
* Vat-space reference. Format: `${'o'|'p'}${'+' | '-'}${number}`.
* E.g. `"o+0"`, `"p-7"`.
* Vat-space reference. Mirrors the vref grammar produced by
* `@agoric/swingset-liveslots`'s `parseVatSlot`:
*
* - `${'o'|'p'}${'+' | '-'}${number}` for plain refs
* (kernel imports, ephemeral exports, promises).
* E.g. `"o+0"`, `"o-3"`, `"p-7"`.
* - `o+${'d'|'v'}${kindId}/${instanceId}` for vat-allocated
* virtual (`v`) or durable (`d`) objects.
* E.g. `"o+d10/1"`, `"o+v3/4"`.
* - `o+${'d'|'v'}${kindId}/${instanceId}:${facetId}` for the
* same with a facet selector.
* E.g. `"o+d10/1:0"`.
*
* The durability/subid/facet syntax is only valid for `o+`
* (vat-allocated objects); kernel imports, promises, and remotes
* never carry it.
*/
export type VRef = string & { readonly [VRefBrand]: never };

Expand Down Expand Up @@ -139,7 +153,8 @@ export const isKRef = (value: unknown): value is KRef =>
typeof value === 'string' && /^k[op]\d+$/u.test(value);

export const isVRef = (value: unknown): value is VRef =>
typeof value === 'string' && /^[op][+-]\d+$/u.test(value);
typeof value === 'string' &&
/^(?:p[+-]\d+|o-\d+|o\+(?:\d+|[dv]\d+\/\d+(?::\d+)?))$/u.test(value);

export const isRRef = (value: unknown): value is RRef =>
typeof value === 'string' && /^r[op][+-]\d+$/u.test(value);
Expand Down
Loading