Giter Club home page Giter Club logo

salad's Introduction

Salad

A CoinJoin implementation of the Enigma Discovery Network.

How to Build

Dependencies

  • node == v10.0.17
  • rustc == nightly-2019-08-01
  • yarn == 1.17.3
  • Docker == 18.09.8-ce, build 0dd43dd87f
  • docker-compose == 1.24.1, build 4667896

Instructions

This project uses Yarn Workplaces so using yarn over npm is recommended.

  • git clone https://github.com/enigmampc/salad
  • cd salad
  • yarn configure or yarn configure-sw to run salad on Simulation Mode
  • yarn install
  • yarn dc pull
  • In another console, run: yarn start

after closing it, you can run yarn stop to shut down the docker containers.

In your first console now you can:

  • yarn migrate, or
  • yarn test

Launch the front-end for development

  • yarn clean-run-operator // Truncate the database, re-deploy the contracts and start the operator
  • cd frontend
  • yarn start // Host the frontend with webpack and watch for changes

Architecture

Definitions

  • Relayer: The operator who brokers messages between users and the Enigma network
  • Deposit: The currency about sent by each participant
  • Recipient Address: The obfuscated account receiving each deposit
  • Anonymity Set: The number of participants
  • Quorum: The number of participant in a deal
  • Last Execution Block Number: The block number during last deal execution
  • Last Mix Block Number: The block number during last deal execution or quorum not reached
  • Deal: A coin mixer transaction
  • Fee: The fee taken by the relayer as percentage of currency mixed

Overview

Here is the highly simplified user workflow:

  1. Send Deposit: Guided by a user interface, the participant sends a deposit to the mixer contract.
  2. Send Signed Encrypted Recipient Address: Guided by the same UI, the participant encrypts the recipient address (who should receive the deposit after mixing), sign the message and send to the Relayer.
  3. Watch Liquidity: The Relayer does accounting of all deposits matching the amount sent by the participant until the pre-defined participation threshold is reached. The participation threshold is the number of participants per deal required to achieve sufficient anonymity. For example, suppose that Bob deposits 1ETH and the participation threshold is 2. When Alice follows and deposit 1ETH, the participation threshold is reached and the Relayer can create a new deal.
  4. To create a deal, the Relayer gathers the following data: 1) Number of participants; 2) Deposit addresses; 3) Encrypted recipient addresses. The Relayer uses enigma-js to include this payload into an Enigma transaction.
  5. Decrypt and Mix Recipients: The secret contract decrypt recipient addresses and uses the eng_wasm randomness service to re-order (mix) them.
  6. Submit Mixed Plain Text Recipient Addresses: The secret contract calls the Ethereum smart contract with the list of mixed addresses (in clear text) and other deal attributes. The smart contract has a modifier that only authorizes Enigma.
  7. Distribute Deposits: The smart contract send the deposits of the deal to the specified recipients.

Notes

Why the Relayer?

It is theoretically possible for an end-user to interface with Ethereum and Enigma from a browser frontend. However, here are some key reasons why a Relayer is appropriate.

  1. Synchronize Enigma and Ethereum: Enigma can write to Ethereum but, at the time of writing, it cannot read. The Relayer acts as a gate-keeper to ensure that all participants have a deposit locked in escrow on Ethereum. This avoids obvious spam attacks that would cause deals to break down on execution.
  2. Transaction Hell: In a naive implementation, the end-user would have to make three Ethereum transactions (instead of one) to make a deposit: 1) Deposit on Ethereum; 2) Approve ENG for gas; 3) Create a Task Record. This is a much worse UX, arguably unusable for some.
  3. Economic Abstraction: Since only the Relayer interfaces with Enigma, the end-user does not need to own ENG (more on that below).
  4. Cost: As described in point 2, the number of transactions would increase exponentially. With a Relayer, there is only one Enigma transaction per deal. Without, there will be one transaction per deposit. Since deals are expected to have a relatively large quorum, the total transaction cost of a deal would be orders of magnitude higher.
  5. Scheduling: Neither Enigma nor Ethereum have a scheduler, which is required to execute deals at specific time intervals (more on that below). In addition, the Relayer can perform other proactive operations; like monitoring account balances, which can make upfront deposits unnecessary and improve the UX further.
  6. Compatibility: Other mixer projects (e.g. Semaphore) are designing a standard Relayer api, by maintaining compatibility, the Enigma CoinJoin can be part an aggregate where the end-users choose their preferred backend. This could result in significantly more liquidity.

