Agoric / agoric-sdk

monorepo for the Agoric Javascript smart contract platform
Apache License 2.0
326 stars 206 forks source link

Can SwingSet run on browser? #58

Closed danfinlay closed 3 years ago

danfinlay commented 5 years ago

The docs make this look like a command line tool, but is there a way to also run these vats and support cross-process communication within a browser?

erights commented 5 years ago

Hi @danfinlay !

We have not been. But we've been trying to stick to the discipline that, as much as possible, we assume only a standard modern JavaScript language, while keeping all host dependencies small and contained. So guessing: It probably doesn't quite work, but can be made to work without too much effort.

The the "it" there is running an entire SwingSet (the kernel and all the vats using that kernel) in one JavaScript process. When you say "cross-process", what do you have in mind?

danfinlay commented 5 years ago

When you say "cross-process", what do you have in mind?

There was probably a better word I could have chosen. In this context by "process" I mean independent JavaScript event loops that do not have access to each other's memory or objects. I guess this is sometimes called "across an async membrane"? Still learning some of the lingo.

Some examples of "separate processes":

I dream that things like the third use case (the ability to edit a specific paragraph in a document) could be as easy as participantB.canEdit(paragraphA), and the runtime (SwingSet, if running within a browser) could handle the serialization, signing, and deserialization, and signature verification on invocation of this remote function reference "under the hood".

Am I understanding correctly what role Swingset plays, and what it could do if it were modified to run within a browser?

erights commented 5 years ago

Am I understanding correctly what role Swingset plays, and what it could do if it were modified to run within a browser?

I think so, but cc'ing @warner, @michaelfig, and @kriskowal who can correct me where I misstep. Some of your scenarios would be supported by multiple vats within one swingset, and some by multiple swingsets talking to each other. When explaining vats and swingsets, I start with

Multiple vats within the same swingset effectively share one event loop, which is some interleaving of the turns for each of the member vats. Each turn of any vat runs to completion before anything happens in any other vat. There is no genuine parallelism within a swingset.

An object within one vat can have a direct reference only to other objects in its own vat. For all other objects, it holds only a remote reference to them, in the form of a handled promise which communicate to their swingset kernel through a data only interface. Although we are actually running them all within one realm, we could switch these to separate realms without any observable difference.

A separate worker within the browser is, to us, as separate as a remote server. Likewise with a background process, or anything reached over websockets or webrtc. We would put a separate swingset in each, each with its own comms vat.

Whatever the low level communications mechanism is between these units, we'd need to abstract it into a vattp connection. Q-Connection did something similar.

danfinlay commented 5 years ago

This helps a lot, thanks for clarifying the role between a vat and a SwingSet.

Q-Connection is very nice, and maybe usable for what I was looking for at the moment. In part I was considering whether some of this prior work had tooling that I could use as an alternative to building capnode (a remote object-proxy library), and while its interface isn't as open-ended, I really like that it supports pipelining out of the box.

I was also curious whether those messages were being cryptographically signed within SwingSet, or maybe you could point me to which layer of the stack the cryptographic serialization is happening at?

kriskowal commented 5 years ago

There are a number of things I would do the same and differently with Q-Connection if I were to pick it up and carry it again.

Notably, I’d get back in touch with Mark and Dean and figure out how to hoist it atop the new standard promise. I’m not entirely sure that’s possible without subverting it or further spec work for something like a AsyncProxy(handler) that regular promises will forward messages to. I understand they’ve given more thought to this.

I would also use WeakRef, finally, to collect entries in the connection’s tables when nothing else refers to them. As written, all remote references are tracked indefinitely or with an arbitrary map implementation. The arbitrary map can be a fixed size cache, but the developer ergonomics for that are pretty awful. They place an arbitrary limit on how many outstanding remote references you can have and require you to code defensively, with retry loops that recreate the chain of references to all involved remote objects from the remote root API. Far better to retain the Connection for the scope of a transaction and discard it all.

I would move the responsibility of JSON.stringify,parse to the stream adapter and use bidirectional async iterables as the stream abstraction. I would remove entirely the concern of adapting all the bonkers stream abstractions to async iterables, leaving that to another library or other libraries.

Q-Connection memoizes every individual local data object and transmits nested objects shallowly, in postfix construction order. I would keep this. This makes it possible to orchestrate the development of remote object graphs, including those with cycles.

I would do something to accommodate binary payloads, and probably would use a binary wire protocol instead of JSON. My first uses for Q-Connection involved proxying an attenuated filesystem and it didn’t turn out to be particularly useful because it couldn’t transit binary data.

In general, Q-Connection is really outrageously small. All the real work happens in Q because Q exposes a promise constructor that’s like a Proxy constructor. I would copy it and fiddle, not depend upon it. Most of the work is in clawing that property back from the JavaScript Promise.

