Switch to using the schema-generated code. It's about half the speed. I wonder why?

This commit is contained in:
Tony Garnock-Jones 2021-03-11 21:56:35 +01:00
parent 4393561fc9
commit 5086e3b209
23 changed files with 600 additions and 673 deletions

View File

@ -10,7 +10,7 @@
"@preserves/schema": "^0.1.1"
},
"scripts": {
"regenerate": "rm -rf ./src/gen && preserves-schema-ts --module Pointer=./src/actor.ts --output ./src/gen ./schemas/**/*.prs",
"regenerate": "rm -rf ./src/gen && preserves-schema-ts --module Actor=./src/actor.ts --module Protocol=./src/protocol.ts --output ./src/gen './schemas/**/*.prs'",
"regenerate:watch": "yarn regenerate --watch",
"compile": "tsc",
"compile:watch": "tsc -w",

View File

@ -1,8 +1,9 @@
version 1 .
pointer Pointer.Ref .
pointer Actor.Ref .
BoxState = <BoxState int>.
SetBox = <SetBox int>.
BoxCap = BoxState / <Observe =SetBox ref>.
ClientCap = SetBox / <Observe =BoxState ref>.
; BoxCap = BoxState / <Observe =SetBox ref>.
; ClientCap = SetBox / <Observe =BoxState ref>.
.

View File

@ -1,7 +1,8 @@
version 1 .
pointer Actor.Ref .
;As implemented
Observe = <Observe symbol ref>.
Observe = <Observe @label symbol @observer ref>.
; ;As will be implemented soon
; Observe = <Observe Pattern ref>.

13
schemas/protocol.prs Normal file
View File

@ -0,0 +1,13 @@
version 1 .
pointer Protocol.WireRef .
Assertion = any .
Handle = int .
Event = Assert / Retract / Message / Sync .
Oid = int .
Turn = [[Oid Event] ...].
Assert = <assert @assertion Assertion @handle Handle>.
Retract = <retract @handle Handle>.
Message = <message @body Assertion>.
Sync = <sync @peer ref>.

View File

@ -1,20 +0,0 @@
version 1 .
ConstructorSpec = <rec any int> / <arr int> / <dict>.
Pattern = </[
<_>
<bind symbol Pattern>
<and [Pattern ...]>
<not Pattern>
<lit any>
<compound ConstructorSpec { any: Pattern ...:... }>
]>.
Template = </[
<ref symbol>
<lit any>
<compound ConstructorSpec { any: Template ...:... }>
]>.
Rewrite = <rewrite Pattern Template>.

38
schemas/sturdy.prs Normal file
View File

@ -0,0 +1,38 @@
version 1 .
pointer Actor.Ref .
; Each Attenuation is a stage. The sequence of Attenuations is run RIGHT-TO-LEFT.
; That is, the newest Attenuations are at the right.
SturdyRef = <ref @oid any @caveatChain [Attenuation ...] @sig bytes>.
; An individual Attenuation is run RIGHT-TO-LEFT.
; That is, the newest Caveats are at the right.
Attenuation = [Caveat ...].
; embodies 1st-party caveats over assertion structure, but nothing else
; can add 3rd-party caveats and richer predicates later
Caveat = Rewrite / Alts .
Rewrite = <rewrite @pattern Pattern @template Template>.
Alts = <or @alternatives [Rewrite ...]>.
Resolve = <resolve @sturdyref SturdyRef @observer ref>.
;---------------------------------------------------------------------------
ConstructorSpec = CRec / CArr / CDict .
CRec = <rec @label any @arity int>.
CArr = <arr @arity int>.
CDict = <dict>.
Lit = <lit @value any>.
Pattern = PDiscard / PBind / PAnd / PNot / Lit / PCompound .
PDiscard = <_>.
PBind = <bind @name symbol @pattern Pattern>.
PAnd = <and @patterns [Pattern ...]>.
PNot = <not @pattern Pattern>.
PCompound = <compound @ctor ConstructorSpec @members { any: Pattern ...:... }>.
Template = TRef / Lit / TCompound .
TRef = <ref @name symbol>.
TCompound = <compound @ctor ConstructorSpec @members { any: Template ...:... }>.

View File

@ -1,17 +0,0 @@
version 1 .
SturdyRef = <ref any [Caveat ...] bytes>.
Caveat = rewrite.Rewrite / <or [rewrite.Rewrite ...]>.
Handle = int .
Event = Assert / Retract / Message / Sync .
Oid = int .
Turn = [[Oid Event] ...].
Assert = <assert @assertion any @handle Handle>.
Retract = <retract @handle Handle>.
Message = <message @body any>.
Sync = <sync @peer ref>.
; WireRef = [0 Oid] / [1 Oid AttenuationStage ...].
.

View File

@ -1,6 +1,6 @@
import { BoxState, SetBox } from "./gen/box-protocol.js";
import { Observe } from "./gen/dataspace.js";
import { Assertion, Handle, Ref, Turn } from "./actor.js";
import { Observe } from "./dataspace.js";
let startTime = Date.now();
let prevValue = 0;

View File

@ -1,5 +1,5 @@
import { BoxState, SetBox } from "./gen/box-protocol.js";
import { Observe } from "./dataspace.js";
import { Observe } from "./gen/dataspace.js";
import { Assertion, Ref, Turn } from "./actor.js";
export default function (t: Turn, ds: Ref) {

View File

@ -2,6 +2,9 @@ import { Assertion, Entity, Handle, Ref, Turn } from 'actor';
import { Dictionary, IdentityMap, is, Record, Tuple } from '@preserves/core';
import { Bag, ChangeDescription } from './bag';
import { Observe } from './gen/dataspace';
export * from './gen/dataspace';
// Q. Why keep "Observe"? Why not do the clever trick of asserting the
// observer, and having the dataspace read the implicit pattern it's
// interested in off its attenuator?
@ -14,7 +17,12 @@ import { Bag, ChangeDescription } from './bag';
// Because we want to have onlookers have some hope of seeing whether
// a pattern of interest to them is being observed, and if we used
// attenuators to match, we'd have to expose visibility into
// attenuators into the pattern language. See next question.
// attenuators into the pattern language. See next question. (3)
// Because reflection on attenuators is a big, heavy hammer, and it's
// better to be explicit about patterns! Also, some attenuations
// happen behind a veil of secrecy - they're not all open for the
// world to read about. Actors may proxy communications in arbitrary,
// secret ways.
//
// Q. What kinds of constraints on the pattern language are there?
//
@ -26,9 +34,6 @@ import { Bag, ChangeDescription } from './bag';
// that enforced some kind of normal forms for its patterns, so
// observer-observers and attenuator patterns don't have to deal with
// spurious variation.
//
export const Observe = Record.makeConstructor<{label: Assertion, observer: Ref}, Ref>()(
Symbol.for('Observe'), ['label', 'observer']);
export class Dataspace implements Partial<Entity> {
readonly handleMap: IdentityMap<Handle, Record<Assertion, any, Ref>> = new IdentityMap();

View File

@ -1,8 +1,7 @@
import * as _ from "@preserves/core";
import * as _i_Pointer from "../actor";
import * as _i_Actor from "../actor";
export const $BoxState = Symbol.for("BoxState");
export const $Observe = Symbol.for("Observe");
export const $SetBox = Symbol.for("SetBox");
export const BoxState = _.Record.makeConstructor<{"_field0": number}, _ptr>()($BoxState, ["_field0"]);
@ -13,11 +12,7 @@ export const SetBox = _.Record.makeConstructor<{"_field0": number}, _ptr>()($Set
export type SetBox = _.Record<(typeof $SetBox), [number], _ptr>;
export type BoxCap = (BoxState | _.Record<(typeof $Observe), [(typeof $SetBox), _ptr], _ptr>);
export type ClientCap = (SetBox | _.Record<(typeof $Observe), [(typeof $BoxState), _ptr], _ptr>);
export type _ptr = _i_Pointer.Ref;
export type _ptr = _i_Actor.Ref;
export type _val = _.Value<_ptr>;
@ -46,33 +41,3 @@ export function asSetBox(v: any): SetBox {
if (!isSetBox(v)) {throw new TypeError(`Invalid SetBox: ${_.stringify(v)}`);} else {return v;};
}
export function isBoxCap(v: any): v is BoxCap {
return (
isBoxState(v) ||
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $Observe) &&
((v.length === 2) && _.is(v[0], $SetBox) && _.isPointer(v[1]))
)
);
}
export function asBoxCap(v: any): BoxCap {
if (!isBoxCap(v)) {throw new TypeError(`Invalid BoxCap: ${_.stringify(v)}`);} else {return v;};
}
export function isClientCap(v: any): v is ClientCap {
return (
isSetBox(v) ||
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $Observe) &&
((v.length === 2) && _.is(v[0], $BoxState) && _.isPointer(v[1]))
)
);
}
export function asClientCap(v: any): ClientCap {
if (!isClientCap(v)) {throw new TypeError(`Invalid ClientCap: ${_.stringify(v)}`);} else {return v;};
}