On Competing Approaches

Mixers are fashionable at the moment, here comprehensive info about competing projects: https://hackmd.io/rCARMvVQSCKDHFk19bVhww?view Basically, there is ongoing work to standardize on a ZK mixer. Here is the Semaphore implementation: https://hackmd.io/qlKORn5MSOes1WtsEznu_g#Withdrawals-at-midnight-UTC At the time of writing, here are some potential benefits of our approach. These need be confirmed as the ZK implementations evolve and others might be added to the list.

  1. Lower Fee: Computing ZK proofs on chain is expensive and mixers are no exception. Using Enigma is MAYBE cheaper (more analysis needed).
  2. Non-Interactive: With ZK implementation, a participants must interact with the system to deposit and withdraw: 1) Make a deposit and obtain a note; 2) Wait for multiple other deposits to maximize anonymity; 3) Submit the note with a recipient address (withdraw). Our mixer requires a single interaction (during deposit). Withdraw happens asynchronously without user interaction.
  3. Possibility to Split a Deposit into Multiple Deals: Because withdraw is non-interactive, our mixer can split a single deposit into multiple deals. For example, Bob decides to mix 1ETH and gives the Mixer 24H to finish. The Mixer uses 0.2ETH in 5 deals during this period based on available liquidity. With the Semaphore implementation, users are limited to mixing 0.1ETH per day. This is because of how the ZK proof is constructed, it's not currently possible to issue multiple notes for the same address.
  4. Frontrunning: Enigma transactions are encrypted therefore more difficult to frontrun. Frontrunning withdraw is possible with ZK mixers.

Note that all advantages above can have a significant impact on user experience. Suppose that Bob wants to mix 100ETH, he can simply make a deposit and give a deadline. His deposit will be mixed automatically across multiple concurrent and non-concurrent deals without his interraction. When the deadline expires, the deposit is either mixed in full or Bob receives some change. All this for a one time fee. In the process, Bob's 100ETH provides significant liquidity to the anonimity set, helping fund multiple deals. If Bob were to use Semaphore instead, Bob would have to wait 1000 days or somehow split his balances into up to 1000 wallets. Also, Bob would need a secure system that can reliably keep a browser window alive while propecting his Notes (which equivalent to a blank check) exposed in plain text in the browser memory. Bob would also pay a fee for each 0.1ETH deposit.

On Economic Abstraction

Other than for his deposit (which could be replace by a proxy approval), the participants does not directly pay gas nor interface with Enigma. Gas is paid to Enigma by the Relayer. This business cost is recouped by the Relayer by taking a share of each deal during execution.

On Encryption

The encryption scheme isn't yet fully defined but it is implied that participants will authorize a secure enclave to decrypt their recipient address, not the Relayer. To achieve this, we can either:

  1. Create a static key pair in the secret contract after deployment. The private key will never be revealed externally. The public key will be used to encrypt all addresses.
  2. Users get selected worker public key using enigma-js and use for encryption. The problem with this is that deals cannot include deposits from earlier epochs.
  3. Consider proxy re-encryption.

For the initial prototype, I'm using approach #1. ZK-based mixers don't utilize encryption like this because mixing requires 2-step process. First, a participant computes a ZK proof and gets a Note. At a later time, the same participant submit the Note and a recipient address. Assuming that the proof does not leak information, the deposit address is never directly linked to the recipient address.

Attack Vectors

  1. Spam: An attacker could submit large amount of deposits to create artificial liquidity and de-anonymize other participants. If there are 10 participants in a deal, and 9 of those are the attacker, the 1 left isn't anonymous to the attacker. Semaphore addresses this by executing deals on a schedule, not by a participation threshold. The best solution is probably a combination of both. More research needed.

