# WebZjs and Snap Architecture > @wollum I realised that a lot of the design for webz and the snap is contained in my head still since a lot changed during the development process. This doc exists to try and make everything more visible and get everyone on the same page. ## Bacground on WebZjs and Librustzcash ### Zcash and privacy cryptocurrencies Wallets for private cryptocurrencies are significantly more complex than for non-private ones. In a non-private wallet such as an Ethereum or Bitcoin wallet it is possible to delegate the chain scanning and indexing to a third party (e.g. Infura). This is not possible for a private wallet as doing so would de-anonymize your accounts. So the wallet itself needs to perform a LOT of additional work. Namely: - Scanning all blocks in the blockchain looking for incoming transactions targeting your accounts - Updating a global note Merkle tree such that the wallet has a merkle proof for all notes held by the wallet ### Librustzcash Librustzcash is the offical set of Zcash Rust crates maintained by the Zcash foundation and Electric Coin Company. It is used by Zashi the official iOS and Android wallet. The `zcash_client_backend` crate contains a set of traits that can be implemented by consumers to instantiate a new wallet backed by some storage. For example a sqlite database. It connects to a service called `lightwalletd` which provides a high-speed way to retrieve chain data without compromising anonyminity provided it is used correctly. Once a backend has been instantiated this can be connected to a front-end resulting in a functional wallet. ### WebZjs Overview We created WebZjs by building an in-memory storage backend using librustzcash that can compile to Wasm. This is then exposed to javascript developers using an interface generated using wasm-bindgen. The result is a browser compatible javascript library implementing a wallet backend that allows developers to write their front-end using web technologies. We made several design choices when building WebZjs to make its execution feasible in the browser: - The expensive sync operations (which can take several minutes or potentially hours in extreme cases) takes place in a WebWorker which itself uses a configurable sized thread-pool of WebWorkers to get parallel speed-ups - The wallet state is stored in-memory but can be serialized for persistance. It is up to the consuming code to decide where and when wallet state is persisted. - Connection to a `lightwalletd` instance needs to go via a gRPC-web proxy. ChainSafe currently runs one of these for Zcash mainnet and testnet (https://zcash-mainnet.chainsafe.dev and https://zcash-testnet.chainsafe.dev) #### Types of Keys Modern Zcash (e.g. Orchard) has a number of different key types which have different levels of capability. Keys higher up the chain (e.g. the bottom of the image below) have more capability and keys further down can be derived from these. > Note these keys have `Unified` variations meaning they work across different pools (Orchard and Sapling) ![image](https://hackmd.io/_uploads/rJl701c1yx.png) The top level of key in Zcash is a Unified Spending Key (USK). This can be derived directly from a 24 word seed phrase plus a HD index following [ZIP32](https://zips.z.cash/zip-0032). Access to a spending key is required when generating transactions that spend funds of a wallet. A Unified Full Viewing Key (UFVK) can be derived from a USK. This is the type of key needed to sync a wallet and retrieve its balance. Knowing a UFVK allows someone to know a wallets balance and all transactions it has made but does NOT allow spending these funds. WebZjs requires a UFVK for each account it manages meaning it actually cannot produce transactions unless it is given a USK. The other key types are less important but it is good to know that a payment address can be derived from a viewing key and this is safe to share with anyone. You can generate as many payment addresses as you like per account and should aim to never reuse them. ## WebZjs API ### Adding Accounts An account can be added to a WebWallet instance by calling `create_account` and providing a seed phrase or by calling `import_account_ufvk` and providing a UFVK. Internally `create_account` just uses the seed phrase to derive a UFVK then deletes it. `import_account_ufvk` should be preferred where possible to keep the seed phrase safe. Once an account has been added to a wallet the wallet will ensure it is kept in sync with the blockchain by scanning relevant blocks and decrypting transactions to/from the account. ### Sending Transactions There are three phases involved in submitting a transaction to the Zcash network: proposal, creation, and submission. #### Proposal Proposing a transaction involves selecting the notes (UTXOs) to spend from the available notes in the wallet and constructing the change notes such that the total in/out notes sums to zero. In WebZjs this can be performed by calling `propose_transaction` and passing the destination address and amount. Proposing does NOT require any keys, only access to the account state. #### Creation Creating a transaction takes a proposal and performs the proof generation and signing required for it to be authorized and valid. There is currently only one way to create a transaction which requires the spending key to be passed into the browser context. This is not ideal... Currently work is being done by the Zcash Foundation on something called Partially Constructed Zcash Transactions (PCZTs). Once this lands it will be possible to perform the expensive proof generation in the browser while the sensitive signing can be done in a secure environment (e.g. hardware wallet or Secure ECMA Script in the Snap). ##### Current Strategy Currently in WebZjs a transaction is created by calling `create_proposed_transaction` and passing a proposal, a seed phrase, and a derivation index. This takes care of generating the proof, which is a relatively expensive operation taking maybe a few seconds, and creating the spend authorization using the spending key. Calling this requires that either the seed phrase or USK be available to the wallet code that is calling this function. ```mermaid sequenceDiagram participant WebZ participant Browser Wallet participant Snap Browser Wallet->>Snap: Request USK Snap->>Snap: Get approval from user Snap->>Browser Wallet: [USK] Browser Wallet->>WebZ: create_proposed_transaction(proposal, usk) WebZ->>Browser Wallet: [Authorized Transaction] ``` This sucks for a number of reasons: - It requires the seed phrase to be used in the browser meaning the web wallet code really needs to be audited - The user is authorizing the page to use the spending key but has no guarentees what it will do with it - You need to ensure the seed phrase and HD index match the account used when creating the proposal. This is weird and awkward. We can go ahead with this for the moment but it should be emphazied this is not something that should be used in production ##### PCZT Strategy Once PCZTs are finalized it will be possible to have the proving performed in the browser without the spending key while the less expensive but more sensitive authorization part can be performed in the Snap. This is comparable to how hardware wallets work currently. This is much better and safer. Here when the user is approving they can see exactly what they are authorizing and the page never gets the USK. ```mermaid sequenceDiagram participant WebZ participant Browser Wallet participant Snap Browser Wallet->>WebZ: partially_create_proposed_transaction(proposal) WebZ->>Browser Wallet: [PCZT] Browser Wallet->>Snap: authorize(pbzt) Snap->>Snap: Get approval from user Snap->>Browser Wallet: [Authorized Transaction] ``` This should supercede the other stragegy once it is available #### Sending Once an authorized transaction is available from either of the above strategies it can be sent to the network by calling `send_authorized_transactions`. This updates the internal wallet state and sends the transaction(s) to the mempool via the connected `lightwalletd` instance. ### Persistance > [!Note] > The persistance API for WebZjs is not yet finalized. It will probably be similar to what is described here but may differ in details. Syncing a wallet is an expensive operations but once it has been done for a set of accounts there is no need to repeat it for historical blocks. Therefore it is important that this work be cached in order to provide a good user experience. In addition to sync state there is additional wallet state data (e.g. sent transactions) that cannot be retrieved from the blockchain and so should be persisted where possible. In WebZjs the `WebWallet` can be serialized to a Uint8Array using `serialize()` and a WebWallet instance can be created from its serialized form using the overloaded constructor `new WebWallet(bytes)`. Where this data is persisted to is ultimately up to the wallet front-end developer although there are some considerations that should be made. Using IndexDb is a good option however this is restricted per-origin. This means that the wallet would need to be re-synced for each dApp! A valid comparison for Ethereum would be if you have to spend minutes syncing a wallet to use uniswap, and then have to do it again to use the OP bridge. The local filesystem could be used requiring the user to 'download' the state before they nagivate away and then 'load' the state from file when the next visit the wallet. This is also poor UX for different reasons. A third option when using WebZjs with the Snap is to use the Snap storage as a persistance layer. The [Snap storage API](https://docs.metamask.io/snaps/features/data-storage/) allows for persisting up to 100MB of data per Snap, and a custom RPC method could expose this to the web wallet page. Depending on the wallet activity 100MB may not be enough however it is probably sufficient for most users and we could devise a way to prune data if required. ## WebZ and Snap Architecture ![image](https://hackmd.io/_uploads/B1YY_Gqykx.png) While WebZjs can be used to create Zcash web wallets without requiring a Snap, I believe using the Snap, or another browser plugin, should be the reccomended way to interact with it. The snap should provide: - Key derivation - Key storage - Wallet state storage and retrieval via custom RPC - Transaction proposal approval flow via custom RPC