View File

@ -1,12 +1,13 @@
import * as _ from "@preserves/core";
import * as _i_Actor from "../actor";
export const $Observe = Symbol.for("Observe");
export const Observe = _.Record.makeConstructor<{"_field0": symbol, "_field1": _ptr}, _ptr>()($Observe, ["_field0","_field1"]);
export const Observe = _.Record.makeConstructor<{"label": symbol, "observer": _ptr}, _ptr>()($Observe, ["label","observer"]);
export type Observe = _.Record<(typeof $Observe), [symbol, _ptr], _ptr>;
export type _ptr = never;
export type _ptr = _i_Actor.Ref;
export type _val = _.Value<_ptr>;

View File

@ -1,18 +1,12 @@
import * as _ from "@preserves/core";
import * as _i_rewrite from "./rewrite";
import * as _i_Protocol from "../protocol";
export const $assert = Symbol.for("assert");
export const $message = Symbol.for("message");
export const $or = Symbol.for("or");
export const $ref = Symbol.for("ref");
export const $retract = Symbol.for("retract");
export const $sync = Symbol.for("sync");
export const SturdyRef = _.Record.makeConstructor<{"_field0": _val, "_field1": Array<Caveat>, "_field2": _.Bytes}, _ptr>()($ref, ["_field0","_field1","_field2"]);
export type SturdyRef = _.Record<(typeof $ref), [_val, Array<Caveat>, _.Bytes], _ptr>;
export type Caveat = (_i_rewrite.Rewrite | _.Record<(typeof $or), [Array<_i_rewrite.Rewrite>], _ptr>);
export type Assertion = _val;
export type Handle = number;
@ -22,70 +16,31 @@ export type Oid = number;
export type Turn = Array<[Oid, Event]>;
export const Assert = _.Record.makeConstructor<{"assertion": _val, "handle": Handle}, _ptr>()($assert, ["assertion","handle"]);
export const Assert = _.Record.makeConstructor<{"assertion": Assertion, "handle": Handle}, _ptr>()($assert, ["assertion","handle"]);
export type Assert = _.Record<(typeof $assert), [_val, Handle], _ptr>;
export type Assert = _.Record<(typeof $assert), [Assertion, Handle], _ptr>;
export const Retract = _.Record.makeConstructor<{"handle": Handle}, _ptr>()($retract, ["handle"]);
export type Retract = _.Record<(typeof $retract), [Handle], _ptr>;
export const Message = _.Record.makeConstructor<{"body": _val}, _ptr>()($message, ["body"]);
export const Message = _.Record.makeConstructor<{"body": Assertion}, _ptr>()($message, ["body"]);
export type Message = _.Record<(typeof $message), [_val], _ptr>;
export type Message = _.Record<(typeof $message), [Assertion], _ptr>;
export const Sync = _.Record.makeConstructor<{"peer": _ptr}, _ptr>()($sync, ["peer"]);
export type Sync = _.Record<(typeof $sync), [_ptr], _ptr>;
export type _ptr = never;
export type _ptr = _i_Protocol.WireRef;
export type _val = _.Value<_ptr>;
export function isSturdyRef(v: any): v is SturdyRef {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $ref) &&
(
(v.length === 3) &&
true &&
(
_.Array.isArray(v[1]) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[1]) &&
(v[1].length >= 0) &&
v[1].slice(0).every(v => (isCaveat(v)))
) &&
_.Bytes.isBytes(v[2])
)
);
}
export function isAssertion(v: any): v is Assertion {return true;}
export function asSturdyRef(v: any): SturdyRef {
if (!isSturdyRef(v)) {throw new TypeError(`Invalid SturdyRef: ${_.stringify(v)}`);} else {return v;};
}
export function isCaveat(v: any): v is Caveat {
return (
_i_rewrite.isRewrite(v) ||
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $or) &&
(
(v.length === 1) &&
(
_.Array.isArray(v[0]) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0]) &&
(v[0].length >= 0) &&
v[0].slice(0).every(v => (_i_rewrite.isRewrite(v)))
)
)
)
);
}
export function asCaveat(v: any): Caveat {
if (!isCaveat(v)) {throw new TypeError(`Invalid Caveat: ${_.stringify(v)}`);} else {return v;};
export function asAssertion(v: any): Assertion {
if (!isAssertion(v)) {throw new TypeError(`Invalid Assertion: ${_.stringify(v)}`);} else {return v;};
}
export function isHandle(v: any): v is Handle {return typeof v === 'number';}
@ -131,7 +86,7 @@ export function isAssert(v: any): v is Assert {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $assert) &&
((v.length === 2) && true && isHandle(v[1]))
((v.length === 2) && isAssertion(v[0]) && isHandle(v[1]))
);
}
@ -155,7 +110,7 @@ export function isMessage(v: any): v is Message {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $message) &&
((v.length === 1) && true)
((v.length === 1) && isAssertion(v[0]))
);
}

View File