Problems

  • Unable to pass bytes32 to smart contracts

Detailed Protocol

Checked list items have been implemented, others are pending.

Deposit Payload

  • Sender Address
  • Amount
  • Encrypted Recipient Address
  • User Public Key

Hashes

  • DealId: H(Sender Addresses, Amount, Relayer Ethereum Address, Relayer Ethereum Nonce)

Workflow

User

  1. Get the Public Encryption Key, and the signature of the worker who generated it, from the Relayer (previously fetched from the secret contract and stored in cache).
  2. Recover the worker's address from the signature, verify its authenticity against the Worker Registry of the Enigma contract.
  3. Generate a key pair for encryption of the Recipient Address.
  4. Make a deposit in the CoinJoin smart contract. A record of "Sender Address => Amount" now exists on-chain.
  5. Encrypt the Recipient Address using a key derived from the Public Encryption Key and the User Private Key.
  6. Sign the payload using an Ethereum wallet (e.g. MetaMask).
  7. Submit the Deposit Payload and Signature to the Relayer.

Mixer

  1. To prevent spam, the Relayer verifies the Payload Signature.
  2. Relayer holds the Payload until a trigger (based on time and participation threshold).
  3. Relayer creates a Deal on-chain by submitting the Amount and Sender Addresses. The DealId hash is computed on-chain. A record of a Pending Deal now exists on-chain.
  4. Relayer submits its Ethereum Address, Ethereum Nonce, Payloads and Signatures to the secret contract.
  5. Secret contract verifies the signatures. We now have verified Payloads in the secret contract.
  6. Relayer computes the DealId, which is a hash computed partly from verified Sender Addresses, becoming a proof to be validated on-chain. It is not possible to target a DealId hash without having verified the signatures.
  7. Secret contract decrypts each Recipient Address by deriving a key from the Encryption Private Key and each User Public Key.
  8. Secret contract generates a random seed and mixes the address using a Fisher–Yates shuffle.
  9. Secret contract submits the DealId and Shuffled Plaintext Recipient Addresses on-chain.
  10. Smart contract finds the Deal by DealId, verifies that the status is Pending and verifies the Deposit balances.
  11. Smart contract transfers each deposit to the Recipients, subtracting them from the each Sender's balance.
  12. The Deal status is now Executed

salad's People

Contributors

fredfortier avatar reuvenpo avatar bshevchenko avatar lacabra avatar mdemri avatar levackt avatar

Stargazers

LZ91X  avatar  avatar  avatar Anton Cheng avatar will avatar Michael Demarais avatar Chen Kai avatar  avatar Morgan Moskalyk avatar Yenwen Feng avatar private avatar HAOYUatHZ avatar Nick avatar keysemails avatar  avatar Alexsey Ramzaev avatar

Watchers

James Cloos avatar  avatar  avatar Michael Demarais avatar  avatar  avatar Tom Langer avatar private avatar  avatar Cashmaney avatar Lena avatar Assaf Morami avatar  avatar Avishai Weingarten avatar  avatar

salad's Issues

dApp having issues connecting to Salad Server

Also reported by @stan_yi on dev forum here: https://forum.enigma.co/t/browser-stucked-at-connecting-to-the-salad-server/1212/2

I'm not sure yet if the issue is due to his environment: exposing the right ports for his configuration or if it's related to the develop discovery images.

In my case it happens after I submit a deal transaction, then change the current MetaMask account and reload the browser (trying to simulate sending from different accounts).

If I just switch MetaMask accounts and reload the browser, without submitting any deals, it's able to connect to the Salad Server successfully each time.

If I launch 3 different browsers (Brave, Chrome and Safari) each set to a different MetaMask account, I'm able to submit deals, reload and the dApp and it connects to the Salad Server.

I've tried resettin MetaMask accounts too, but that didn't help.

Snapshot of browser dev tools info attached.

