Better SturdyRef binding and lookup
This commit is contained in:
parent
cb8dae7ca4
commit
61174815eb
|
@ -16,6 +16,7 @@ Rewrite = <rewrite @pattern Pattern @template Template>.
|
||||||
Alts = <or [@alternatives Rewrite ...]>.
|
Alts = <or [@alternatives Rewrite ...]>.
|
||||||
|
|
||||||
Resolve = <resolve @sturdyref SturdyRef @observer ref>.
|
Resolve = <resolve @sturdyref SturdyRef @observer ref>.
|
||||||
|
Bind = <bind @oid any @key bytes @target ref>.
|
||||||
|
|
||||||
;---------------------------------------------------------------------------
|
;---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Assertion, Entity, Handle, LocalAction, Ref, Turn } from 'actor';
|
import { Assertion, Entity, Handle, LocalAction, Ref, Turn } from 'actor';
|
||||||
import { Dictionary, IdentityMap, is, preserves, Record, Tuple } from '@preserves/core';
|
import { Dictionary, IdentityMap, is, Record, Tuple } from '@preserves/core';
|
||||||
import { Bag, ChangeDescription } from './bag';
|
import { Bag, ChangeDescription } from './bag';
|
||||||
|
|
||||||
import { fromObserve, toObserve, Observe } from './gen/dataspace';
|
import { fromObserve, toObserve, Observe } from './gen/dataspace';
|
||||||
|
@ -87,16 +87,39 @@ export class Dataspace implements Partial<Entity> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function during(f: (t: Turn, a: Assertion) => (LocalAction | null)): Partial<Entity> {
|
export function during(f: (t: Turn, a: Assertion) => Promise<LocalAction | null>): Partial<Entity> {
|
||||||
const assertionMap = new Map<Handle, LocalAction>();
|
const assertionMap = new Map<Handle, LocalAction | 'dead'>();
|
||||||
return {
|
return {
|
||||||
assert(t: Turn, a: Assertion, h: Handle): void {
|
assert(t: Turn, a: Assertion, h: Handle): void {
|
||||||
const g = f(t, a);
|
f(t, a).then(g => {
|
||||||
if (g !== null) assertionMap.set(h, g);
|
if (g === null) g = _t => {};
|
||||||
|
switch (assertionMap.get(h)) {
|
||||||
|
case void 0:
|
||||||
|
assertionMap.set(h, g);
|
||||||
|
break;
|
||||||
|
case 'dead':
|
||||||
|
assertionMap.delete(h);
|
||||||
|
t.freshen(g);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error('during: Duplicate handle in assert: ' + h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
retract(t: Turn, h: Handle): void {
|
retract(t: Turn, h: Handle): void {
|
||||||
assertionMap.get(h)?.(t);
|
const g = assertionMap.get(h);
|
||||||
assertionMap.delete(h);
|
switch (g) {
|
||||||
|
case void 0:
|
||||||
|
assertionMap.set(h, 'dead');
|
||||||
|
break;
|
||||||
|
case 'dead':
|
||||||
|
console.error('during: Duplicate handle in retract: ' + h);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assertionMap.delete(h);
|
||||||
|
g(t);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ export type Alts = {"alternatives": Array<Rewrite>};
|
||||||
|
|
||||||
export type Resolve = {"sturdyref": SturdyRef, "observer": _ptr};
|
export type Resolve = {"sturdyref": SturdyRef, "observer": _ptr};
|
||||||
|
|
||||||
|
export type Bind = {"oid": _val, "key": _.Bytes, "target": _ptr};
|
||||||
|
|
||||||
export type ConstructorSpec = (
|
export type ConstructorSpec = (
|
||||||
{"_variant": "CRec", "value": CRec} |
|
{"_variant": "CRec", "value": CRec} |
|
||||||
{"_variant": "CArr", "value": CArr} |
|
{"_variant": "CArr", "value": CArr} |
|
||||||
|
@ -123,6 +125,8 @@ export function Alts(alternatives: Array<Rewrite>): Alts {return {"alternatives"
|
||||||
|
|
||||||
export function Resolve({sturdyref, observer}: {sturdyref: SturdyRef, observer: _ptr}): Resolve {return {"sturdyref": sturdyref, "observer": observer};}
|
export function Resolve({sturdyref, observer}: {sturdyref: SturdyRef, observer: _ptr}): Resolve {return {"sturdyref": sturdyref, "observer": observer};}
|
||||||
|
|
||||||
|
export function Bind({oid, key, target}: {oid: _val, key: _.Bytes, target: _ptr}): Bind {return {"oid": oid, "key": key, "target": target};}
|
||||||
|
|
||||||
export namespace ConstructorSpec {
|
export namespace ConstructorSpec {
|
||||||
export function CRec(value: CRec): ConstructorSpec {return {"_variant": "CRec", "value": value};};
|
export function CRec(value: CRec): ConstructorSpec {return {"_variant": "CRec", "value": value};};
|
||||||
export function CArr(value: CArr): ConstructorSpec {return {"_variant": "CArr", "value": value};};
|
export function CArr(value: CArr): ConstructorSpec {return {"_variant": "CArr", "value": value};};
|
||||||
|
@ -369,6 +373,36 @@ export function toResolve(v: _val): undefined | Resolve {
|
||||||
|
|
||||||
export function fromResolve(_v: Resolve): _val {return _.Record($resolve, [fromSturdyRef(_v["sturdyref"]), _v["observer"]]);}
|
export function fromResolve(_v: Resolve): _val {return _.Record($resolve, [fromSturdyRef(_v["sturdyref"]), _v["observer"]]);}
|
||||||
|
|
||||||
|
export function asBind(v: _val): Bind {
|
||||||
|
let result = toBind(v);
|
||||||
|
if (result === void 0) throw new TypeError(`Invalid Bind: ${_.stringify(v)}`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toBind(v: _val): undefined | Bind {
|
||||||
|
let result: undefined | Bind;
|
||||||
|
if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v)) {
|
||||||
|
let _tmp0: (null) | undefined;
|
||||||
|
_tmp0 = _.is(v.label, $bind) ? null : void 0;
|
||||||
|
if (_tmp0 !== void 0) {
|
||||||
|
let _tmp1: (_val) | undefined;
|
||||||
|
_tmp1 = v[0];
|
||||||
|
if (_tmp1 !== void 0) {
|
||||||
|
let _tmp2: (_.Bytes) | undefined;
|
||||||
|
_tmp2 = _.Bytes.isBytes(v[1]) ? v[1] : void 0;
|
||||||
|
if (_tmp2 !== void 0) {
|
||||||
|
let _tmp3: (_ptr) | undefined;
|
||||||
|
_tmp3 = _toPtr(v[2]);
|
||||||
|
if (_tmp3 !== void 0) {result = {"oid": _tmp1, "key": _tmp2, "target": _tmp3};};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fromBind(_v: Bind): _val {return _.Record($bind, [_v["oid"], _v["key"], _v["target"]]);}
|
||||||
|
|
||||||
export function asConstructorSpec(v: _val): ConstructorSpec {
|
export function asConstructorSpec(v: _val): ConstructorSpec {
|
||||||
let result = toConstructorSpec(v);
|
let result = toConstructorSpec(v);
|
||||||
if (result === void 0) throw new TypeError(`Invalid ConstructorSpec: ${_.stringify(v)}`);
|
if (result === void 0) throw new TypeError(`Invalid ConstructorSpec: ${_.stringify(v)}`);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Actor, Ref, Turn } from "./actor";
|
||||||
import { Relay, spawnRelay } from "./relay";
|
import { Relay, spawnRelay } from "./relay";
|
||||||
import { sturdyDecode } from "./sturdy";
|
import { sturdyDecode } from "./sturdy";
|
||||||
import { Resolve, asSturdyRef, fromResolve } from "./gen/sturdy";
|
import { Resolve, asSturdyRef, fromResolve } from "./gen/sturdy";
|
||||||
|
import { during } from "./dataspace";
|
||||||
|
|
||||||
import * as net from 'net';
|
import * as net from 'net';
|
||||||
import { Bytes } from "@preserves/core";
|
import { Bytes } from "@preserves/core";
|
||||||
|
@ -37,11 +38,10 @@ const socket = net.createConnection({ port: 5999, host: 'localhost' }, () => {
|
||||||
t.assert(shutdownRef, true);
|
t.assert(shutdownRef, true);
|
||||||
t.assert(gatekeeper, fromResolve(Resolve({
|
t.assert(gatekeeper, fromResolve(Resolve({
|
||||||
sturdyref: asSturdyRef(cap),
|
sturdyref: asSturdyRef(cap),
|
||||||
observer: t.ref({
|
observer: t.ref(during(async (t, ds) => {
|
||||||
assert(t, ds) {
|
const facet = t.facet(t => m.default(t, ds));
|
||||||
m.default(t, ds);
|
return t => t.stop(facet);
|
||||||
}
|
})),
|
||||||
})
|
|
||||||
})));
|
})));
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Assertion, Ref, Turn } from "./actor.js";
|
||||||
import { attachReadline } from './readline.js';
|
import { attachReadline } from './readline.js';
|
||||||
|
|
||||||
export default function (t: Turn, ds: Ref) {
|
export default function (t: Turn, ds: Ref) {
|
||||||
observe(t, ds, $joinedUser, during((t, j0) => {
|
observe(t, ds, $joinedUser, during(async (t, j0) => {
|
||||||
const j = asJoin(j0);
|
const j = asJoin(j0);
|
||||||
const facet = t.facet(t => runSession(t, j.uid, j.handle));
|
const facet = t.facet(t => runSession(t, j.uid, j.handle));
|
||||||
return t => t.stop(facet);
|
return t => t.stop(facet);
|
||||||
|
@ -37,7 +37,7 @@ function runSession(t: Turn, uid: UserId, session: Ref) {
|
||||||
updateUsername(t, 'user' + process.pid);
|
updateUsername(t, 'user' + process.pid);
|
||||||
|
|
||||||
const users = new Map<UserId, string>();
|
const users = new Map<UserId, string>();
|
||||||
observe(t, session, $user, during((_t, ui0) => {
|
observe(t, session, $user, during(async (_t, ui0) => {
|
||||||
const ui = asUserInfo(ui0);
|
const ui = asUserInfo(ui0);
|
||||||
const oldName = users.get(ui.uid);
|
const oldName = users.get(ui.uid);
|
||||||
console.log(oldName === void 0
|
console.log(oldName === void 0
|
||||||
|
|
|
@ -13,17 +13,28 @@ import {
|
||||||
fromUserInfo,
|
fromUserInfo,
|
||||||
} from "./gen/secure-chat-protocol.js";
|
} from "./gen/secure-chat-protocol.js";
|
||||||
import { Assertion, Handle, Ref, Turn } from "./actor.js";
|
import { Assertion, Handle, Ref, Turn } from "./actor.js";
|
||||||
import { observe, during, $Observe, asObserve } from "./dataspace.js";
|
import { observe, during, $Observe, asObserve, Dataspace } from "./dataspace.js";
|
||||||
import { attenuate, rfilter, pRec, pPointer, pString, pLit } from "./rewrite.js";
|
import { attenuate, rfilter, pRec, pPointer, pString, pLit } from "./rewrite.js";
|
||||||
|
import { attenuate as sturdyAttenuate, fromBind, Bind, KEY_LENGTH, sturdyEncode, fromSturdyRef, mint } from "./sturdy.js";
|
||||||
|
import { Bytes } from "@preserves/core";
|
||||||
|
|
||||||
export default function (t: Turn, ds: Ref) {
|
export default function (t: Turn, gatekeeperDs: Ref) {
|
||||||
let nextUserId: UserId = 0;
|
let nextUserId: UserId = 0;
|
||||||
|
|
||||||
// TODO: print out limited authority, only allowed to observe $joinedUser
|
const ds = t.ref(new Dataspace());
|
||||||
|
|
||||||
|
const chatOid = 'chat';
|
||||||
|
const chatKey = new Bytes(KEY_LENGTH);
|
||||||
|
t.assert(gatekeeperDs, fromBind(Bind({ oid: chatOid, key: chatKey, target: ds })));
|
||||||
|
mint(chatOid, chatKey).then(async r => {
|
||||||
|
r = await sturdyAttenuate(r, rfilter(pRec($Observe, pLit($joinedUser), pPointer())));
|
||||||
|
console.log(fromSturdyRef(r).asPreservesText());
|
||||||
|
console.log(sturdyEncode(fromSturdyRef(r)).toHex());
|
||||||
|
});
|
||||||
|
|
||||||
const nicks = new Map<string, UserId>();
|
const nicks = new Map<string, UserId>();
|
||||||
|
|
||||||
observe(t, ds, $Observe, during((t, o0) => {
|
observe(t, ds, $Observe, during(async (t, o0) => {
|
||||||
const o = asObserve(o0);
|
const o = asObserve(o0);
|
||||||
if (o.label !== $joinedUser) return null;
|
if (o.label !== $joinedUser) return null;
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,31 @@
|
||||||
import { Actor, Handle, Turn } from './actor.js';
|
import { Actor, Handle, Turn } from './actor.js';
|
||||||
import { Dataspace } from './dataspace.js';
|
import { Dataspace, during, observe } from './dataspace.js';
|
||||||
import { Relay, spawnRelay } from './relay.js';
|
import { Relay, spawnRelay } from './relay.js';
|
||||||
import * as net from 'net';
|
import * as net from 'net';
|
||||||
|
|
||||||
import { mint, sturdyEncode, validate } from './sturdy.js';
|
import { mint, sturdyEncode, validate } from './sturdy.js';
|
||||||
import { KEY_LENGTH } from './cryptography.js';
|
import { KEY_LENGTH } from './cryptography.js';
|
||||||
import { attenuate } from './rewrite.js';
|
import { attenuate } from './rewrite.js';
|
||||||
import { Bytes, IdentityMap } from '@preserves/core';
|
import { Bytes, is } from '@preserves/core';
|
||||||
import { Attenuation, fromSturdyRef, toResolve } from './gen/sturdy.js';
|
import { $bind, Attenuation, Bind, fromBind, fromSturdyRef, toBind, toResolve, _val } from './gen/sturdy.js';
|
||||||
|
|
||||||
const secretKey = new Bytes(KEY_LENGTH);
|
|
||||||
mint('syndicate', secretKey).then(v => {
|
|
||||||
console.log(fromSturdyRef(v).asPreservesText());
|
|
||||||
console.log(sturdyEncode(fromSturdyRef(v)).toHex());
|
|
||||||
});
|
|
||||||
|
|
||||||
new Actor(t => {
|
new Actor(t => {
|
||||||
t.activeFacet.preventInertCheck();
|
t.activeFacet.preventInertCheck();
|
||||||
|
|
||||||
const ds = t.ref(new Dataspace());
|
const ds = t.ref(new Dataspace());
|
||||||
|
|
||||||
|
const dsOid = 'syndicate';
|
||||||
|
const dsKey = new Bytes(KEY_LENGTH);
|
||||||
|
t.assert(ds, fromBind(Bind({
|
||||||
|
oid: dsOid,
|
||||||
|
key: dsKey,
|
||||||
|
target: ds,
|
||||||
|
})));
|
||||||
|
mint(dsOid, dsKey).then(v => {
|
||||||
|
console.log(fromSturdyRef(v).asPreservesText());
|
||||||
|
console.log(sturdyEncode(fromSturdyRef(v)).toHex());
|
||||||
|
});
|
||||||
|
|
||||||
function spawnConnection(t: Turn, socket: net.Socket) {
|
function spawnConnection(t: Turn, socket: net.Socket) {
|
||||||
console.log('connection', socket.remoteAddress, socket.remotePort);
|
console.log('connection', socket.remoteAddress, socket.remotePort);
|
||||||
spawnRelay(t, {
|
spawnRelay(t, {
|
||||||
|
@ -31,28 +38,26 @@ new Actor(t => {
|
||||||
socket.on('data', data => r.accept(data));
|
socket.on('data', data => r.accept(data));
|
||||||
t.activeFacet.actor.atExit(() => socket.destroy());
|
t.activeFacet.actor.atExit(() => socket.destroy());
|
||||||
},
|
},
|
||||||
initialRef: t.ref({
|
initialRef: t.ref(during(async (t, a0) => {
|
||||||
handleMap: new IdentityMap<Handle, Handle>(),
|
const a = toResolve(a0);
|
||||||
async assert(t, a0, h) {
|
if (a === void 0) return null;
|
||||||
const a = toResolve(a0);
|
const r = a.sturdyref;
|
||||||
if (a === void 0) return;
|
let facet = t.facet(t => {
|
||||||
const r = a.sturdyref;
|
observe(t, ds, $bind, during(async (t, b0) => {
|
||||||
if (!await validate(r, secretKey)) {
|
const b = toBind(b0);
|
||||||
console.warn(`Invalid SturdyRef: ${r.asPreservesText()}`);
|
if (b === void 0) return null;
|
||||||
return;
|
if (!is(r.oid, b.oid)) return null;
|
||||||
}
|
if (!await validate(r, b.key)) return null;
|
||||||
const cavs: Attenuation = [];
|
const cavs: Attenuation = [];
|
||||||
r.caveatChain.forEach(cs => cavs.push(... cs));
|
r.caveatChain.forEach(cs => cavs.push(... cs));
|
||||||
const attenuated_ds = attenuate(ds, ... cavs);
|
const attenuated_ds = attenuate(b.target, ... cavs);
|
||||||
t.freshen(t => this.handleMap.set(
|
let replyHandle: Handle | undefined;
|
||||||
h,
|
t.freshen(t => replyHandle = t.assert(a.observer, attenuated_ds));
|
||||||
t.assert(a.observer, attenuated_ds)));
|
return t => t.retract(replyHandle);
|
||||||
},
|
}));
|
||||||
retract(t, h) {
|
});
|
||||||
t.retract(this.handleMap.get(h));
|
return t => t.stop(facet);
|
||||||
this.handleMap.delete(h);
|
})),
|
||||||
}
|
|
||||||
}),
|
|
||||||
// debug: true,
|
// debug: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default function (t: Turn, ds: Ref) {
|
||||||
}
|
}
|
||||||
updateUsername(t, 'user' + process.pid);
|
updateUsername(t, 'user' + process.pid);
|
||||||
|
|
||||||
observe(t, ds, $Present, during((_t, e0) => {
|
observe(t, ds, $Present, during(async (_t, e0) => {
|
||||||
const e = asPresent(e0);
|
const e = asPresent(e0);
|
||||||
console.log(`${e.username} arrived`);
|
console.log(`${e.username} arrived`);
|
||||||
return (_t) => console.log(`${e.username} departed`);
|
return (_t) => console.log(`${e.username} departed`);
|
||||||
|
|
Loading…
Reference in New Issue