@ -1,227 +0,0 @@
import * as _ from "@preserves/core";
export const $__ = Symbol.for("_");
export const $and = Symbol.for("and");
export const $arr = Symbol.for("arr");
export const $bind = Symbol.for("bind");
export const $compound = Symbol.for("compound");
export const $dict = Symbol.for("dict");
export const $lit = Symbol.for("lit");
export const $not = Symbol.for("not");
export const $rec = Symbol.for("rec");
export const $ref = Symbol.for("ref");
export const $rewrite = Symbol.for("rewrite");
export const __lit3 = Symbol.for("/");
export type ConstructorSpec = (
_.Record<(typeof $rec), [_val, number], _ptr> |
_.Record<(typeof $arr), [number], _ptr> |
_.Record<(typeof $dict), [], _ptr>
);
export const Pattern = _.Record.makeConstructor<{
"_field0": [
_.Record<(typeof $__), [], _ptr>,
_.Record<(typeof $bind), [symbol, Pattern], _ptr>,
_.Record<(typeof $and), [Array<Pattern>], _ptr>,
_.Record<(typeof $not), [Pattern], _ptr>,
_.Record<(typeof $lit), [_val], _ptr>,
_.Record<(typeof $compound), [ConstructorSpec, _.KeyedDictionary<_val, Pattern>], _ptr>
]
}, _ptr>()(__lit3, ["_field0"]);
export type Pattern = _.Record<
(typeof __lit3),
[
[
_.Record<(typeof $__), [], _ptr>,
_.Record<(typeof $bind), [symbol, Pattern], _ptr>,
_.Record<(typeof $and), [Array<Pattern>], _ptr>,
_.Record<(typeof $not), [Pattern], _ptr>,
_.Record<(typeof $lit), [_val], _ptr>,
_.Record<(typeof $compound), [ConstructorSpec, _.KeyedDictionary<_val, Pattern>], _ptr>
]
],
_ptr
>;
export const Template = _.Record.makeConstructor<{
"_field0": [
_.Record<(typeof $ref), [symbol], _ptr>,
_.Record<(typeof $lit), [_val], _ptr>,
_.Record<(typeof $compound), [ConstructorSpec, _.KeyedDictionary<_val, Template>], _ptr>
]
}, _ptr>()(__lit3, ["_field0"]);
export type Template = _.Record<
(typeof __lit3),
[
[
_.Record<(typeof $ref), [symbol], _ptr>,
_.Record<(typeof $lit), [_val], _ptr>,
_.Record<(typeof $compound), [ConstructorSpec, _.KeyedDictionary<_val, Template>], _ptr>
]
],
_ptr
>;
export const Rewrite = _.Record.makeConstructor<{"_field0": Pattern, "_field1": Template}, _ptr>()($rewrite, ["_field0","_field1"]);
export type Rewrite = _.Record<(typeof $rewrite), [Pattern, Template], _ptr>;
export type _ptr = never;
export type _val = _.Value<_ptr>;
export function isConstructorSpec(v: any): v is ConstructorSpec {
return (
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $rec) &&
((v.length === 2) && true && typeof v[1] === 'number')
) ||
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $arr) &&
((v.length === 1) && typeof v[0] === 'number')
) ||
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $dict) &&
((v.length === 0))
)
);
}
export function asConstructorSpec(v: any): ConstructorSpec {
if (!isConstructorSpec(v)) {throw new TypeError(`Invalid ConstructorSpec: ${_.stringify(v)}`);} else {return v;};
}
export function isPattern(v: any): v is Pattern {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, __lit3) &&
(
(v.length === 1) &&
(
_.Array.isArray(v[0]) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0]) &&
(v[0].length === 6) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][0]) &&
_.is(v[0][0].label, $__) &&
((v[0][0].length === 0))
) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][1]) &&
_.is(v[0][1].label, $bind) &&
(
(v[0][1].length === 2) &&
typeof v[0][1][0] === 'symbol' &&
isPattern(v[0][1][1])
)
) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][2]) &&
_.is(v[0][2].label, $and) &&
(
(v[0][2].length === 1) &&
(
_.Array.isArray(v[0][2][0]) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][2][0]) &&
(v[0][2][0].length >= 0) &&
v[0][2][0].slice(0).every(v => (isPattern(v)))
)
)
) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][3]) &&
_.is(v[0][3].label, $not) &&
((v[0][3].length === 1) && isPattern(v[0][3][0]))
) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][4]) &&
_.is(v[0][4].label, $lit) &&
((v[0][4].length === 1) && true)
) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][5]) &&
_.is(v[0][5].label, $compound) &&
(
(v[0][5].length === 2) &&
isConstructorSpec(v[0][5][0]) &&
(
_.Dictionary.isDictionary<_val, _ptr>(v[0][5][1]) &&
((() => {
for (const e of v[0][5][1]) {if (!(true)) return false; if (!(isPattern(e[1]))) return false;};
return true;
})())
)
)
)
)
)
);
}
export function asPattern(v: any): Pattern {
if (!isPattern(v)) {throw new TypeError(`Invalid Pattern: ${_.stringify(v)}`);} else {return v;};
}
export function isTemplate(v: any): v is Template {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, __lit3) &&
(
(v.length === 1) &&
(
_.Array.isArray(v[0]) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0]) &&
(v[0].length === 3) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][0]) &&
_.is(v[0][0].label, $ref) &&
((v[0][0].length === 1) && typeof v[0][0][0] === 'symbol')
) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][1]) &&
_.is(v[0][1].label, $lit) &&
((v[0][1].length === 1) && true)
) &&
(
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0][2]) &&
_.is(v[0][2].label, $compound) &&
(
(v[0][2].length === 2) &&
isConstructorSpec(v[0][2][0]) &&
(
_.Dictionary.isDictionary<_val, _ptr>(v[0][2][1]) &&
((() => {
for (const e of v[0][2][1]) {if (!(true)) return false; if (!(isTemplate(e[1]))) return false;};
return true;
})())
)
)
)
)
)
);
}
export function asTemplate(v: any): Template {
if (!isTemplate(v)) {throw new TypeError(`Invalid Template: ${_.stringify(v)}`);} else {return v;};
}
export function isRewrite(v: any): v is Rewrite {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $rewrite) &&
((v.length === 2) && isPattern(v[0]) && isTemplate(v[1]))
);
}
export function asRewrite(v: any): Rewrite {
if (!isRewrite(v)) {throw new TypeError(`Invalid Rewrite: ${_.stringify(v)}`);} else {return v;};
}

372
src/gen/sturdy.ts Normal file
View File

