Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

raw devices: maybe remove deviceSlots, reenable promises, merge companion vat #1346

Open
warner opened this issue Jul 29, 2020 · 4 comments
Open
Labels
enhancement New feature or request SwingSet package: SwingSet

Comments

@warner
Copy link
Member

warner commented Jul 29, 2020

What is the Problem Being Solved?

While walking @FUDCo through #1339, we were discussing the limitations of swingset Devices which prompted @Chris-Hibbert and I to add a companion vat to each device:

  • deterministic operation cannot be guaranteed in the face of endowments
  • therefore orthgonal persistence was removed, along with the transcript. Instead, devices must call getDeviceState() and setDeviceState(newState) at the right times. These go through marshal() so devices can include Presences in their state.
  • Promises are harder to reason about without orthogonal persistence, so they were removed
  • now the only syscall devices can make is sendOnly
  • but many devices want an API that includes Promises (e.g. D(timer).wakeUpAt(when).then(makeCoffee))
  • so we wind up with a pattern where the device is invoked externally, queues a sendOnly to the companion vat, and returns. Later, when the vat receives that message, it resolves the promise for the original caller

I'm starting to think that some of the problem comes from trying too hard to retain the liveSlots model in the device world. We have a stripped-down version of liveSlots named deviceSlots.js for devices, which lets them use Presences in the same way as vats do (although using SO(target).method(args) instead of E(), to reflect the send-only nature). But because devices can't use orthogonal persistence, devices can't persist the objects they export (#62), so they can't really provide objects as part of their API. And Promises were already removed, so those are off the list too.

So now I'm thinking that it might be better to go low-tech. If we removed deviceSlots, and defined devices in terms of the low-level syscall and dispatch objects, then we could bring back the full API surface (imported/exported objects, imported/exported promises), which would let us have comfortable device APIs without the awkward companion vat.

Description of the Design

