comit-network / waves

Workspace for project waves - bringing DeFi to Liquid.
MIT License
15 stars 6 forks source link

On the complexity of wasm imports #228

Closed thomaseizinger closed 3 years ago

thomaseizinger commented 3 years ago

Historical context

Currently, importing and using functions from the WASM blob is quite cumbersome.

Our version of CRA depends on webpack 4 which doesn't support asynchronous chunks being loaded as part of the initial request. As a result, we cannot use say:

import { wallet_status } from "./wallet"

Where ./wallet represents the module generated by wasm-bindgen.

Instead we have to use dynamic imports (await import ("./wallet")) to access any function defined in the WASM binary.

Problems

These dynamic imports impose problems:

  1. Duplicated wallets: If we load the wallet from different endpoints (i.e. not just the background script but say, a new popup window), we effectively get two instances of the wallet and unlocking one will not unlock the other.
  2. Complexity: We have to write a lot of dumb code to call a function exposed by the WASM blob. To make the developer experience nicer, this integration should be as seamless as possible. This complexity also surfaces in the lack of nice error reporting when something goes wrong.

Ideas to improve the situation

  1. Experiment with the recent alpha release of react-scripts: https://www.npmjs.com/package/react-scripts/v/5.0.0-next.31

This release upgrades to webpack5 which supports loading asynchronous modules as part of the initial chunk. In other words, if we get this working, we can just import the wasm-functions like any other functions from the background script, effectively getting rid of wasm_proxy.ts. Note that I think this still doesn't solve the "duplicate wallet" issue if we were to import the wasm from multiple places.

  1. Register message handlers directly from within Rust.

Once #227 is merged, message handlers for rpc-messages from the content-script are registered independently. We could therefore experiment with registering a handler through browser.runtime.onMessage.addListener directly from within the Rust module. This would cut out on the indirection through the Typescript-based background script.

Vision

If we manage to do (1) and (2), we should be able to:

  1. Have a single, very simple background script that only imports the wasm-blob, effectively initializing the wallet and acting as the wallet "host". Because the background script lives for the entire duration of the extension, there would only be one wallet.
  2. Directly response to messages from the content-script and other popups from within Rust, cutting out all pass-through layers like the current background script, background proxy, wasm-proxy, etc. Rust has excellent meta-programming which should allow us to keep the boilerplate of creating such event listener small through the use of proc-macros.
thomaseizinger commented 3 years ago

Report on experimentation with react-scripts

We don't actually directly depend on react-scripts but react-browser-extension-scripts.

I've submitted a PR upstream to update that to the latest changes in react-scripts that come with webpack 5: https://github.com/VasilyShelkov/create-react-extension/pull/32.

It is more or less working (after a lot of playing around). The only problem now is that something is wrong with the URL of the wasm blob which is why it is not yet loaded. But, the much more important finding is that using webpack 5, we can directly import wasm modules like any other JS module!

Draft PR here: https://github.com/comit-network/waves/pull/240