Skip to content

Commit

Permalink
Merge branch 'main' into thomivy-patch-5-1
Browse files Browse the repository at this point in the history
  • Loading branch information
drewstone authored Jun 16, 2023
2 parents 765106c + fbfe1eb commit 47852ee
Show file tree
Hide file tree
Showing 17 changed files with 2,110 additions and 1,195 deletions.
46 changes: 46 additions & 0 deletions components/images/masp/Masp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Image from "next/image";
import MASPPrivateTransferLight from "./masp_private_transfer_light.png";
import MASPPrivateTransferDark from "./masp_private_transfer_dark.png";
import MASPSwapLight from "./masp_swap_light.png";
import MASPSwapDark from "./masp_swap_dark.png";
import MASPSwapDiagramLight from "./masp_swap_diagram_light.png";
import MASPSwapDiagramDark from "./masp_swap_diagram_dark.png";

export const MASPPrivateTransferImage = () => {
return (
<>
<div className="block dark:hidden">
<Image src={MASPPrivateTransferLight} alt="masp light" />
</div>
<div className="hidden dark:block">
<Image src={MASPPrivateTransferDark} alt="masp dark" />
</div>
</>
);
};

export const MASPSwapImage = () => {
return (
<>
<div className="block dark:hidden">
<Image src={MASPSwapLight} alt="masp light" />
</div>
<div className="hidden dark:block">
<Image src={MASPSwapDark} alt="masp dark" />
</div>
</>
);
};

export const MASPSwapDiagramImage = () => {
return (
<>
<div className="block dark:hidden">
<Image src={MASPSwapDiagramLight} alt="masp light" />
</div>
<div className="hidden dark:block">
<Image src={MASPSwapDiagramDark} alt="masp dark" />
</div>
</>
);
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added components/images/masp/masp_swap_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added components/images/masp/masp_swap_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion pages/docs/protocols/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"definitions": "Definitions",
"asset-transfer": "Asset Transfer",
"dkg": "DKG",
"identity": "Identity"
"identity": "Identity",
"masp": "Multi-Asset Shielded Pool"
}
7 changes: 7 additions & 0 deletions pages/docs/protocols/masp/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"overview": "Overview",
"note-encryption": "Note Encryption",
"swap": "Swap Circuit",
"rollup": "Rollup",
"proof": "Delegatable Proof Generation"
}
64 changes: 64 additions & 0 deletions pages/docs/protocols/masp/note-encryption.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: MASP Note Encryption
description: Describes the MASP Note Encryption.
---

import Callout from "../../../../components/Callout";

# MASP Note Encryption (without FMD)

## Note Structure

Say Alice `transact`s via the `Transaction.circom` circuit and the recipient of both output commitments is Bob. We have the following desiderata:

1. If I possess Alice’s viewing key, I should be able to see the internal data of the two output commitments.
2. If I possess Bob’s viewing key, I should be able to see all the internal data of the output commitments.

This allows:

1. Recipients to spend MASP commitments transferred to them.
2. Regulators and outside observers to view BOTH sent and received funds of any party they hold the viewing keys of.

<Callout type="info">
Note due to our MASP key design, even if regulators know the blinding of a commitment, they still cannot spend the output commitment, because they do not have the proof authorizing key, `ak`, which is needed to create a valid ZKP.
</Callout>

### Note Data

Each note corresponds to an output commitment. The data contained in a note is simply the internal data of the output commitment:

1. AssetID
2. TokenID
3. Amount
4. DestinationChainID
5. PublicKey_X
6. PublicKey_Y
7. blinding

## Encrypting the Note