Devices written in this style would be pretty raw. There would be no Promise objects and no E() orSO() wrappers to send messages. Device code would handle object/promise/device-node reference identifiers directly (strings like o+123), which means there would be no ocap safety within a device (any device code that can send a message to one remote object could equally easily send it to any remote object that's been shared with the device).

The timer device in this world might look something like this:

function capdata(data, slots = []) {
  return harden({ body: JSON.stringify(data), slots });
}
function parseCapdata(capdata) { // ignores slots
  const data = JSON.parse(capdata.body);
  return data;
}
function makeError(msg) {
  // serialize an Error into capdata with @qclass='error', name, message
}

export function buildDevice(syscall, state, endowments) {
  // endowments are things we can invoke on the outside
  const { getCurrentTime, setWakeup } = endowments;
  let { alarms = [] } = state.get() || {};
  const ROOT = 'o+0';

  function saveState() {
    state.set({ alarms });
  }

  // things the kernel can tell us to do
  const dispatch = harden({
    send(target, method, args, result) {
      if (target === ROOT) {
        if (method === 'getCurrentTime' && result) {
          const now = getCurrentTime();
          syscall.fulfillToData(result, capdata(now));
        } else if (method === 'setWakeup' && result) {
          const [when] = parseCapdata(args);
          alarms.push({ when, result });
          setWakeup(min(alarms.map(a => a.when)));
          saveState();
        } else {
          result && syscall.reject(result, makeError(`unknown method ${method}`));
        }
      } else {
        result && syscall.reject(result, makeError(`unknown target ${target}`));
      }
    }
  });  

  // things which the host can call from outside the swingset
  const externals = harden({
    wakeup(now) {
      const remainingAlarms = [];
      for (let alarm of alarms) {
        if (alarm.when <= now) {
          syscall.fulfillToData(alarm.result, capdata(now));
        } else {
          remainingAlarms.push(alarm);
        }
      }
      alarms = remainingAlarms;
      saveState();
    },
  });

  return { dispatch, externals };
}

(this presumes a change to the way we set up devices, to return an externals table, but now I'm thinking that might be a nice way to help #720 : a controller.addDevice(bundleFile, endowments) call would return this externals object to the caller, wrapped in the #720 queue handler, instead of having the host call a {externals, bundleFile, deviceEndowments} = buildFooDevice(endowments) function ahead of time, keeping 'externals' for itself and passing the rest into the kernel)

To add the ability to cancel alarms, we'd want to return an object from setWakeup:

const { nextObjectID = 1,
        nextPromiseID = 1,
        alarms = [] } = state.get() || {};
function saveState() {
  state.set({ nextObjectID, nextPromiseID, alarms });
}
function cancel(cancelObjID)  {
  const remainingAlarms = [];
  for (let alarm of alarms) {
    if (alarm.cancelID !== cancelObjID)
      remainingAlarms.push(alarm);
    }
    setWakeup(min(alarms.map(a => a.when)));
    saveState();
  }
}

...
  else if (method === 'setWakeup' && result) {
    const [when] = parseCapdata(args);
    const cancelObjID = `o+${nextObjectID}`;
    nextObjectID += 1;
    const donePromiseID = `p+${nextPromiseID}`;
    nextPromiseID += 1;
    alarms.push({ when, donePromiseID, cancelObjID });
    setWakeup(min(alarms.map(a => a.when)));
    saveState();
    syscall.fulfillToData(result, capdata({ cancel: slotIndex(0),
                                            done: slotIndex(1),
                                          }, [cancelObjID, donePromiseID]));
  }
...

else if (method === 'cancel') {
  for (let alarm of alarms) {
    if (alarm.cancelObjID === target) {
      cancel(target);
    }
  }
}
...

I can imagine some helper functions that provide affordances for mapping the target to a given handler, in which the handlers are populated at device startup time (by parsing state.get) or later as new objecst are created, to make this process less manual. I'd also add helpers for returning a record that contains slots (a subset of what marshal does, written in terms of slotIDs instead of Presence and Promise objects).

Security Considerations

The loss of intra-device authority limits is a bit sad, as it reverts the device security model to the "bad old days" where a bug anywhere in the file would compromise the entire device. But I'm not sure we're benefiting from that model in deviceSlot -based devices anyway: devices are pretty small and there isn't a lot of internal partitioning anyways (and the availability of setDeviceState everywhere is a pretty broad compromise vector).

This approach makes it harder to make sure we're answering every message: there's no implicit return undefined at the end to cause a syscall.fulfillToData() on each invocation of dispatch.deliver. Forgetting that would make callers using await to hang, and would prevent the result promises from ever getting GCed.

I think the overall tradeoff is:

  • deviceSlots lets us have a halfway-nice environment for writing devices
  • this non-deviceSlots approach would remove the need for a companion vat and the runtime coordination that entails

This would obsolete #1316, since we'd be deleting deviceSlots.js, and removing Presence objects from the device environment.

@warner warner added enhancement New feature or request SwingSet package: SwingSet labels Jul 29, 2020
warner added a commit that referenced this issue Sep 15, 2020
Vats which hold device nodes (`d-NN` references) can use `syscall.callNow()`
on them, to make a synchronous invocation (which can return data). The
outbound arguments and return data are capdata, which is translated through
c-lists just like regular `syscall.send()` and promise resolution.

However devices do not (currently) handle Promises at all. The
kernel-to-device c-list translation will panic the kernel if asked to
translate a promise reference (`kpNN`).

Vats should not be able to panic the kernel, even if we give them access to a
device node. This changes the vat-to-kernel translator to reject promise
references in the arguments of `callNow`, making it a vat-fatal error. This
will terminate the vat, but leave the kernel running.

In the long run (#1346), devices should accept Promises, but it will take
more work (and probably require devices to operate on a much lower level than
vats do). But this should fix the immediate kernel panic.

Note that killing a vat is not exactly friendly either. The bug described in
issue #1358 was triggered by user REPL input causing the HTTP vat to try
sending a Promise into the Command device, killing the kernel. With this
change, this will instead kill the HTTP vat, which breaks the REPL, rendering
the system mostly unusable. But at least the attribution is correct.

We have another fix in the works that will change liveslots.js to catch this
situation during the call to `D(devnode).methname(args)`, which should reduce
the blast radius to merely throw an exception in `D()`, rather than killing
the whole vat.

refs #1358
warner added a commit that referenced this issue Sep 15, 2020
Vats which hold device nodes (`d-NN` references) can use `syscall.callNow()`
on them, to make a synchronous invocation (which can return data). The
outbound arguments and return data are capdata, which is translated through
c-lists just like regular `syscall.send()` and promise resolution.

However devices do not (currently) handle Promises at all. The
kernel-to-device c-list translation will panic the kernel if asked to
translate a promise reference (`kpNN`).

Vats should not be able to panic the kernel, even if we give them access to a
device node. This changes the vat-to-kernel translator to reject promise
references in the arguments of `callNow`, making it a vat-fatal error. This
will terminate the vat, but leave the kernel running.

In the long run (#1346), devices should accept Promises, but it will take
more work (and probably require devices to operate on a much lower level than
vats do). But this should fix the immediate kernel panic.

Note that killing a vat is not exactly friendly either. The bug described in
issue #1358 was triggered by user REPL input causing the HTTP vat to try
sending a Promise into the Command device, killing the kernel. With this
change, this will instead kill the HTTP vat, which breaks the REPL, rendering
the system mostly unusable. But at least the attribution is correct.

We have another fix in the works that will change liveslots.js to catch this
situation during the call to `D(devnode).methname(args)`, which should reduce
the blast radius to merely throw an exception in `D()`, rather than killing
the whole vat.

refs #1358
warner added a commit that referenced this issue Sep 15, 2020
Vats which hold device nodes (`d-NN` references) can use `syscall.callNow()`
on them, to make a synchronous invocation (which can return data). The
outbound arguments and return data are capdata, which is translated through
c-lists just like regular `syscall.send()` and promise resolution.

However devices do not (currently) handle Promises at all. The
kernel-to-device c-list translation will panic the kernel if asked to
translate a promise reference (`kpNN`).

Vats should not be able to panic the kernel, even if we give them access to a
device node. This changes the vat-to-kernel translator to reject promise
references in the arguments of `callNow`, making it a vat-fatal error. This
will terminate the vat, but leave the kernel running.

In the long run (#1346), devices should accept Promises, but it will take
more work (and probably require devices to operate on a much lower level than
vats do). But this should fix the immediate kernel panic.

Note that killing a vat is not exactly friendly either. The bug described in
issue #1358 was triggered by user REPL input causing the HTTP vat to try
sending a Promise into the Command device, killing the kernel. With this
change, this will instead kill the HTTP vat, which breaks the REPL, rendering
the system mostly unusable. But at least the attribution is correct.

We have another fix in the works that will change liveslots.js to catch this
situation during the call to `D(devnode).methname(args)`, which should reduce
the blast radius to merely throw an exception in `D()`, rather than killing
the whole vat.

refs #1358
@warner
Copy link
Member Author

warner commented Jan 5, 2022

This is becoming more interesting as I look at #1848 vat upgrade, in particular the ZCF/contract upgrade flow described in #1848 (comment), because:

  • the version2 vat needs to re-associate all virtual-object Kinds during the upgrade phase (i.e. before buildRootObject or upgrade finishes), so that it is prepared to honor any messages to or about those virtual objects by the time they might arrive (which is the very next crank after the upgrade phase)
  • so the new contract code bundle must be available during the upgrade phase, without sending any messages to retrieve the bundle contents (because the response to those messages would arrive in a different crank, which is too late)
    • we have vatParameters to work with, but we probably don't want to put the entire contract bundle in there, it's large and annoying
    • so vatParameters can point to a bundle, but we'd rather it not contain the bundle directly
    • vatParameters could hold a device node, and device nodes can be queried synchronously, without messages
    • so we can use a device node to represent a bundle
  • the device (probably vatAdminDevice, but maybe a new one) that exports those device nodes will be creating new ones, each time a new contract bundle is installed
  • as described in devices cannot persist exports #62, this is currently awkward, for the same reasons that prompted us to create Virtual Objects in vats

We could either add more features to deviceSlots to make it easier to synthesize device nodes on-demand, or we could rip out features to make it easier to accept deliveries for device nodes directly. I'm leaning towards the latter, with an API basically like above. I'll probably use a multi-key vatStoreGet/Set/Delete API instead of the single-key get/setDeviceState, which should make the code simpler. Maybe name it deviceStore instead.

My plan is to have an alternative entry point: if the device bundle exports buildDevice, it gets called with a raw syscall object and is expected to provide a dispatch object. If it doesn't export that, we expect it to export buildRootDeviceNode instead, with the old API.

@warner
Copy link
Member Author

warner commented Jan 8, 2022

I think I'll need a small helper library so marshal is easier to use. It will be common to want a method invocation to return a record that includes a new device node. I don't want the device code to know the marshal syntax for a Slot, rather I want it to say something like dref = allocateDref(); state.update(something, dref); return serialize(DeviceNode(dref)). Then DeviceNode() would make a new short-lived object and register it in a WeakMap that's shared with the serialize from our marshal instance. That instance's convertValToSlot would use the WeakMap to learn the dref and serialize it properly.

Likewise, there should be a helper function to deserialize incoming arguments, and the objects that come out of that deserialization may include a Presence or a Promise. I don't intend to put a lot of methods on the Presence/Promise placeholder objects (in particular I do not intend to make them HandledPromise presences), I just need something from which a vref can be extracted (and stored in the device state somewhere), and which can be used when serializing arguments, naming the target to which we perform a sendOnly, or name a promise that we're resolving. So like syscall.resolve(p1.vref, false, serialize(data)) instead of p1.resolve(data).

@warner warner self-assigned this Jan 27, 2022
@warner warner changed the title devices: maybe remove deviceSlots, reenable promises, merge companion vat raw devices: maybe remove deviceSlots, reenable promises, merge companion vat Jan 28, 2022
warner added a commit that referenced this issue Jan 28, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
warner added a commit that referenced this issue Jan 28, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
warner added a commit that referenced this issue Jan 29, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
warner added a commit that referenced this issue Jan 29, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
warner added a commit that referenced this issue Jan 29, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
warner added a commit that referenced this issue Jan 31, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
@Tartuffo Tartuffo added the MN-1 label Feb 2, 2022
warner added a commit that referenced this issue Feb 3, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
warner added a commit that referenced this issue Feb 3, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
warner added a commit that referenced this issue Feb 3, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
warner added a commit that referenced this issue Feb 4, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
dtribble pushed a commit that referenced this issue Feb 4, 2022
"Raw devices" bypass the deviceSlots layer and allow device code direct
access to `syscall`, and the arguments arriving through the `dispatch` object
it must produce. This makes some patterns much easier to implement, such as
producing new device nodes as part of the device's API (e.g. one device node
per code bundle).

It also provides vatstoreGet/Set/Delete, so the device code can manage one
piece of state at a time, instead of doing an expensive read-modify-write
cycle on a single large aggregate state object.

A helper library named deviceTools.js was added to make it slightly easier to
write a raw device.

In the longer run (see #1346), we'd like these devices to support Promises
and plain object references. This change doesn't go that far. The remaining
limitations are:

* the deviceTools.js library refuses to handle exported objects, imported
foreign device nodes, or promises of any sort
* the outbound translator (deviceKeeper.js `mapDeviceSlotToKernelSlot`)
refuses to handle exported objects and exported promises
* the vat outbound translator (vatTranslator.js `translateCallNow`) refuses
to handle promises
* liveslots rejects promises in `D()` arguments

refs #1346
@warner
Copy link
Member Author

warner commented Feb 7, 2022

#4419 has landed, providing all the functionality I care about right now. The new src/deviceTools.js doesn't make it easy to manipulate Promises, but I don't need that now.

The remainder of this ticket is:

  • enhance deviceTools.js to manipulate Promises
  • change existing devices (timer, mailbox, etc) into "raw devices", improve API to take advantage of promises, merge companion vat

None of those are mainnet-1 -critical, so I'm moving this ticket out of the MN-1 stack. If we don't make those changes before MN-1 launch, I don't know how we'd migrate to the new devices later, but I think we can survive on the existing devices without problems (although #4282 and #4286 may requires changes to the timer device, which might be an excuse to do the work earlier).

@warner
Copy link
Member Author

warner commented Feb 26, 2022

Ugh, this got moved back to the MN-1 pipeline because the PR finally landed. I'll move it back out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request SwingSet package: SwingSet
Projects
None yet
Development

No branches or pull requests

3 participants