Better SturdyRef binding and lookup

This commit is contained in:
Tony Garnock-Jones 2021-04-19 13:00:08 +02:00
parent cb8dae7ca4
commit 61174815eb
8 changed files with 124 additions and 50 deletions

View File

@ -16,6 +16,7 @@ Rewrite = <rewrite @pattern Pattern @template Template>.
Alts = <or [@alternatives Rewrite ...]>.
Resolve = <resolve @sturdyref SturdyRef @observer ref>.
Bind = <bind @oid any @key bytes @target ref>.
;---------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
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 { 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> {
const assertionMap = new Map<Handle, LocalAction>();
export function during(f: (t: Turn, a: Assertion) => Promise<LocalAction | null>): Partial<Entity> {
const assertionMap = new Map<Handle, LocalAction | 'dead'>();
return {
assert(t: Turn, a: Assertion, h: Handle): void {
const g = f(t, a);
if (g !== null) assertionMap.set(h, g);
f(t, a).then(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 {
assertionMap.get(h)?.(t);
assertionMap.delete(h);
const g = assertionMap.get(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);
}
},
};
}

View File

@ -42,6 +42,8 @@ export type Alts = {"alternatives": Array<Rewrite>};
export type Resolve = {"sturdyref": SturdyRef, "observer": _ptr};
export type Bind = {"oid": _val, "key": _.Bytes, "target": _ptr};
export type ConstructorSpec = (
{"_variant": "CRec", "value": CRec} |
{"_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 Bind({oid, key, target}: {oid: _val, key: _.Bytes, target: _ptr}): Bind {return {"oid": oid, "key": key, "target": target};}
export namespace ConstructorSpec {
export function CRec(value: CRec): ConstructorSpec {return {"_variant": "CRec", "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 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 {
let result = toConstructorSpec(v);
if (result === void 0) throw new TypeError(`Invalid ConstructorSpec: ${_.stringify(v)}`);

View File

@ -2,6 +2,7 @@ import { Actor, Ref, Turn } from "./actor";
import { Relay, spawnRelay } from "./relay";
import { sturdyDecode } from "./sturdy";
import { Resolve, asSturdyRef, fromResolve } from "./gen/sturdy";
import { during } from "./dataspace";
import * as net from 'net';
import { Bytes } from "@preserves/core";
@ -37,11 +38,10 @@ const socket = net.createConnection({ port: 5999, host: 'localhost' }, () => {
t.assert(shutdownRef, true);
t.assert(gatekeeper, fromResolve(Resolve({
sturdyref: asSturdyRef(cap),
observer: t.ref({
assert(t, ds) {
m.default(t, ds);
}
})
observer: t.ref(during(async (t, ds) => {
const facet = t.facet(t => m.default(t, ds));
return t => t.stop(facet);
})),
})));
})));
});

View File

@ -4,7 +4,7 @@ import { Assertion, Ref, Turn } from "./actor.js";
import { attachReadline } from './readline.js';
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 facet = t.facet(t => runSession(t, j.uid, j.handle));
return t => t.stop(facet);
@ -37,7 +37,7 @@ function runSession(t: Turn, uid: UserId, session: Ref) {
updateUsername(t, 'user' + process.pid);
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 oldName = users.get(ui.uid);
console.log(oldName === void 0

View File

@ -13,17 +13,28 @@ import {
fromUserInfo,
} from "./gen/secure-chat-protocol.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 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;
// 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>();
observe(t, ds, $Observe, during((t, o0) => {
observe(t, ds, $Observe, during(async (t, o0) => {
const o = asObserve(o0);
if (o.label !== $joinedUser) return null;

View File

@ -1,24 +1,31 @@
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 * as net from 'net';
import { mint, sturdyEncode, validate } from './sturdy.js';
import { KEY_LENGTH } from './cryptography.js';
import { attenuate } from './rewrite.js';
import { Bytes, IdentityMap } from '@preserves/core';
import { Attenuation, fromSturdyRef, toResolve } 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());
});
import { Bytes, is } from '@preserves/core';
import { $bind, Attenuation, Bind, fromBind, fromSturdyRef, toBind, toResolve, _val } from './gen/sturdy.js';
new Actor(t => {
t.activeFacet.preventInertCheck();
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) {
console.log('connection', socket.remoteAddress, socket.remotePort);
spawnRelay(t, {
@ -31,28 +38,26 @@ new Actor(t => {
socket.on('data', data => r.accept(data));
t.activeFacet.actor.atExit(() => socket.destroy());
},
initialRef: t.ref({
handleMap: new IdentityMap<Handle, Handle>(),
async assert(t, a0, h) {
const a = toResolve(a0);
if (a === void 0) return;
const r = a.sturdyref;
if (!await validate(r, secretKey)) {
console.warn(`Invalid SturdyRef: ${r.asPreservesText()}`);
return;
}
const cavs: Attenuation = [];
r.caveatChain.forEach(cs => cavs.push(... cs));
const attenuated_ds = attenuate(ds, ... cavs);
t.freshen(t => this.handleMap.set(
h,
t.assert(a.observer, attenuated_ds)));
},
retract(t, h) {
t.retract(this.handleMap.get(h));
this.handleMap.delete(h);
}
}),
initialRef: t.ref(during(async (t, a0) => {
const a = toResolve(a0);
if (a === void 0) return null;
const r = a.sturdyref;
let facet = t.facet(t => {
observe(t, ds, $bind, during(async (t, b0) => {
const b = toBind(b0);
if (b === void 0) return null;
if (!is(r.oid, b.oid)) return null;
if (!await validate(r, b.key)) return null;
const cavs: Attenuation = [];
r.caveatChain.forEach(cs => cavs.push(... cs));
const attenuated_ds = attenuate(b.target, ... cavs);
let replyHandle: Handle | undefined;
t.freshen(t => replyHandle = t.assert(a.observer, attenuated_ds));
return t => t.retract(replyHandle);
}));
});
return t => t.stop(facet);
})),
// debug: true,
});
}

View File

@ -13,7 +13,7 @@ export default function (t: Turn, ds: Ref) {
}
updateUsername(t, 'user' + process.pid);
observe(t, ds, $Present, during((_t, e0) => {
observe(t, ds, $Present, during(async (_t, e0) => {
const e = asPresent(e0);
console.log(`${e.username} arrived`);
return (_t) => console.log(`${e.username} departed`);