Each note is encrypted twice, once with Alice’s public key and once with Bob’s public key. More specifically the encryption process works as follows. We will use a similar design to [zkopru](https://github.com/zkopru-network/zkopru/blob/340d5f3f0b4c5112e767bef122c42cdd6f0ab89c/packages/transaction/src/utxo.ts#L53-L80).

### Encrypting with Alice’s Public Key

1. Alice generates an ephemeral babyjubjub keypair `(esk, epk)`.
2. Alice computes `esk[pk_A]` and uses this as the secret key in `chacha20` symmetric encryption scheme to encrypt the note.
3. Alice posts the encryption along with `epk` on the blockchain.

### Encrypting with Bob’s Public Key

1. Bob generates an ephemeral babyjubjub keypair `(esk, epk)`.
2. Bob computes `esk[pk_B]` and uses this as the secret key in `chacha20` symmetric encryption scheme to encrypt the note.
3. Bob posts the encryption along with `epk` on the blockchain.

## Decrypting Notes

We will use a similar design to zkopru:

[zkopru-network/zkopru](https://github.com/zkopru-network/zkopru/blob/340d5f3f0b4c5112e767bef122c42cdd6f0ab89c/packages/transaction/src/utxo.ts#L112-L171)

<Callout type="info">
Note `esk[pk] = vk[epk]`, so anyone with the viewing key can compute `vk[epk]` and use `chacha20` to decrypt notes.
</Callout>

Potential recipients can decrypt new encrypted notes on the blockchain, compute the `Record` commitment from the internal data, and see if it matches the output commitment posted on the blockchain. If so, this note/commitment is meant for the recipient. Eventually, we will have relayers take care of a lot of the note encryption and decryption work so each individual does not have to do it.
141 changes: 141 additions & 0 deletions pages/docs/protocols/masp/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
title: Multi-Asset Shielded Pool (MASP)
description: A description of Webb's Asset Multi-Asset Shielded Pool (MASP).
---

import { MASPPrivateTransferImage } from '../../../../components/images/masp/Masp.tsx'

# Multi-Asset Shielded Pool (MASP)

The MASP is a multi-asset interoperable shielded pool. It allows users to transfer multiple types of assets within a single pool such as fungible and non-fungible tokens. The MASP is more featureful than existing Webb ZK applications in this way and supports a variety of other features such as:

- An updated key structure system supporting delegatable proof generation.
- An incentive mechanism that rewards users for depositing and staying within the shielded pool.
- A batch deposit system that allows cheap and efficient deposits into the pool.
- A swap circuit that allows users to execute atomic swap transactions within the pool.

## What are shielded transactions and pools?

A shielded transaction is a transfer of assets between two parties, Alice and Bob, that conceals the sender, receiver, and amount from outside observers. Shielded pools are one way to implement a shielded transfer system. At a high level, Alice deposits assets into a shielded pool, and Bob withdraws from it. Since all depositors contribute to the shielded pool, their assets are combined, making it impossible for an outside observer to link Alice and Bob. The pool's size is often referred to as the anonymity set.

## What is a multi-asset shielded pool (MASP)?

Traditional shielded pool systems, like Tornado Cash, only support a single asset type. This means a separate shielded pool smart contract must be deployed for each ERC20 token.

A multi-asset shielded pool (MASP) is a shielded pool that supports shielded transactions for multiple asset types. In addition to hiding the sender, receiver, and transaction amount, shielded transactions can also conceal the asset type being transferred. This increases the anonymity set size and provides a better user experience for the end user.

## Features

### Support for Fungible and Non-Fungible Tokens

Webb’s MASP protocol supports depositing both fungible and non-fungible tokens (NFTs).

### Private Asset Transfers

The core functionality of the MASP is private asset transfers. Every user in the MASP has associated MASP keys of the following form.

<MASPPrivateTransferImage />

- `g` is a generator for the `babyjubjub` elliptic curve and therefore, `(sk, ak)` and `(vk, pk)` form valid secret/public keypairs, which can be used for EdDSA signatures and encryption/decryption.

When Alice deposits a `Record` is inserted into a Merkle tree on-chain.

```jsx
BlindingHash = Poseidon(blinding)
PartialRecord = Poseidon(DestinationChainId, Pk_X, Pk_Y, BlindingHash)
Record = Poseidon(AssetId, TokenId, Amount, PartialRecord)
```

The `Record` components are:

- `blinding`: A randomly generated number only known to Alice.
- `DestinationChainId`: The `chainId` where Alice intends to spend her deposited funds.
- `Pk_X, Pk_Y`: The `x` and `y` coordinate of Alice’s MASP public key.
- `AssetId`: The `AssetId` of the token being deposited.
- `TokenId`: If the asset being deposited is an NFT, this is the `TokenId` of that NFT. Otherwise, for fungible tokens, this value is 0.
- `Amount`: The amount of tokens being deposited.

The root of this Merkle tree is then relayed to all the MASPs across the bridge.

When Alice wants to spend her tokens, she submits a zero-knowledge proof via a relayer on the `DestinationChainId`'s MASP. The zero-knowledge proof verifies that she knows the internal values (`blinding`, `MASP key`, `AssetId`, `TokenId`, `Amount`) of a `Record` located on a Merkle tree in one of the MASPs on the bridge. Only Alice can create such a zero-knowledge proof since values like the `blinding` are secret and known only to her.

### Structure of a MASP Transaction

The MASP supports a more feature-rich set of transaction types than simple deposits and withdraws. Specifically, a MASP transasction consists of:

- `publicAmount`: This is the amount being deposited into or withdrawn from the MASP. If this value is 0, the transaction is known as an internal shielded transfer and is special because an outside observer does not even know the type of asset that is being transacted.
- `inputRecord`s: These are the `Record`s that are being spent. They have already been inserted into a Merkle tree on one of the MASPs on the bridge.
- `outputRecord`s: These are the new `Record`s that are being created. They will be inserted into the MASP’s Merkle tree.

The following invariant must hold so that funds are not created out of thin-air:

```markdown
sum of inputRecords amounts + public Amount = sum of outputRecords amounts
```

This invariant is checked in the zero-knowledge proof. For valid transactions, the `inputRecord`s are nullified, so they cannot be double spent, and the `outputRecord`s are inserted into the MASPs Merkle tree.

### Yield Generation via Anonymity Mining

The strength of privacy provided by the MASP is strongly tied to the size of the anonymity set. The anonymity set grows the longer users keep their assets locked in the shielded pool. Therefore, Webb’s MASP protocol includes an anonymity mining system which tracks, in a privacy-preserving manner, the amount of time user’s keep their funds locked and proportionally rewards them with tokens. As per the current iteration of the MASP protocol, anonymity miners are rewarded in Tangle tokens, which is the native token of Webb’s MPC Substrate blockchain.

### Shielded Swaps

The Webb MASP protocol allows two counterparties Alice and Bob to swap assets. For instance, Alice can swap her shielded NFT for Bob’s shielded USDC. All an outside observer sees is `Record`s inserted and nullified on the MASP’s Merkle tree.

At a high level, the process works as follows:

- Via some communication network Alice advertises that she wants to sell her NFT for USDC.
- Bob responds to Alice and they agree upon a price.
- Various details of the swap transaction are sent to a relayer which then produces a zero-knowledge proof and submits it on-chain. The technical details of this mechanism are present in this document:

[MASP Swap Circuit](./swap.mdx)

### Rollup Functionalities

Inserting into an on-chain Merkle tree is a computationally expensive operation since many hashes must be computed. One solution to this issue is to store the leaf data that needs to be inserted into the Merkle tree in an on-chain queue. A relayer can then come and batch insert these elements into the Merkle tree via a succinct zero-knowledge proof and the new Merkle tree root is stored on the MASP smart contract.

More technical details about this mechanism are in the following docs:

[MASP Rollup Functionality v1](./rollup.mdx)

### Delegatable Transaction Proof Generation

For many current shielded proof systems, the zero-knowledge proof for transacting is generated in the user’s browser. This is because the data needed to generate a valid zero-knowledge proof includes secret values, such as the MASP secret key, that should not be shared with an external party. The downside of this approach is that proof generation is a computationally intensive task. As such, it would be ideal to delegate proof generation to a computationally powerful relayer.

This is exactly what the proof authorizing key (part of the MASP key) allows for. Particularly, it allows a user to delegate proof generation to a relayer, without exposing its MASP secret key. For the technical details on this process, please see:

[MASP Delegatable Proof Generation](./proof.mdx)

## Lifecycles

### For yield farmers

1. Deposit tokens into shielded pool
2. Keep funds locked for long period of time and accumulate anonymity points.
3. When ready to claim Tangle tokens as reward, spend funds inside shielded pool, so a commitment gets inserted on `RewardSpentTree`.
4. Claim anonymity points via a zero-knowledge proof on Tangle blockchain.
5. Use AMM pallet to swap anonymity points into Tangle Tokens.

### For transactors

1. Deposit into shielded pool.
2. Submit a zero-knowledge proof via a relayer to spend funds.

### For swappers

1. Assume Alice and Bob already have funds inside the shielded pool.
2. Alice sends a message over a communication network indicating that she wants to swap her token(s).
3. Bob responds to Alice’s request and they agree on a price.
4. They submit swap proof inputs to a relayer.
5. Relayer computes swap proof and submits it on-chain.

## Fees and Incentives for Relayers

- Relayers that submit transaction proofs on-chain are paid fees inside the shielded pool, in one of a number of whitelisted fee tokens. That is, the transactor/user must pay its fees via `Record`s that it owns. These `Record`s are used to form `feeOutputRecord`s owned by the relayer.
- There are currently no relayer fees for submitting proofs for batch inserting into Merkle trees.
- There are currently no relayer fees for submitting swap proofs.

## Links

- `masp` branch on `protocol-solidity` repo: [https://github.com/webb-tools/protocol-solidity/tree/masp](https://github.com/webb-tools/protocol-solidity/tree/masp).
35 changes: 35 additions & 0 deletions pages/docs/protocols/masp/proof.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
title: MASP Delegatable Proof Generation
description: A description of MASP Delegatable Proof Generation.
---

import { MASPPrivateTransferImage } from '../../../../components/images/masp/Masp.tsx'

# MASP Delegatable Proof Generation

## Overview

In Webb’s MASP system, delegatable proof generation offloads the computationally intensive proof generation process from the user’s browser to a computationally powerful, semi-trusted prover (which we call a relayer). As we will see, this process is not as trivial as the user sending the relayer its proof inputs, since the proof inputs contain secret data that the relayer can then use to steal the user’s funds.

## Technical Details

### MASP key structure:

<MASPPrivateTransferImage />

### MASP `Record` structure:

```circom
BlindingHash = Poseidon(blinding)
PartialRecord = Poseidon(DestinationChainId, Pk_X, Pk_Y, BlindingHash)
Record = Poseidon(AssetId, TokenId, Amount, PartialRecord)
```

### Preserving Security

The relayer must not be able to change the contents of the `outputRecord`s. If it could, it would simply change the `outputRecord`s to be under its keys and transfer the user’s funds to itself. To prevent such behavior, we make use of the fact that `(sk, ak)` is a secret/public keypair with which EdDSA signatures can be made. In particular, we do the following:

- User signs `inputRecord`s and `outputRecord`s with `sk`
- Inside the ZKP, we verify that these `Record`s are correctly signed, by verifying with `ak`.

This begs the question: can’t the relayer just generate a new `(sk, ak)` pair and forge the signatuers with altered `outputRecord`s? The answer is NO! Because the `inputRecord`s are bound to `ak`. That is `pk_X` and `pk_Y` are derived inside the circuit from `ak` so if the relayer changes `ak` the `inputRecord`s will no longer be valid `Record`'s on the MASP Merkle trees.
Loading

0 comments on commit 47852ee

Please sign in to comment.