@ -0,0 +1,372 @@
import * as _ from "@preserves/core";
import * as _i_Actor from "../actor";
export const $__ = Symbol.for("_");
export const $and = Symbol.for("and");
export const $arr = Symbol.for("arr");
export const $bind = Symbol.for("bind");
export const $compound = Symbol.for("compound");
export const $dict = Symbol.for("dict");
export const $lit = Symbol.for("lit");
export const $not = Symbol.for("not");
export const $or = Symbol.for("or");
export const $rec = Symbol.for("rec");
export const $ref = Symbol.for("ref");
export const $resolve = Symbol.for("resolve");
export const $rewrite = Symbol.for("rewrite");
export const SturdyRef = _.Record.makeConstructor<{"oid": _val, "caveatChain": Array<Attenuation>, "sig": _.Bytes}, _ptr>()($ref, ["oid","caveatChain","sig"]);
export type SturdyRef = _.Record<(typeof $ref), [_val, Array<Attenuation>, _.Bytes], _ptr>;
export type Attenuation = Array<Caveat>;
export type Caveat = (Rewrite | Alts);
export const Rewrite = _.Record.makeConstructor<{"pattern": Pattern, "template": Template}, _ptr>()($rewrite, ["pattern","template"]);
export type Rewrite = _.Record<(typeof $rewrite), [Pattern, Template], _ptr>;
export const Alts = _.Record.makeConstructor<{"alternatives": Array<Rewrite>}, _ptr>()($or, ["alternatives"]);
export type Alts = _.Record<(typeof $or), [Array<Rewrite>], _ptr>;
export const Resolve = _.Record.makeConstructor<{"sturdyref": SturdyRef, "observer": _ptr}, _ptr>()($resolve, ["sturdyref","observer"]);
export type Resolve = _.Record<(typeof $resolve), [SturdyRef, _ptr], _ptr>;
export type ConstructorSpec = (CRec | CArr | CDict);
export const CRec = _.Record.makeConstructor<{"label": _val, "arity": number}, _ptr>()($rec, ["label","arity"]);
export type CRec = _.Record<(typeof $rec), [_val, number], _ptr>;
export const CArr = _.Record.makeConstructor<{"arity": number}, _ptr>()($arr, ["arity"]);
export type CArr = _.Record<(typeof $arr), [number], _ptr>;
export const CDict = _.Record.makeConstructor<{}, _ptr>()($dict, []);
export type CDict = _.Record<(typeof $dict), [], _ptr>;
export const Lit = _.Record.makeConstructor<{"value": _val}, _ptr>()($lit, ["value"]);
export type Lit = _.Record<(typeof $lit), [_val], _ptr>;
export type Pattern = (PDiscard | PBind | PAnd | PNot | Lit | PCompound);
export const PDiscard = _.Record.makeConstructor<{}, _ptr>()($__, []);
export type PDiscard = _.Record<(typeof $__), [], _ptr>;
export const PBind = _.Record.makeConstructor<{"name": symbol, "pattern": Pattern}, _ptr>()($bind, ["name","pattern"]);
export type PBind = _.Record<(typeof $bind), [symbol, Pattern], _ptr>;
export const PAnd = _.Record.makeConstructor<{"patterns": Array<Pattern>}, _ptr>()($and, ["patterns"]);
export type PAnd = _.Record<(typeof $and), [Array<Pattern>], _ptr>;
export const PNot = _.Record.makeConstructor<{"pattern": Pattern}, _ptr>()($not, ["pattern"]);
export type PNot = _.Record<(typeof $not), [Pattern], _ptr>;
export const PCompound = _.Record.makeConstructor<{"ctor": ConstructorSpec, "members": _.KeyedDictionary<_val, Pattern, _ptr>}, _ptr>()($compound, ["ctor","members"]);
export type PCompound = _.Record<
(typeof $compound),
[ConstructorSpec, _.KeyedDictionary<_val, Pattern, _ptr>],
_ptr
>;
export type Template = (TRef | Lit | TCompound);
export const TRef = _.Record.makeConstructor<{"name": symbol}, _ptr>()($ref, ["name"]);
export type TRef = _.Record<(typeof $ref), [symbol], _ptr>;
export const TCompound = _.Record.makeConstructor<{"ctor": ConstructorSpec, "members": _.KeyedDictionary<_val, Template, _ptr>}, _ptr>()($compound, ["ctor","members"]);
export type TCompound = _.Record<
(typeof $compound),
[ConstructorSpec, _.KeyedDictionary<_val, Template, _ptr>],
_ptr
>;
export type _ptr = _i_Actor.Ref;
export type _val = _.Value<_ptr>;
export function isSturdyRef(v: any): v is SturdyRef {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $ref) &&
(
(v.length === 3) &&
true &&
(
_.Array.isArray(v[1]) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[1]) &&
(v[1].length >= 0) &&
v[1].slice(0).every(v => (isAttenuation(v)))
) &&
_.Bytes.isBytes(v[2])
)
);
}
export function asSturdyRef(v: any): SturdyRef {
if (!isSturdyRef(v)) {throw new TypeError(`Invalid SturdyRef: ${_.stringify(v)}`);} else {return v;};
}
export function isAttenuation(v: any): v is Attenuation {
return (
_.Array.isArray(v) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
(v.length >= 0) &&
v.slice(0).every(v => (isCaveat(v)))
);
}
export function asAttenuation(v: any): Attenuation {
if (!isAttenuation(v)) {throw new TypeError(`Invalid Attenuation: ${_.stringify(v)}`);} else {return v;};
}
export function isCaveat(v: any): v is Caveat {return (isRewrite(v) || isAlts(v));}
export function asCaveat(v: any): Caveat {
if (!isCaveat(v)) {throw new TypeError(`Invalid Caveat: ${_.stringify(v)}`);} else {return v;};
}
export function isRewrite(v: any): v is Rewrite {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $rewrite) &&
((v.length === 2) && isPattern(v[0]) && isTemplate(v[1]))
);
}
export function asRewrite(v: any): Rewrite {
if (!isRewrite(v)) {throw new TypeError(`Invalid Rewrite: ${_.stringify(v)}`);} else {return v;};
}
export function isAlts(v: any): v is Alts {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $or) &&
(
(v.length === 1) &&
(
_.Array.isArray(v[0]) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0]) &&
(v[0].length >= 0) &&
v[0].slice(0).every(v => (isRewrite(v)))
)
)
);
}
export function asAlts(v: any): Alts {
if (!isAlts(v)) {throw new TypeError(`Invalid Alts: ${_.stringify(v)}`);} else {return v;};
}
export function isResolve(v: any): v is Resolve {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $resolve) &&
((v.length === 2) && isSturdyRef(v[0]) && _.isPointer(v[1]))
);
}
export function asResolve(v: any): Resolve {
if (!isResolve(v)) {throw new TypeError(`Invalid Resolve: ${_.stringify(v)}`);} else {return v;};
}
export function isConstructorSpec(v: any): v is ConstructorSpec {return (isCRec(v) || isCArr(v) || isCDict(v));}
export function asConstructorSpec(v: any): ConstructorSpec {
if (!isConstructorSpec(v)) {throw new TypeError(`Invalid ConstructorSpec: ${_.stringify(v)}`);} else {return v;};
}
export function isCRec(v: any): v is CRec {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $rec) &&
((v.length === 2) && true && typeof v[1] === 'number')
);
}
export function asCRec(v: any): CRec {
if (!isCRec(v)) {throw new TypeError(`Invalid CRec: ${_.stringify(v)}`);} else {return v;};
}
export function isCArr(v: any): v is CArr {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $arr) &&
((v.length === 1) && typeof v[0] === 'number')
);
}
export function asCArr(v: any): CArr {
if (!isCArr(v)) {throw new TypeError(`Invalid CArr: ${_.stringify(v)}`);} else {return v;};
}
export function isCDict(v: any): v is CDict {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $dict) &&
((v.length === 0))
);
}
export function asCDict(v: any): CDict {
if (!isCDict(v)) {throw new TypeError(`Invalid CDict: ${_.stringify(v)}`);} else {return v;};
}
export function isLit(v: any): v is Lit {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $lit) &&
((v.length === 1) && true)
);
}
export function asLit(v: any): Lit {
if (!isLit(v)) {throw new TypeError(`Invalid Lit: ${_.stringify(v)}`);} else {return v;};
}
export function isPattern(v: any): v is Pattern {
return (
isPDiscard(v) ||
isPBind(v) ||
isPAnd(v) ||
isPNot(v) ||
isLit(v) ||
isPCompound(v)
);
}
export function asPattern(v: any): Pattern {
if (!isPattern(v)) {throw new TypeError(`Invalid Pattern: ${_.stringify(v)}`);} else {return v;};
}
export function isPDiscard(v: any): v is PDiscard {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $__) &&
((v.length === 0))
);
}
export function asPDiscard(v: any): PDiscard {
if (!isPDiscard(v)) {throw new TypeError(`Invalid PDiscard: ${_.stringify(v)}`);} else {return v;};
}
export function isPBind(v: any): v is PBind {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $bind) &&
((v.length === 2) && typeof v[0] === 'symbol' && isPattern(v[1]))
);
}
export function asPBind(v: any): PBind {
if (!isPBind(v)) {throw new TypeError(`Invalid PBind: ${_.stringify(v)}`);} else {return v;};
}
export function isPAnd(v: any): v is PAnd {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $and) &&
(
(v.length === 1) &&
(
_.Array.isArray(v[0]) &&
!_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v[0]) &&
(v[0].length >= 0) &&
v[0].slice(0).every(v => (isPattern(v)))
)
)
);
}
export function asPAnd(v: any): PAnd {
if (!isPAnd(v)) {throw new TypeError(`Invalid PAnd: ${_.stringify(v)}`);} else {return v;};
}
export function isPNot(v: any): v is PNot {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $not) &&
((v.length === 1) && isPattern(v[0]))
);
}
export function asPNot(v: any): PNot {
if (!isPNot(v)) {throw new TypeError(`Invalid PNot: ${_.stringify(v)}`);} else {return v;};
}
export function isPCompound(v: any): v is PCompound {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $compound) &&
(
(v.length === 2) &&
isConstructorSpec(v[0]) &&
(
_.Dictionary.isDictionary<_val, _ptr>(v[1]) &&
((() => {
for (const e of v[1]) {if (!(true)) return false; if (!(isPattern(e[1]))) return false;};
return true;
})())
)
)
);
}
export function asPCompound(v: any): PCompound {
if (!isPCompound(v)) {throw new TypeError(`Invalid PCompound: ${_.stringify(v)}`);} else {return v;};
}
export function isTemplate(v: any): v is Template {return (isTRef(v) || isLit(v) || isTCompound(v));}
export function asTemplate(v: any): Template {
if (!isTemplate(v)) {throw new TypeError(`Invalid Template: ${_.stringify(v)}`);} else {return v;};
}
export function isTRef(v: any): v is TRef {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $ref) &&
((v.length === 1) && typeof v[0] === 'symbol')
);
}
export function asTRef(v: any): TRef {
if (!isTRef(v)) {throw new TypeError(`Invalid TRef: ${_.stringify(v)}`);} else {return v;};
}
export function isTCompound(v: any): v is TCompound {
return (
_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v) &&
_.is(v.label, $compound) &&
(
(v.length === 2) &&
isConstructorSpec(v[0]) &&
(
_.Dictionary.isDictionary<_val, _ptr>(v[1]) &&
((() => {
for (const e of v[1]) {if (!(true)) return false; if (!(isTemplate(e[1]))) return false;};
return true;
})())
)
)
);
}
export function asTCompound(v: any): TCompound {
if (!isTCompound(v)) {throw new TypeError(`Invalid TCompound: ${_.stringify(v)}`);} else {return v;};
}