To resolve the issue I stop the dApp and restart the process:

  1. yarn clean-run-operator
  2. (another terminal) cd frontend
  3. yarn start

If I launch the dApp and don't change the sender address, I'm able to submit the req'd 3 deals and see the deal execution and funds distributed to the recipient addresses.

Screen Shot 2019-12-15 at 5 39 47 PM

Discovery integration tests failing on "yarn start"

This issue originated in the dev forum here: https://forum.enigma.co/t/salad-some-integration-tests-fail/1243

Summary:
Doing a fresh git clone of the salad repo and following the README instructions up to and including yarn start, some of the integration tests fail.

Pull was done from the develop branch. Configuration used: yarn configure-sw.

Screen Shot 2020-01-21 at 5 27 22 PM

Env:
MacOS Catalina 10.15.1
Node: v11.15.0
rustc 1.38.0-nightly (8a58268b5 2019-07-31)
yarn: 1.19.2
docker: Docker version 19.03.5, build 633a0ea
docker-compose: docker-compose version 1.24.1, build 4667896b

Troubleshooting:
Used this info to remove all docker images, including orphaned and dangling and re-tried the README steps: https://getintodevops.com/blog/keeping-the-whale-happy-how-to-clean-up-after-docker

The integration test failures were reproduced consistently.

Details:

  • Six (6) out of the 14 integration tests fail with errors
Init tests › should check that 1 worker(s) and the principal node, and only them, are registered
client_1    | 
client_1    |     expect(received).toEqual(expected) // deep equality
client_1    | 
client_1    |     - Expected
client_1    |     + Received
client_1    | 
client_1    |     @@ -1,7 +1,7 @@
client_1    |       Array [
client_1    |     -   2,
client_1    |     +   0,
client_1    |         0,
client_1    |         0,
client_1    |         0,
client_1    |         0,
client_1    |         0,
 Init tests › should deposit tokens in worker banks
client_1    | 
client_1    |     Failed: "Returned error: VM Exception while processing transaction: revert Unregistered worker"
Init tests › should login the worker(s)
client_1    | 
client_1    |     : Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Error
Init tests › should check that 1 worker(s), and only them, are logged in
client_1    | 
client_1    |     expect(received).toEqual(expected) // deep equality
client_1    | 
client_1    |     - Expected
client_1    |     + Received
client_1    | 
client_1    |     @@ -1,7 +1,7 @@
client_1    |       Array [
client_1    |     -   1,
client_1    |     +   0,
client_1    |         0,
client_1    |         0,
client_1    |         0,
client_1    |         0,
client_1    |         0,
Init tests › should get the worker parameters for the current block
client_1    | 
client_1    |     expect(received).toEqual(expected) // deep equality
client_1    | 
client_1    |     - Expected
client_1    |     + Received
client_1    | 
client_1    |     - Array [
client_1    |     -   "0x0000000000000000000000000000000000000000",
client_1    |     - ]
client_1    |     + Array []
Init tests › should create getTaskEncryptionKey from core (with call to P2P)
client_1    | 
client_1    |     : Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error:

Dockerfile Nightly Version of Rust Doesn't Have Clippy

When running the Dockerfile manually, I run into the following error on this line:

Step 8/13 : RUN rustup override set nightly-2019-07-04
 ---> Running in 8d8f71b6b684
info: syncing channel updates for 'nightly-2019-07-04-x86_64-unknown-linux-gnu'
info: latest update on 2019-07-04, rust version 1.37.0-nightly (088b98730 2019-07-03)
error: component 'clippy' for target 'x86_64-unknown-linux-gnu' is unavailable for download for channel nightly-2019-07-04
The command '/bin/sh -c rustup override set nightly-2019-07-04' returned a non-zero code: 1

My fix for it was to update the Nightly Date to:

RUN rustup override set nightly-2019-11-04

It installs clippy now. For reference, following link shows missing packages from nightly installs:
https://rust-lang.github.io/rustup-components-history/index.html

I can submit a PR to your Dockerfile, but wanted to share this first with you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.