On Thu, Sep 19, 2019 at 2:26 PM Dan Finlay notifications@github.com wrote:

This helps a lot, thanks for clarifying the role between a vat and a SwingSet.

Q-Connection is very nice, and maybe usable for what I was looking for at the moment. In part I was considering whether some of this prior work had tooling that I could use as an alternative to building capnode https://www.npmjs.com/package/capnode (a remote object-proxy library), and while its interface isn't as open-ended, I really like that it supports pipelining out of the box.

I was also curious whether those messages were being cryptographically signed within SwingSet, or maybe you could point me to which layer of the stack the cryptographic serialization is happening at?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Agoric/SwingSet/issues/153?email_source=notifications&email_token=AAAOXBXGFB7MTIDQYVG6A73QKPU65A5CNFSM4IXEYTDKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7E36FQ#issuecomment-533315350, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAOXBRKAUHHWKF673WXC2DQKPU65ANCNFSM4IXEYTDA .

erights commented 5 years ago

I was also curious whether those messages were being cryptographically signed within SwingSet, or maybe you could point me to which layer of the stack the cryptographic serialization is happening at?

The crypto should be happening within the vattp layer. The comms vat should not know about crypto.

warner commented 4 years ago

FWIW, I think of SwingSet as a library first, which must be embedded/instantiated inside some "host". The host creates the kernel and the initial vats, and gets back a controller. The host can use the controller to process the run-queue. The host should also (generally) set up some "devices" at the same time (e.g. the Mailbox device) and use them to cause messages to be added to the run queue. The host also provides something to store kernel+vat state in.

The bin/vat executable is a bare-bones demo host: it's really only useful for running a set of vats from bootstrap to completion, with no facilities for adding new messages. I'm thinking of adding some comms functionality to make it more useful, but I'm not sure that a command-line tool is the most natural home for a swingset.

Our "cosmic-swingset" repo is a blockchain-based host for a swingset environment. Since the state management of the vats (checkpointing at the end of each large turn) is very similar to the state management of a blockchain (one checkpoint per block), it's fairly straightforward to treat the swingset library as the state machine inside the blockchain world: each signed transaction may deliver a message through the Mailbox device, and then for each block we process the runqueue to completion.

We may have some work to do before it's possible, but in general you should be able to instantiate a swingset environment in a browser. But we have to decide what the "host" looks like, and how it satisfies the swingset's needs from the host: somewhere to store state (unless you're satisfied with ephemeral vats), some way to get messages in (unless you're satisfied with very short-lived vats), and some way for messages to get out (unless you're satisfied with having no side-effects).

The work that may need to be done includes:

dckc commented 3 years ago

@ski I think you asked me this question a few weeks ago.

erights commented 3 years ago

Hi @michaelfig I think this is closest to things you are working on, so may I assign you?

michaelfig commented 3 years ago

The short answer of "can SwingSet run in a browser" is "not without a lot of work." The long answer is https://github.com/Agoric/agoric-sdk/issues/58#issuecomment-538566953 And also, that work is not really on Agoric's roadmap at this time.

A better question is "can a vat run in a browser" (i.e. without the deterministic persistent multi-vat world that SwingSet provides for)? The answer to that is "yes"! Use SES and CapTP, and be prepared to reestablish your remote objects from scratch on reload. You can take advantage of any platform features in this model (like async databases, remote hosts, etc): no need to be deterministic. What we're missing is LavaMoat integration to partition risk nearly as seamlessly as SwingSet provides.

Our most obvious example of this is the wallet UI which uses SES and CapTP messaging to talk to the local ag-solo. It's in Svelte. We're making more examples of this kind of UI Really Soon Now.

As far as making this pattern generalisable to other runtime environments: -cough-Endo-cough-. But as @danfinlay knows, MetaMask and Agoric are both going together in this direction. And we have a plan for how to get there in phases. As for @ski, the Svelte captp stuff may be what you're looking for. Feel free to open another issue if you have questions about how to reproduce that model in (IIRC) Vue.

Closing this issue until somebody signs up to do the "lot of work" it would take.

dckc commented 3 years ago

It seems like a bunch of the hard work has actually been done. I wonder how much is left:

The work that may need to be done includes:

  • getting SES to work in the environment (e.g. Workers) without a DOM, from which the Realms shim gets an <iframe> to get a new set of primordials. The Compartments API is the way forward here.

The Compartment API is pretty mature by now.

  • providing a meaningful durable state-storage object. Maybe IndexedDB or something.

https://github.com/Agoric/agoric-sdk/pull/3171 seems to make that feasible.

  • providing messaging channels: WebSocket comes to mind but there are a lot of questions about how new connections are established and named that we need to figure out first. The IBC protocol that we're helping to develop is relevant too

I guess that part has a ways to go... but we're making progress: #1670 ,

see also #3180