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 ...]>.
|
||||
|
||||
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 { 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);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)}`);
|
||||
|
|
|
@ -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);
|
||||
})),
|
||||
})));
|
||||
})));
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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`);
|
||||
|
|
Loading…
Reference in New Issue