View File

@ -1,12 +1,13 @@
import { Actor, Assertion, Turn } from './actor.js';
import { Actor, Assertion, Ref, Turn } from './actor.js';
import { Dictionary, Record } from '@preserves/core';
import { Dataspace, Observe } from './dataspace.js';
import { Worker } from 'worker_threads';
import { Relay, spawnRelay } from './relay.js';
import { BoxState, SetBox } from './gen/box-protocol.js';
import { attenuate, CRec, Lit, Pattern, PCompound, rfilter } from './rewrite.js';
import path from 'path';
import { BoxState, SetBox } from './gen/box-protocol.js';
const Instance = Record.makeConstructor<{moduleName: string, arg: Assertion}>()(
Symbol.for('Instance'), ['moduleName', 'arg']);
@ -60,7 +61,7 @@ Turn.for(new Actor(), async (t: Turn) => {
new Dictionary()),
PCompound(CRec(Observe.constructorInfo.label,
Observe.constructorInfo.arity),
new Dictionary<Pattern, never>([
new Dictionary<Pattern, Ref>([
[0, Lit(SetBox.constructorInfo.label)]]))));
const ds_for_client = attenuate(
@ -70,7 +71,7 @@ Turn.for(new Actor(), async (t: Turn) => {
new Dictionary()),
PCompound(CRec(Observe.constructorInfo.label,
Observe.constructorInfo.arity),
new Dictionary<Pattern, never>([
new Dictionary<Pattern, Ref>([
[0, Lit(BoxState.constructorInfo.label)]]))));

View File

@ -1,77 +1,17 @@
import { Handle, Ref } from './actor.js';
import { Attenuation, Pattern, Template } from './rewrite.js';
import { Record, Value } from '@preserves/core';
import { Attenuation } from './gen/sturdy.js';
import * as IO from './gen/protocol.js';
import { Ref } from './actor.js';
export const _Assert = Symbol.for('assert');
export const _Retract = Symbol.for('retract');
export const _Message = Symbol.for('message');
export const _Sync = Symbol.for('sync');
function mk<T extends object>() {
return {
Assert: Record.makeConstructor<{assertion: Value<T>, handle: Handle}, T>()(
_Assert, ['assertion', 'handle']),
Retract: Record.makeConstructor<{handle: Handle}, T>()(
_Retract, ['handle']),
Message: Record.makeConstructor<{body: Value<T>}, T>()(
_Message, ['body']),
Sync: Record.makeConstructor<{peer: T}, T>()(
_Sync, ['peer']),
};
}
export type EntityMessage<T extends object> =
| Record<typeof _Assert, [Value<T>, Handle], T>
| Record<typeof _Retract, [Handle], T>
| Record<typeof _Message, [Value<T>], T>
| Record<typeof _Sync, [T], T>;
export type TurnMessage<T extends object> = Array<[Oid, EntityMessage<T>]>;
export type Oid = number;
export type WireSymbol = { oid: Oid, ref: Ref, count: number };
export type WireSymbol = { oid: IO.Oid, ref: Ref, count: number };
export type WireRef =
| { loc: "mine", oid: Oid }
| { loc: "your", oid: Oid, attenuation: EncodedAttenuation };
| { loc: "mine", oid: IO.Oid }
| { loc: "your", oid: IO.Oid, attenuation: Attenuation };
export const IO = mk<WireRef>();
export function myRef(oid: Oid): WireRef & { loc: "mine" } {
export function myRef(oid: IO.Oid): WireRef & { loc: "mine" } {
return { loc: 'mine', oid };
}
export function yourRef(oid: Oid, attenuation: EncodedAttenuation): WireRef & { loc: "your" } {
export function yourRef(oid: IO.Oid, attenuation: Attenuation): WireRef & { loc: "your" } {
return { loc: 'your', oid, attenuation };
}
export type EncodedAttenuation = Array<Array<[Value<WireRef>, Value<WireRef>]>>;
export function encodeAttenuation(a: Attenuation | undefined): EncodedAttenuation {
if (a === void 0) return [];
return a.map(s => s.map(({pattern, template}) => [
pattern as Value<WireRef>,
template as Value<WireRef>,
]));
}
export function decodeAttenuation(v: Array<Value<WireRef>>): Attenuation {
function complain(): never {
throw new Error(
`Received invalid attenuation ${v.asPreservesText()} from peer`);
}
if (v.length === 0) return [];
return v.map(s => {
if (!Array.isArray(s)) complain();
return s.map(e => {
if (!(Array.isArray(e) && e.length === 2)) complain();
// TODO: check structure of pattern and template
return {
pattern: e[0] as Pattern,
template: e[1] as Template,
};
});
});
}

View File

@ -1,24 +1,11 @@
import { Actor, Assertion, Entity, Handle, Ref, Turn } from './actor.js';
import { BytesLike, canonicalString, Decoder, encode, FlexMap, IdentityMap, mapPointers, underlying, Value } from '@preserves/core';
import {
EncodedAttenuation,
EntityMessage,
IO,
Oid,
TurnMessage,
WireRef,
WireSymbol,
_Assert,
_Message,
_Retract,
_Sync,
decodeAttenuation,
encodeAttenuation,
myRef,
yourRef,
} from './protocol.js';
import * as IO from './gen/protocol.js';
import { myRef, WireRef, WireSymbol, yourRef } from './protocol.js';
import { queueTask } from './task.js';
import { attenuate } from './rewrite.js';
import { asAttenuation, Attenuation } from './gen/sturdy.js';
import { pointerNotAllowed } from './sturdy.js';
export class SyncPeerEntity implements Entity {
readonly relay: Relay;
@ -53,14 +40,14 @@ export class SyncPeerEntity implements Entity {
export class RelayEntity implements Entity {
readonly relay: Relay;
readonly oid: Oid;
readonly oid: IO.Oid;
constructor(relay: Relay, oid: Oid) {
constructor(relay: Relay, oid: IO.Oid) {
this.relay = relay;
this.oid = oid;
}
send(m: EntityMessage<WireRef>): void {
send(m: IO.Event): void {
this.relay.send(this.oid, m);
}
@ -87,7 +74,7 @@ export class RelayEntity implements Entity {
}
export class Membrane {
readonly byOid = new IdentityMap<Oid, WireSymbol>();
readonly byOid = new IdentityMap<IO.Oid, WireSymbol>();
readonly byRef = new IdentityMap<Ref, WireSymbol>();
grab<Table extends "byOid" | "byRef">(table: Table,
@ -142,8 +129,8 @@ export class Relay {
readonly outboundAssertions = new IdentityMap<Handle, Array<WireSymbol>>();
readonly exported = new Membrane();
readonly imported = new Membrane();
nextLocalOid: Oid = 0;
pendingTurn: TurnMessage<WireRef> = [];
nextLocalOid: IO.Oid = 0;
pendingTurn: IO.Turn = [];
debug: boolean;
trustPeer: boolean;
@ -157,14 +144,13 @@ export class Relay {
if (!(Array.isArray(v) && v.length >= 2 && typeof v[1] === 'number')) {
complain();
}
const oid = v[1] as Oid;
const oid = v[1] as IO.Oid;
switch (v[0]) {
case 0:
if (v.length > 2) complain();
return myRef(oid);
case 1:
// TODO: check EncodedAttenuation
return yourRef(oid, v.slice(2) as EncodedAttenuation);
return yourRef(oid, asAttenuation(v.slice(2)));
default:
complain();
}
@ -212,7 +198,7 @@ export class Relay {
// This reference has been attenuated since it was sent to us.
// Do we trust the peer to enforce such attenuation on our behalf?
if (this.trustPeer) {
return yourRef(r.target.oid, encodeAttenuation(r.attenuation));
return yourRef(r.target.oid, r.attenuation);
} else {
// fall through: treat the attenuated ref as a local ref, and re-export it.
}
@ -239,13 +225,13 @@ export class Relay {
if (n.attenuation.length === 0 || r === INERT_REF) {
return r;
} else {
type AttenuatedRef = Ref & { __attenuations?: FlexMap<EncodedAttenuation, Ref> };
type AttenuatedRef = Ref & { __attenuations?: FlexMap<Attenuation, Ref> };
const ar = r as AttenuatedRef;
if (ar.__attenuations === void 0) {
ar.__attenuations = new FlexMap(canonicalString);
}
return ar.__attenuations.getOrSet(n.attenuation, () =>
attenuate(r, ... decodeAttenuation(n.attenuation)));
attenuate(r, ... n.attenuation));
}
}
case 'mine': {
@ -257,7 +243,7 @@ export class Relay {
}
}
send(remoteOid: Oid, m: EntityMessage<WireRef>): void {
send(remoteOid: IO.Oid, m: IO.Event): void {
if (this.pendingTurn.length === 0) {
queueTask(() => {
if (this.debug) console.log('OUT', this.pendingTurn.asPreservesText());
@ -266,7 +252,8 @@ export class Relay {
encodePointer: n => {
switch (n.loc) {
case 'mine': return [0, n.oid];
case 'your': return [1, n.oid, ... n.attenuation];
case 'your': return [1, n.oid, ... mapPointers<Ref, IO._ptr>(
n.attenuation, pointerNotAllowed) as Array<IO._val>];
}
},
})));
@ -276,7 +263,7 @@ export class Relay {
this.pendingTurn.push([remoteOid, m]);
}
lookupLocal(localOid: Oid): Ref {
lookupLocal(localOid: IO.Oid): Ref {
return this.exported.byOid.get(localOid)?.ref ?? INERT_REF;
}
@ -284,7 +271,7 @@ export class Relay {
Turn.for(this.actor, t => {
this.decoder.write(bs);
while (true) {
const wireTurn = this.decoder.try_next() as (TurnMessage<WireRef> | undefined);
const wireTurn = this.decoder.try_next() as (IO.Turn | undefined);
if (wireTurn === void 0) break;
// TODO: deep check that wireTurn really is a TurnMessage
if (this.debug) console.log('IN', wireTurn.asPreservesText());
@ -296,9 +283,9 @@ export class Relay {
});
}
handle(t: Turn, r: Ref, m: EntityMessage<WireRef>) {
handle(t: Turn, r: Ref, m: IO.Event) {
switch (m.label) {
case _Assert: {
case IO.$assert: {
const [a, imported] = this.rewriteIn(t, IO.Assert._.assertion(m));
this.inboundAssertions.set(IO.Assert._.handle(m), {
localHandle: t.assert(r, a),
@ -306,7 +293,7 @@ export class Relay {
});
break;
}
case _Retract: {
case IO.$retract: {
const remoteHandle = IO.Retract._.handle(m);
const h = this.inboundAssertions.get(remoteHandle);
if (h === void 0) throw new Error(`Peer retracted invalid handle ${remoteHandle}`);
@ -315,13 +302,13 @@ export class Relay {
t.retract(h.localHandle);
break;
}
case _Message: {
case IO.$message: {
const [a, imported] = this.rewriteIn(t, IO.Message._.body(m));
if (imported.length > 0) throw new Error("Cannot receive transient reference");
t.message(r, a);
break;
}
case _Sync: {
case IO.$sync: {
const imported: Array<WireSymbol> = [];
const k = this.rewriteRefIn(t, IO.Sync._.peer(m), imported);
t.sync(r).then(t => {
@ -335,12 +322,12 @@ export class Relay {
}
export interface RelayActorOptions extends RelayOptions {
initialOid?: Oid;
initialOid?: IO.Oid;
initialRef?: Ref;
nextLocalOid?: Oid;
nextLocalOid?: IO.Oid;
}
export function spawnRelay(t: Turn, options: RelayActorOptions & {initialOid: Oid}): Promise<Ref>;
export function spawnRelay(t: Turn, options: RelayActorOptions & {initialOid: IO.Oid}): Promise<Ref>;
export function spawnRelay(t: Turn, options: Omit<RelayActorOptions, 'initialOid'>): Promise<null>;
export function spawnRelay(t: Turn, options: RelayActorOptions): Promise<Ref | null>
{

View File

@ -1,124 +1,59 @@
import type { Assertion, Bindings, Handle, Ref, Turn } from "./actor.js";
import { Dictionary, IdentityMap, is, Record, Tuple, Value } from "@preserves/core";
import { Dictionary, IdentityMap, is, Record, Tuple } from "@preserves/core";
export type Attenuation = Array<RewriteStage>; // array of stages, each a list of alternatives
export type RewriteStage = Array<Rewrite>;
export type Rewrite = { pattern: Pattern, template: Template };
import * as S from './gen/sturdy.js';
export * from './gen/sturdy.js';
export const _CRec = Symbol.for('rec');
export const CRec = Record.makeConstructor<{label: Value<never>, arity: number}, never>()(
_CRec, ['label', 'arity']);
export const _CArr = Symbol.for('arr');
export const CArr = Record.makeConstructor<{arity: number}, never>()(
_CArr, ['arity']);
export const _CDict = Symbol.for('dict');
export const CDict = Record.makeConstructor<{}, never>()(
_CDict, []);
export type ConstructorSpec =
| Record<typeof _CRec, [Value<never>, number], never>
| Record<typeof _CArr, [number], never>
| Record<typeof _CDict, [], never>;
export const _PDiscard = Symbol.for('_');
export const PDiscard = Record.makeConstructor<{}, never>()(
_PDiscard, []);
export const _PBind = Symbol.for('bind');
export const PBind = Record.makeConstructor<{name: symbol, pattern: Pattern}, never>()(
_PBind, ['name', 'pattern']);
export const _PAnd = Symbol.for('and');
export const PAnd = Record.makeConstructor<{patterns: Array<Pattern>}, never>()(
_PAnd, ['patterns']);
export const _PNot = Symbol.for('not');
export const PNot = Record.makeConstructor<{pattern: Pattern}, never>()(
_PNot, ['pattern']);
export const _Lit = Symbol.for('lit');
export const Lit = Record.makeConstructor<{value: Value<never>}, never>()(
_Lit, ['value']);
export const _PCompound = Symbol.for('compound');
export const PCompound =
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Pattern, never>}, never>()(
_PCompound, ['ctor', 'members']);
export type Pattern =
| Record<typeof _PDiscard, [], never>
| Record<typeof _PBind, [symbol, Pattern], never>
| Record<typeof _PAnd, [Pattern[]], never>
| Record<typeof _PNot, [Pattern], never>
| Record<typeof _Lit, [Value<never>], never>
| Record<typeof _PCompound, [ConstructorSpec, Dictionary<Pattern, never>], never>;
export const _TRef = Symbol.for('ref');
export const TRef = Record.makeConstructor<{name: symbol}, never>()(
_TRef, ['name']);
export const _TCompound = Symbol.for('compound');
export const TCompound =
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Template, never>}, never>()(
_TCompound, ['ctor', 'members']);
export type Template =
| Record<typeof _TRef, [symbol], never>
| Record<typeof _Lit, [Value<never>], never>
| Record<typeof _TCompound, [ConstructorSpec, Dictionary<Template, never>], never>;
export function match(p: Pattern, v: Assertion): Bindings | null {
export function match(p: S.Pattern, v: Assertion): Bindings | null {
let bindings: Bindings = {};
function walk(p: Pattern, v: Assertion): boolean {
function walk(p: S.Pattern, v: Assertion): boolean {
switch (p.label) {
case _PDiscard:
case S.$__:
return true;
case _PBind:
if (walk(PBind._.pattern(p), v)) {
bindings[PBind._.name(p).asPreservesText()] = v;
case S.$bind:
if (walk(S.PBind._.pattern(p), v)) {
bindings[S.PBind._.name(p).asPreservesText()] = v;
return true;
}
return false;
case _PAnd:
for (const pp of PAnd._.patterns(p)) {
case S.$and:
for (const pp of S.PAnd._.patterns(p)) {
if (!walk(pp, v)) return false;
}
return true;
case _PNot: {
case S.$not: {
const savedBindings = bindings;
bindings = {};
const result = !walk(PNot._.pattern(p), v)
const result = !walk(S.PNot._.pattern(p), v)
bindings = savedBindings;
return result;
}
case _Lit:
return is(Lit._.value(p), v);
case _PCompound: {
const ctor = PCompound._.ctor(p);
const members = PCompound._.members(p);
case S.$lit:
return is(S.Lit._.value(p), v);
case S.$compound: {
const ctor = S.PCompound._.ctor(p);
const members = S.PCompound._.members(p);
switch (ctor.label) {
case _CRec:
case S.$rec:
if (!Record.isRecord<Assertion, Tuple<Assertion>, Ref>(v)) return false;
if (!is(CRec._.label(ctor), v.label)) return false;
if (CRec._.arity(ctor) !== v.length) return false;
if (!is(S.CRec._.label(ctor), v.label)) return false;
if (S.CRec._.arity(ctor) !== v.length) return false;
for (const [key, pp] of members) {
if (typeof key !== 'number') return false;
if (!walk(pp, v[key])) return false;
}
return true;
case _CArr:
case S.$arr:
if (!Array.isArray(v)) return false;
if ('label' in v) return false;
if (CArr._.arity(ctor) !== v.length) return false;
if (S.CArr._.arity(ctor) !== v.length) return false;
for (const [key, pp] of members) {
if (typeof key !== 'number') return false;
if (!walk(pp, v[key])) return false;
}
return true;
case _CDict:
case S.$dict:
if (!Dictionary.isDictionary<Assertion, Ref>(v)) return false;
for (const [key, pp] of members) {
const vv = v.get(key as Assertion);
@ -134,41 +69,41 @@ export function match(p: Pattern, v: Assertion): Bindings | null {
return walk(p, v) ? bindings : null;
}
export function instantiate(t: Template, b: Bindings): Assertion {
function walk(t: Template): Assertion {
export function instantiate(t: S.Template, b: Bindings): Assertion {
function walk(t: S.Template): Assertion {
switch (t.label) {
case _TRef: {
const n = TRef._.name(t).asPreservesText()
case S.$ref: {
const n = S.TRef._.name(t).asPreservesText()
const v = b[n];
if (v === void 0) throw new Error(`Unbound reference: ${n}`);
return v;
}
case _Lit:
return Lit._.value(t) as Assertion;
case _TCompound: {
const ctor = TCompound._.ctor(t);
const members = TCompound._.members(t);
case S.$lit:
return S.Lit._.value(t) as Assertion;
case S.$compound: {
const ctor = S.TCompound._.ctor(t);
const members = S.TCompound._.members(t);
switch (ctor.label) {
case _CRec: {
case S.$rec: {
const v = Record(
CRec._.label(ctor) as Assertion,
S.CRec._.label(ctor) as Assertion,
[] as Assertion[],
);
v.length = CRec._.arity(ctor);
v.length = S.CRec._.arity(ctor);
for (const [key, tt] of members) {
v[key as number] = walk(tt);
}
return v;
}
case _CArr: {
case S.$arr: {
const v = [];
v.length = CArr._.arity(ctor);
v.length = S.CArr._.arity(ctor);
for (const [key, tt] of members) {
v[key as number] = walk(tt);
}
return v;
}
case _CDict: {
case S.$dict: {
const v = new Dictionary<Assertion, Ref>();
for (const [key, tt] of members) {
v.set(key as Assertion, walk(tt));
@ -183,21 +118,25 @@ export function instantiate(t: Template, b: Bindings): Assertion {
return walk(t);
}
export function rewrite(r: Rewrite, v: Assertion): Assertion | null {
const bindings = match(r.pattern, v);
export function rewrite(r: S.Rewrite, v: Assertion): Assertion | null {
const bindings = match(S.Rewrite._.pattern(r), v);
if (bindings === null) return null;
return instantiate(r.template, bindings);
return instantiate(S.Rewrite._.template(r), bindings);
}
export function examineAlternatives(alternatives: RewriteStage, v: Assertion): Assertion | null {
for (const r of alternatives) {
const w = rewrite(r, v);
if (w !== null) return w;
export function examineAlternatives(cav: S.Caveat, v: Assertion): Assertion | null {
if (cav.label === S.$or) {
for (const r of S.Alts._.alternatives(cav)) {
const w = rewrite(r, v);
if (w !== null) return w;
}
return null;
} else {
return rewrite(cav, v);
}
return null;
}
export function runRewrites(a: Attenuation | undefined, v: Assertion): Assertion | null {
export function runRewrites(a: S.Attenuation | undefined, v: Assertion): Assertion | null {
if (a !== void 0) {
for (const stage of a) {
const w = examineAlternatives(stage, v);
@ -210,11 +149,12 @@ export function runRewrites(a: Attenuation | undefined, v: Assertion): Assertion
const _a = Symbol.for('a');
export function rfilter(... patterns: Pattern[]): RewriteStage {
return patterns.map(p => ({ pattern: PBind(_a, p), template: TRef(_a) }));
export function rfilter(... patterns: S.Pattern[]): S.Caveat {
const ps = patterns.map(p => S.Rewrite(S.PBind(_a, p), S.TRef(_a)));
return ps.length === 1 ? ps[0] : S.Alts(ps);
}
export function attenuate(ref: Ref, ... a: Attenuation): Ref {
export function attenuate(ref: Ref, ... a: S.Attenuation): Ref {
return { ... ref, attenuation: [... a, ... (ref.attenuation ?? [])] };
}

View File

@ -1,7 +1,7 @@
import { Actor, Assertion, Ref, Turn } from "./actor.js";
import { Relay, spawnRelay } from "./relay.js";
import { sturdyDecode } from "./sturdy.js";
import { Observe } from "./dataspace.js";
import { Actor, Ref, Turn } from "./actor";
import { Relay, spawnRelay } from "./relay";
import { sturdyDecode } from "./sturdy";
import { Resolve, asSturdyRef } from "./gen/sturdy";
import * as net from 'net';
import { Bytes } from "@preserves/core";
@ -29,7 +29,7 @@ const socket = net.createConnection({ port: 5999, host: 'localhost' }, () => {
// debug: true,
}).then(gatekeeper => import(moduleName).then(m => t.freshen(t => {
t.assert(shutdownRef, true);
t.assert(gatekeeper, Observe(cap as Assertion, t.ref({
t.assert(gatekeeper, Resolve(asSturdyRef(cap), t.ref({
assert(t, ds) {
m.default(t, ds);
}

View File

@ -1,12 +1,13 @@
import { Actor, Handle, Turn } from './actor.js';
import { Dataspace, Observe } from './dataspace.js';
import { Dataspace } from './dataspace.js';
import { Relay, spawnRelay } from './relay.js';
import * as net from 'net';
import { Caveat, mint, Or, Rewrite, sturdyEncode, SturdyRef, validate, _Or, _Rewrite } from './sturdy.js';
import { mint, sturdyEncode, SturdyRef, validate, Resolve } from './sturdy.js';
import { KEY_LENGTH } from './cryptography.js';
import { attenuate, Attenuation } from './rewrite.js';
import { attenuate } from './rewrite.js';
import { Bytes, IdentityMap } from '@preserves/core';
import { Attenuation, isResolve } from './gen/sturdy.js';
const secretKey = new Bytes(KEY_LENGTH);
mint('syndicate', secretKey).then(v => {
@ -14,15 +15,6 @@ mint('syndicate', secretKey).then(v => {
console.log(sturdyEncode(v).toHex());
});
function internalize(caveatChain: Caveat[][]): Attenuation {
const a: Attenuation = [];
caveatChain.slice().reverse().forEach(cs => a.push(... cs.map(c => {
const alts = c.label === _Rewrite ? [c] : Or<Rewrite>()._.alternatives(c);
return alts.map(r => ({ pattern: Rewrite._.pattern(r), template: Rewrite._.template(r) }));
})));
return a;
}
Turn.for(new Actor(), t => {
const ds = t.ref(new Dataspace());
@ -41,19 +33,18 @@ Turn.for(new Actor(), t => {
initialRef: t.ref({
handleMap: new IdentityMap<Handle, Handle>(),
async assert(t, a, h) {
if (!Observe.isClassOf(a)) return;
const r = Observe._.label(a);
if (!SturdyRef.isClassOf(r)) return;
if (!isResolve(a)) return;
const r = Resolve._.sturdyref(a);
if (!await validate(r, secretKey)) {
console.warn(`Invalid SturdyRef: ${r.asPreservesText()}`);
return;
}
const attenuated_ds = attenuate(
ds,
... internalize(SturdyRef._.caveatChain(r)));
const cavs: Attenuation = [];
SturdyRef._.caveatChain(r).forEach(cs => cavs.push(... cs));
const attenuated_ds = attenuate(ds, ... cavs);
t.freshen(t => this.handleMap.set(
h,
t.assert(Observe._.observer(a), attenuated_ds)));
t.assert(Resolve._.observer(a), attenuated_ds)));
},
retract(t, h) {
t.retract(this.handleMap.get(h));

View File

@ -2,15 +2,17 @@ import { newKey } from './cryptography.js';
import { attenuate, KEY_LENGTH, mint, Rewrite, sturdyEncode, validate } from './sturdy.js';
import * as RW from './rewrite.js';
import { Bytes, Dictionary } from '@preserves/core';
import { Ref } from 'actor.js';
async function main() {
const m1 = await mint('hello world', new Bytes(KEY_LENGTH));
console.log(m1.asPreservesText());
const m2 = await attenuate(m1, Rewrite(RW.PBind(Symbol.for('a'),
RW.PCompound(RW.CRec(Symbol.for('says'), 2),
new Dictionary<RW.Pattern, never>([
[0, RW.Lit('Tony')]]))),
RW.TRef(Symbol.for('a'))));
const m2 = await attenuate(m1, Rewrite(
RW.PBind(Symbol.for('a'),
RW.PCompound(RW.CRec(Symbol.for('says'), 2),
new Dictionary<RW.Pattern, Ref>([
[0, RW.Lit('Tony')]]))),
RW.TRef(Symbol.for('a'))));
console.log(m2.asPreservesText());
console.log('should be true:', await validate(m1, new Bytes(KEY_LENGTH)));
console.log('should be true:', await validate(m2, new Bytes(KEY_LENGTH)));

View File

@ -7,69 +7,48 @@
// California: Internet Society, 2014.
import { mac } from './cryptography.js';
import { Bytes, decode, encode, is, Record, Value } from '@preserves/core';
import type { Pattern, Template } from './rewrite.js';
import { Bytes, decode, encode, is } from '@preserves/core';
import * as S from './gen/sturdy.js';
export * from './gen/sturdy.js';
export type EmbeddedRef = never;
export type SturdyValue = Value<EmbeddedRef>;
export const _SturdyRef = Symbol.for('sturdyref');
export const SturdyRef = Record.makeConstructor<{
oid: SturdyValue, // (arbitrary) name of the ultimate target of the ref
caveatChain: Array<Caveat>[],
// ^ caveats/rewrites. Evaluated RIGHT-TO-LEFT; each Array<Caveat> is evaluated LEFT-TO-RIGHT
sig: Bytes, // *keyed* signature of canonicalEncode of rightmost item in [oid, ... caveatChain]
}, EmbeddedRef>()(_SturdyRef, ['oid', 'caveatChain', 'sig']);
export type SturdyRef = [SturdyValue, Array<Caveat>[], Bytes] & { label: typeof _SturdyRef };
export type Caveat = Or<Rewrite>;
// ^ embodies 1st-party caveats over assertion structure, but nothing else
// can add 3rd-party caveats and richer predicates later
export const _Or = Symbol.for('or');
export const Or = <T extends SturdyValue>() =>
Record.makeConstructor<{ alternatives: T[] }, EmbeddedRef>()(_Or, ['alternatives']);
export type Or<T extends SturdyValue> = T | Record<typeof _Or, [T[]], EmbeddedRef>;
export const _Rewrite = Symbol.for('rewrite');
export const Rewrite = Record.makeConstructor<{
pattern: Pattern,
template: Template,
}, EmbeddedRef>()(_Rewrite, ['pattern', 'template']);
export type Rewrite = ReturnType<typeof Rewrite>;
export type SturdyValue = S._val;
export const KEY_LENGTH = 16; // 128 bits
export function pointerNotAllowed(): never {
throw new Error("Embedded Ref not permitted in SturdyRef");
}
export function sturdyEncode(v: SturdyValue): Bytes {
return encode<EmbeddedRef>(v, {
return encode<S._ptr>(v, {
canonical: true,
includeAnnotations: false,
encodePointer() { throw new Error("EmbeddedRef not permitted in SturdyRef"); },
encodePointer: pointerNotAllowed,
});
}
export function sturdyDecode(bs: Bytes): SturdyValue {
return decode<EmbeddedRef>(bs, {
return decode<S._ptr>(bs, {
includeAnnotations: false,
decodePointer() { throw new Error("EmbeddedRef not permitted in SturdyRef"); },
decodePointer: pointerNotAllowed,
});
}
export async function mint(oid: SturdyValue, secretKey: Bytes): Promise<SturdyRef> {
return SturdyRef(oid, [], await mac(secretKey, sturdyEncode(oid)));
export async function mint(oid: SturdyValue, secretKey: Bytes): Promise<S.SturdyRef> {
return S.SturdyRef(oid, [], await mac(secretKey, sturdyEncode(oid)));
}
export async function attenuate(r: SturdyRef, ... a: Array<Caveat>): Promise<SturdyRef> {
return SturdyRef(
SturdyRef._.oid(r),
[... SturdyRef._.caveatChain(r), a],
await mac(SturdyRef._.sig(r), sturdyEncode(a))
export async function attenuate(r: S.SturdyRef, ... a: S.Attenuation): Promise<S.SturdyRef> {
return S.SturdyRef(
S.SturdyRef._.oid(r),
[... S.SturdyRef._.caveatChain(r), a],
await mac(S.SturdyRef._.sig(r), sturdyEncode(a))
);
}
export async function validate(r: SturdyRef, secretKey: Bytes): Promise<boolean> {
const sig = await SturdyRef._.caveatChain(r).reduce(
export async function validate(r: S.SturdyRef, secretKey: Bytes): Promise<boolean> {
const sig = await S.SturdyRef._.caveatChain(r).reduce(
async (sig, a) => mac(await sig, sturdyEncode(a)),
mac(secretKey, sturdyEncode(SturdyRef._.oid(r))));
return is(sig, SturdyRef._.sig(r));
mac(secretKey, sturdyEncode(S.SturdyRef._.oid(r))));
return is(sig, S.SturdyRef._.sig(r));
}